Skip to content

Commit 68297f1

Browse files
committed
Angular NgRx Course
1 parent 52315ed commit 68297f1

10 files changed

+149
-35
lines changed

server/create-course.route.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import {Request, Response} from 'express';
2+
import {COURSES} from './db-data';
3+
4+
export var coursesKeyCounter = 100;
5+
6+
export function createCourse(req: Request, res: Response) {
7+
8+
console.log("Creating new course ...");
9+
10+
const changes = req.body;
11+
12+
const newCourse = {
13+
id: coursesKeyCounter,
14+
seqNo: coursesKeyCounter,
15+
...changes
16+
};
17+
18+
COURSES[newCourse.id] = newCourse;
19+
20+
coursesKeyCounter += 1;
21+
22+
setTimeout(() => {
23+
24+
res.status(200).json(newCourse);
25+
26+
}, 2000);
27+
28+
}
29+

server/db-data.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ export const COURSES: any = {
1515
description: 'NgRx In Depth',
1616
longDescription: 'Learn the modern Ngrx Ecosystem, including NgRx Data, Store, Effects, Router Store, Ngrx Entity, and Dev Tools.',
1717
iconUrl: 'https://angular-university.s3-us-west-1.amazonaws.com/course-images/ngrx-v2.png',
18-
courseListIcon: 'https://angular-academy.s3.amazonaws.com/main-logo/main-page-logo-small-hat.png',
1918
category: 'BEGINNER',
2019
lessonsCount: 10,
2120
seqNo: 0,
@@ -38,7 +37,6 @@ export const COURSES: any = {
3837
description: 'RxJs In Practice Course',
3938
longDescription: 'Understand the RxJs Observable pattern, learn the RxJs Operators via practical examples',
4039
iconUrl: 'https://s3-us-west-1.amazonaws.com/angular-university/course-images/rxjs-in-practice-course.png',
41-
courseListIcon: 'https://angular-academy.s3.amazonaws.com/main-logo/main-page-logo-small-hat.png',
4240
category: 'BEGINNER',
4341
lessonsCount: 10,
4442
seqNo: 2,
@@ -56,18 +54,22 @@ export const COURSES: any = {
5654
url: 'serverless-angular'
5755
},
5856

57+
/*
58+
59+
5960
5: {
6061
id: 5,
6162
description: 'Angular for Beginners',
62-
longDescription: 'Establish a solid layer of fundamentals, learn what\'s under the hood of Angular',
63+
longDescription: "Establish a solid layer of fundamentals, learn what's under the hood of Angular",
6364
iconUrl: 'https://angular-academy.s3.amazonaws.com/thumbnails/angular2-for-beginners-small-v2.png',
64-
courseListIcon: 'https://angular-academy.s3.amazonaws.com/main-logo/main-page-logo-small-hat.png',
6565
category: 'BEGINNER',
6666
lessonsCount: 10,
6767
seqNo: 5,
6868
url: 'angular-for-beginners'
6969
},
7070
71+
*/
72+
7173
12: {
7274
id: 12,
7375
description: 'Angular Testing Course',
@@ -84,7 +86,6 @@ export const COURSES: any = {
8486
description: 'Angular Security Course - Web Security Fundamentals',
8587
longDescription: 'Learn Web Security Fundamentals and apply them to defend an Angular / Node Application from multiple types of attacks.',
8688
iconUrl: 'https://s3-us-west-1.amazonaws.com/angular-university/course-images/security-cover-small-v2.png',
87-
courseListIcon: 'https://s3-us-west-1.amazonaws.com/angular-university/course-images/lock-v2.png',
8889
category: 'ADVANCED',
8990
lessonsCount: 11,
9091
seqNo: 7,
@@ -96,7 +97,6 @@ export const COURSES: any = {
9697
description: 'Angular PWA - Progressive Web Apps Course',
9798
longDescription: 'Learn Angular Progressive Web Applications, build the future of the Web Today.',
9899
iconUrl: 'https://s3-us-west-1.amazonaws.com/angular-university/course-images/angular-pwa-course.png',
99-
courseListIcon: 'https://s3-us-west-1.amazonaws.com/angular-university/course-images/alien.png',
100100
category: 'ADVANCED',
101101
lessonsCount: 8,
102102
seqNo: 8,
@@ -108,7 +108,6 @@ export const COURSES: any = {
108108
description: 'Angular Advanced Library Laboratory: Build Your Own Library',
109109
longDescription: 'Learn Advanced Angular functionality typically used in Library Development. Advanced Components, Directives, Testing, Npm',
110110
iconUrl: 'https://angular-academy.s3.amazonaws.com/thumbnails/advanced_angular-small-v3.png',
111-
courseListIcon: 'https://angular-academy.s3.amazonaws.com/thumbnails/angular-advanced-lesson-icon.png',
112111
category: 'ADVANCED',
113112
seqNo: 9,
114113
url: 'angular-advanced-course'
@@ -119,7 +118,6 @@ export const COURSES: any = {
119118
description: 'The Complete Typescript Course',
120119
longDescription: 'Complete Guide to Typescript From Scratch: Learn the language in-depth and use it to build a Node REST API.',
121120
iconUrl: 'https://angular-academy.s3.amazonaws.com/thumbnails/typescript-2-small.png',
122-
courseListIcon: 'https://angular-academy.s3.amazonaws.com/thumbnails/typescript-2-lesson.png',
123121
category: 'BEGINNER',
124122
seqNo: 10,
125123
url: 'typescript-course'
@@ -130,7 +128,6 @@ export const COURSES: any = {
130128
description: 'Rxjs and Reactive Patterns Angular Architecture Course',
131129
longDescription: 'Learn the core RxJs Observable Pattern as well and many other Design Patterns for building Reactive Angular Applications.',
132130
iconUrl: 'https://s3-us-west-1.amazonaws.com/angular-academy/blog/images/rxjs-reactive-patterns-small.png',
133-
courseListIcon: 'https://angular-academy.s3.amazonaws.com/course-logos/observables_rxjs.png',
134131
category: 'BEGINNER',
135132
seqNo: 11,
136133
url: 'rxjs-patterns-course'

server/server.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {getAllCourses, getCourseById} from "./get-courses.route";
66
import {searchLessons} from "./search-lessons.route";
77
import {loginUser} from "./auth.route";
88
import {saveCourse} from "./save-course.route";
9+
import {createCourse} from './create-course.route';
910

1011
const bodyParser = require('body-parser');
1112

@@ -21,6 +22,8 @@ app.route('/api/login').post(loginUser);
2122

2223
app.route('/api/courses').get(getAllCourses);
2324

25+
app.route('/api/course').post(createCourse);
26+
2427
app.route('/api/course/:id').put(saveCourse);
2528

2629
app.route('/api/course/:id').get(getCourseById);

src/app/courses/courses-card-list/courses-card-list.component.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ export class CoursesCardListComponent implements OnInit {
3333

3434
this.dialog.open(EditCourseDialogComponent, dialogConfig);
3535

36-
3736
}
3837

3938
}

src/app/courses/edit-course-dialog/edit-course-dialog.component.html

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,26 @@ <h2 mat-dialog-title>{{dialogTitle}}</h2>
1515

1616
</mat-form-field>
1717

18+
<ng-container *ngIf="mode == 'create'">
19+
20+
<mat-form-field>
21+
22+
<input matInput
23+
placeholder="Course Url"
24+
formControlName="url">
25+
26+
</mat-form-field>
27+
28+
<mat-form-field>
29+
30+
<input matInput
31+
placeholder="Course Icon Url"
32+
formControlName="iconUrl">
33+
34+
</mat-form-field>
35+
36+
</ng-container>
37+
1838
<mat-form-field>
1939

2040
<mat-select placeholder="Select category"
@@ -57,7 +77,7 @@ <h2 mat-dialog-title>{{dialogTitle}}</h2>
5777
</button>
5878

5979
<button mat-raised-button color="primary"
60-
[disabled]="!form?.valid"
80+
[disabled]="!form?.valid || (this.loading$ | async)"
6181
(click)="onSave()">
6282
Save
6383
</button>

src/app/courses/edit-course-dialog/edit-course-dialog.component.ts

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {AppState} from '../../reducers';
55
import {Store} from '@ngrx/store';
66
import {CourseEntityService} from '../services/course-entity.service';
77
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
8+
import {Observable} from 'rxjs';
89

910
@Component({
1011
selector: 'course-dialog',
@@ -21,6 +22,8 @@ export class EditCourseDialogComponent {
2122

2223
mode: 'create' | 'update';
2324

25+
loading$:Observable<boolean>;
26+
2427
constructor(
2528
private fb: FormBuilder,
2629
private store: Store<AppState>,
@@ -32,13 +35,26 @@ export class EditCourseDialogComponent {
3235
this.course = data.course;
3336
this.mode = data.mode;
3437

35-
this.form = this.fb.group({
36-
description: [this.course.description, Validators.required],
37-
category: [this.course.category, Validators.required],
38-
longDescription: [this.course.longDescription, Validators.required],
39-
promo: [this.course.promo, []]
40-
});
38+
this.loading$ = this.coursesService.loading$;
39+
40+
const formControls = {
41+
description: ['', Validators.required],
42+
category: ['', Validators.required],
43+
longDescription: ['', Validators.required],
44+
promo: ['', []]
45+
};
4146

47+
if (this.mode == 'update') {
48+
this.form = this.fb.group(formControls);
49+
this.form.patchValue({...data.course});
50+
}
51+
else if (this.mode == 'create') {
52+
this.form = this.fb.group({
53+
...formControls,
54+
url: ['', Validators.required],
55+
iconUrl: ['', Validators.required]
56+
});
57+
}
4258
}
4359

4460
onClose() {
@@ -61,6 +77,19 @@ export class EditCourseDialogComponent {
6177
}
6278
else if (this.mode == 'create') {
6379

80+
this.coursesService.add(course)
81+
.subscribe(
82+
(val) => {
83+
84+
console.log("New Course Created", val);
85+
86+
this.dialogRef.close();
87+
},
88+
err => {
89+
console.log("Error creating course: ", err);
90+
}
91+
);
92+
6493
}
6594

6695
}

src/app/courses/home/home.component.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
.title {
44
text-align: center;
5+
margin-right: 15px;
6+
57
}
68

79
.courses-panel {
@@ -21,3 +23,12 @@
2123
h2 {
2224
font-family: "Roboto";
2325
}
26+
27+
.header {
28+
display: flex;
29+
justify-content: center;
30+
align-items: center;
31+
32+
}
33+
34+

src/app/courses/home/home.component.html

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,40 @@
1-
21
<div class="courses-panel">
32

3+
<div class="header">
4+
45
<h2 class="title">All Courses</h2>
56

6-
<div class="counters">
7-
<p>In Promo: {{promoTotal$ | async}}</p>
8-
</div>
7+
<button mat-mini-fab>
8+
<mat-icon class="add-course-btn" (click)="onAddCourse()">add</mat-icon>
9+
</button>
10+
11+
</div>
912

10-
<mat-tab-group>
13+
<div class="counters">
14+
<p>In Promo: {{promoTotal$ | async}}</p>
15+
</div>
1116

12-
<mat-tab label="Beginners">
17+
<mat-tab-group>
1318

14-
<courses-card-list
15-
[courses]="beginnerCourses$ | async">
19+
<mat-tab label="Beginners">
1620

17-
</courses-card-list>
21+
<courses-card-list
22+
[courses]="beginnerCourses$ | async">
1823

19-
</mat-tab>
24+
</courses-card-list>
2025

21-
<mat-tab label="Advanced">
26+
</mat-tab>
2227

23-
<courses-card-list
24-
[courses]="advancedCourses$ | async"
28+
<mat-tab label="Advanced">
2529

26-
></courses-card-list>
30+
<courses-card-list
31+
[courses]="advancedCourses$ | async"
2732

28-
</mat-tab>
33+
></courses-card-list>
2934

30-
</mat-tab-group>
35+
</mat-tab>
3136

37+
</mat-tab-group>
3238

3339

3440
</div>

src/app/courses/home/home.component.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@ import {Course} from "../model/course";
33
import {Observable} from "rxjs";
44
import {AppState} from '../../reducers';
55
import {createSelector, select, Store} from '@ngrx/store';
6-
import {EntityCollectionService, EntityServices} from '@ngrx/data';
76
import {CourseEntityService} from '../services/course-entity.service';
7+
import {defaultDialogConfig} from '../../shared/default-dialog-config';
8+
import {EditCourseDialogComponent} from '../edit-course-dialog/edit-course-dialog.component';
9+
import {MatDialog} from '@angular/material';
10+
11+
12+
813
@Component({
914
selector: 'home',
1015
templateUrl: './home.component.html',
@@ -21,7 +26,8 @@ export class HomeComponent implements OnInit {
2126

2227
constructor(
2328
private store: Store<AppState>,
24-
private coursesService: CourseEntityService) {
29+
private coursesService: CourseEntityService,
30+
private dialog: MatDialog) {
2531

2632
}
2733

@@ -53,4 +59,17 @@ export class HomeComponent implements OnInit {
5359

5460
}
5561

62+
onAddCourse() {
63+
64+
const dialogConfig = defaultDialogConfig();
65+
66+
dialogConfig.data = {
67+
dialogTitle:"Create Course",
68+
mode: 'create'
69+
};
70+
71+
this.dialog.open(EditCourseDialogComponent, dialogConfig);
72+
73+
}
74+
5675
}

src/app/courses/model/course.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
export interface Course {
33
id: number;
44
seqNo:number;
5+
url:string;
56
iconUrl: string;
67
courseListIcon: string;
78
description: string;

0 commit comments

Comments
 (0)