├── .browserslistrc ├── .editorconfig ├── .gitignore ├── INIT.md ├── LICENSE ├── README.md ├── angular.json ├── e2e ├── app.e2e-spec.ts ├── app.po.ts └── tsconfig.e2e.json ├── karma.conf.js ├── package-lock.json ├── package.json ├── protractor.conf.js ├── proxy.json ├── server ├── db-data.ts ├── get-courses.route.ts ├── search-lessons.route.ts ├── server.ts └── server.tsconfig.json ├── src ├── _mixins.scss ├── app │ ├── about │ │ ├── about.component.css │ │ ├── about.component.html │ │ └── about.component.ts │ ├── app-routing.module.ts │ ├── app.component.css │ ├── app.component.html │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── app.module.ts │ ├── course-dialog │ │ ├── course-dialog.component.css │ │ ├── course-dialog.component.html │ │ └── course-dialog.component.ts │ ├── course │ │ ├── course.component.html │ │ ├── course.component.scss │ │ └── course.component.ts │ ├── courses-card-list │ │ ├── courses-card-list.component.css │ │ ├── courses-card-list.component.html │ │ └── courses-card-list.component.ts │ ├── create-course │ │ ├── create-course-step-1 │ │ │ ├── create-course-step-1.component.html │ │ │ ├── create-course-step-1.component.scss │ │ │ └── create-course-step-1.component.ts │ │ ├── create-course-step-2 │ │ │ ├── create-course-step-2.component.html │ │ │ ├── create-course-step-2.component.scss │ │ │ └── create-course-step-2.component.ts │ │ ├── create-course.component.html │ │ ├── create-course.component.scss │ │ └── create-course.component.ts │ ├── drag-drop │ │ ├── drag-drop.component.html │ │ ├── drag-drop.component.scss │ │ └── drag-drop.component.ts │ ├── home │ │ ├── home.component.html │ │ ├── home.component.scss │ │ └── home.component.ts │ ├── model │ │ ├── course.ts │ │ └── lesson.ts │ ├── services │ │ ├── course.resolver.ts │ │ └── courses.service.ts │ ├── tree-demo │ │ ├── tree-demo.component.html │ │ ├── tree-demo.component.scss │ │ └── tree-demo.component.ts │ └── virtual-scrolling │ │ ├── virtual-scrolling.component.html │ │ ├── virtual-scrolling.component.scss │ │ └── virtual-scrolling.component.ts ├── assets │ └── .gitkeep ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── main.ts ├── polyfills.ts ├── styles.scss ├── test.ts ├── tsconfig.app.json ├── tsconfig.spec.json └── typings.d.ts ├── tsconfig.json └── tslint.json /.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.angular/cache 29 | /.sass-cache 30 | /connect.lock 31 | /coverage 32 | /libpeerconnection.log 33 | npm-debug.log 34 | testem.log 35 | /typings 36 | 37 | # e2e 38 | /e2e/*.js 39 | /e2e/*.map 40 | 41 | # System Files 42 | .DS_Store 43 | Thumbs.db 44 | -------------------------------------------------------------------------------- /INIT.md: -------------------------------------------------------------------------------- 1 | 2 | # Initializing a clean Angular Material Project 3 | 4 | These are the commands and steps needed to scaffold a new Angular Material project from scratch, 5 | from an empty folder. 6 | 7 | Please make sure to have the latest CLI, and at least NPM 5. 8 | 9 | When is doubt, its recommended to update to the latest version of node using a node versioning tool 10 | such as for example [nave](https://github.com/isaacs/nave) or [nvm-windows](https://github.com/coreybutler/nvm-windows). 11 | 12 | # Step 1 - Scaffold a clean project using the Angular CLI 13 | 14 | With a CLI version 1.5 or above, let's scaffold a new project with routing: 15 | 16 | ng new angular-material-hello-world --routing 17 | 18 | # Step 2 - Installing Angular Material dependencies 19 | 20 | Next, let's install these dependencies: 21 | 22 | npm install @angular/material @angular/cdk @angular/animations hammerjs 23 | 24 | # Step 3 - Adding Google Material Icons Font 25 | 26 | Let's add this to our index.html: 27 | 28 | 29 | 30 | # Step 4 - choosing a Theme 31 | 32 | Before starting to import components, let's choose a widget theme, have a look at the themes available 33 | 34 | inside `node_modules/@angular/material/prebuild-themes`. 35 | 36 | We can for example use the Indigo Pink theme by adding this line to our styles.css file: 37 | 38 | @import "~@angular/material/prebuilt-themes/indigo-pink.css"; 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Angular University 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Angular Material Course 3 | 4 | This repository contains the code of the [Angular Material In Depth](https://angular-university.io/course/angular-material-course) video course. 5 | 6 | This course repository is updated to Angular 19: 7 | 8 |  9 | 10 | You can find the starting point of the course in the [1-start branch](https://github.com/angular-university/angular-material-course/tree/1-start). 11 | 12 | This master branch contains the *final version of the course code*, that you can use as a reference if you choose to code along. 13 | 14 | # Installation pre-requisites 15 | 16 | IMPORTANT: Please use Node 18 LTS (Long Term Support version). 17 | 18 | # Installing the Angular CLI 19 | 20 | With the following command the angular-cli will be installed globally in your machine: 21 | 22 | npm install -g @angular/cli 23 | 24 | # How To install this repository 25 | 26 | We can install the master branch using the following commands: 27 | 28 | git clone https://github.com/angular-university/angular-material-course.git 29 | 30 | cd angular-material-course 31 | npm ci 32 | 33 | Note: **We recommend using npm ci, instead of npm install**. This will ensure that you use the exact dependency versions set on package-lock.json, unlike npm install which might potentially change those versions. 34 | 35 | # To Run the Development Backend Server 36 | 37 | Our Angular frontend connects to a simple Node server, running also in your local development machine. 38 | 39 | We can start the sample application backend with the following command: 40 | 41 | npm run server 42 | 43 | # To run the Development UI Server 44 | 45 | Once the backend server is up and running, we can now run our frontend server. 46 | 47 | To run the frontend part of our code, we will use the Angular CLI: 48 | 49 | npm start 50 | 51 | The application is visible at port 4200: [http://localhost:4200](http://localhost:4200) 52 | 53 | Note: **make sure to use command npm start and not ng serve, as npm start adds a couple extra options that are needed for our project ** 54 | 55 | # Other Courses 56 | 57 | # Other Courses 58 | # Modern Angular With Signals 59 | 60 | If you are looking for the [Modern Angular With Signals Course](https://angular-university.io/course/angular-signals-course), the repo with the full code can be found here: 61 | 62 |  63 | 64 | # Angular Forms In Depth 65 | 66 | If you are looking for the [Angular Forms In Depth](https://angular-university.io/course/angular-forms-course) course, the repo with the full code can be found here: 67 | 68 |  69 | 70 | # Angular Router In Depth 71 | 72 | If you are looking for the [Angular Router In Depth](https://angular-university.io/course/angular-router-course) course, the repo with the full code can be found here: 73 | 74 |  75 | 76 | # NgRx (with NgRx Data) - The Complete Guide 77 | 78 | If you are looking for the [Ngrx (with NgRx Data) - The Complete Guide](https://angular-university.io/course/ngrx-course), the repo with the full code can be found here: 79 | 80 |  81 | 82 | 83 | # Angular Core Deep Dive Course 84 | 85 | If you are looking for the [Angular Core Deep Dive Course](https://angular-university.io/course/angular-course), the repo with the full code can be found here: 86 | 87 |  88 | 89 | # RxJs In Practice 90 | 91 | If you are looking for the [RxJs In Practice](https://angular-university.io/course/rxjs-course), the repo with the full code can be found here: 92 | 93 |  94 | 95 | # NestJs In Practice (with MongoDB) 96 | 97 | If you are looking for the [NestJs In Practice Course](https://angular-university.io/course/nestjs-course), the repo with the full code can be found here: 98 | 99 |  100 | 101 | # Angular Testing Course 102 | 103 | If you are looking for the [Angular Testing Course](https://angular-university.io/course/angular-testing-course), the repo with the full code can be found here: 104 | 105 |  106 | 107 | # Serverless Angular with Firebase Course 108 | 109 | If you are looking for the [Serverless Angular with Firebase Course](https://angular-university.io/course/firebase-course), the repo with the full code can be found here: 110 | 111 |  112 | 113 | # Angular Universal Course 114 | 115 | If you are looking for the [Angular Universal Course](https://angular-university.io/course/angular-universal-course), the repo with the full code can be found here: 116 | 117 |  118 | 119 | # Angular PWA Course 120 | 121 | If you are looking for the [Angular PWA Course](https://angular-university.io/course/angular-pwa-course), the repo with the full code can be found here: 122 | 123 |  124 | 125 | # Angular Security Masterclass 126 | 127 | If you are looking for the [Angular Security Masterclass](https://angular-university.io/course/angular-security-course), the repo with the full code can be found here: 128 | 129 | [Angular Security Masterclass](https://github.com/angular-university/angular-security-course). 130 | 131 |  132 | 133 | # Angular Advanced Library Laboratory Course 134 | 135 | If you are looking for the Angular Advanced Course, the repo with the full code can be found here: 136 | 137 | [Angular Advanced Library Laboratory Course: Build Your Own Library](https://angular-university.io/course/angular-advanced-course). 138 | 139 |  140 | 141 | 142 | ## RxJs and Reactive Patterns Angular Architecture Course 143 | 144 | If you are looking for the RxJs and Reactive Patterns Angular Architecture Course code, the repo with the full code can be found here: 145 | 146 | [RxJs and Reactive Patterns Angular Architecture Course](https://angular-university.io/course/reactive-angular-architecture-course) 147 | 148 |  149 | 150 | 151 | ## Complete Typescript Course - Build A REST API 152 | 153 | If you are looking for the Complete Typescript 2 Course - Build a REST API, the repo with the full code can be found here: 154 | 155 | [https://angular-university.io/course/typescript-2-tutorial](https://github.com/angular-university/complete-typescript-course) 156 | 157 | [Github repo for this course](https://github.com/angular-university/complete-typescript-course) 158 | 159 |  160 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angular-material-course": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "architect": { 11 | "build": { 12 | "builder": "@angular-devkit/build-angular:application", 13 | "options": { 14 | "outputPath": { 15 | "base": "dist" 16 | }, 17 | "index": "src/index.html", 18 | "tsConfig": "src/tsconfig.app.json", 19 | "polyfills": [ 20 | "src/polyfills.ts" 21 | ], 22 | "assets": [ 23 | "src/assets", 24 | "src/favicon.ico" 25 | ], 26 | "styles": [ 27 | "src/styles.scss" 28 | ], 29 | "scripts": [], 30 | "extractLicenses": false, 31 | "sourceMap": true, 32 | "optimization": false, 33 | "namedChunks": true, 34 | "browser": "src/main.ts" 35 | }, 36 | "configurations": { 37 | "production": { 38 | "budgets": [ 39 | { 40 | "type": "anyComponentStyle", 41 | "maximumWarning": "6kb" 42 | } 43 | ], 44 | "optimization": true, 45 | "outputHashing": "all", 46 | "sourceMap": false, 47 | "namedChunks": false, 48 | "extractLicenses": true, 49 | "fileReplacements": [ 50 | { 51 | "replace": "src/environments/environment.ts", 52 | "with": "src/environments/environment.prod.ts" 53 | } 54 | ] 55 | } 56 | } 57 | }, 58 | "serve": { 59 | "builder": "@angular-devkit/build-angular:dev-server", 60 | "options": { 61 | "buildTarget": "angular-material-course:build" 62 | }, 63 | "configurations": { 64 | "production": { 65 | "buildTarget": "angular-material-course:build:production" 66 | } 67 | } 68 | }, 69 | "extract-i18n": { 70 | "builder": "@angular-devkit/build-angular:extract-i18n", 71 | "options": { 72 | "buildTarget": "angular-material-course:build" 73 | } 74 | }, 75 | "test": { 76 | "builder": "@angular-devkit/build-angular:karma", 77 | "options": { 78 | "main": "src/test.ts", 79 | "karmaConfig": "./karma.conf.js", 80 | "polyfills": "src/polyfills.ts", 81 | "tsConfig": "src/tsconfig.spec.json", 82 | "scripts": [], 83 | "styles": [ 84 | "src/styles.scss" 85 | ], 86 | "assets": [ 87 | "src/assets", 88 | "src/favicon.ico" 89 | ] 90 | } 91 | } 92 | } 93 | }, 94 | "angular-material-course-e2e": { 95 | "root": "", 96 | "sourceRoot": "", 97 | "projectType": "application", 98 | "architect": { 99 | "e2e": { 100 | "builder": "@angular-devkit/build-angular:protractor", 101 | "options": { 102 | "protractorConfig": "./protractor.conf.js", 103 | "devServerTarget": "angular-material-course:serve" 104 | } 105 | } 106 | } 107 | } 108 | }, 109 | "schematics": { 110 | "@schematics/angular:component": { 111 | "style": "scss" 112 | }, 113 | "@schematics/angular:directive": { 114 | "prefix": "" 115 | } 116 | }, 117 | "cli": { 118 | "analytics": "94ed0c6d-944a-4adc-89a1-2bc801038d0b" 119 | } 120 | } -------------------------------------------------------------------------------- /e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('angular-material-course App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to app!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": [ 9 | "jasmine", 10 | "jasminewd2", 11 | "node" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client:{ 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ], 20 | fixWebpackSourcePaths: true 21 | }, 22 | angularCli: { 23 | environment: 'dev' 24 | }, 25 | reporters: ['progress', 'kjhtml'], 26 | port: 9876, 27 | colors: true, 28 | logLevel: config.LOG_INFO, 29 | autoWatch: true, 30 | browsers: ['Chrome'], 31 | singleRun: false 32 | }); 33 | }; 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-material-course", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "ng": "ng", 7 | "start": "ng serve --proxy-config ./proxy.json", 8 | "server": "ts-node -P ./server/server.tsconfig.json ./server/server.ts", 9 | "build": "ng build", 10 | "test": "ng test", 11 | "lint": "ng lint", 12 | "e2e": "ng e2e" 13 | }, 14 | "private": true, 15 | "dependencies": { 16 | "@angular/animations": "19.0.0", 17 | "@angular/cdk": "^19.0.0", 18 | "@angular/common": "19.0.0", 19 | "@angular/compiler": "19.0.0", 20 | "@angular/core": "19.0.0", 21 | "@angular/forms": "19.0.0", 22 | "@angular/material": "^19.0.0", 23 | "@angular/material-moment-adapter": "19.0.0", 24 | "@angular/platform-browser": "19.0.0", 25 | "@angular/platform-browser-dynamic": "19.0.0", 26 | "@angular/router": "19.0.0", 27 | "core-js": "^2.4.1", 28 | "cors": "^2.8.5", 29 | "express": "^4.16.2", 30 | "moment": "^2.22.2", 31 | "rxjs": "6.5.4", 32 | "tslib": "^2.0.0", 33 | "zone.js": "~0.15.0" 34 | }, 35 | "devDependencies": { 36 | "@angular-devkit/build-angular": "^19.0.2", 37 | "@angular-devkit/schematics": "^19.0.2", 38 | "@angular/cli": "^19.0.2", 39 | "@angular/compiler-cli": "19.0.0", 40 | "@angular/language-service": "19.0.0", 41 | "@types/express": "^4.0.39", 42 | "@types/jasmine": "~3.8.0", 43 | "@types/jasminewd2": "~2.0.2", 44 | "@types/node": "^12.11.1", 45 | "jasmine-core": "~3.8.0", 46 | "jasmine-spec-reporter": "~5.0.0", 47 | "karma": "~6.3.2", 48 | "karma-chrome-launcher": "~3.1.0", 49 | "karma-cli": "~1.0.1", 50 | "karma-coverage-istanbul-reporter": "~3.0.2", 51 | "karma-jasmine": "~4.0.0", 52 | "karma-jasmine-html-reporter": "^1.5.0", 53 | "ts-node": "~3.2.0", 54 | "typescript": "5.6.3" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './e2e/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: 'e2e/tsconfig.e2e.json' 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /proxy.json: -------------------------------------------------------------------------------- 1 | { 2 | "/api": { 3 | "target": "http://localhost:9000", 4 | "secure": false 5 | } 6 | } -------------------------------------------------------------------------------- /server/db-data.ts: -------------------------------------------------------------------------------- 1 | export const COURSES: any = { 2 | 3 | 11: { 4 | id: 11, 5 | description: 'Angular Material Course', 6 | longDescription: 'Build Applications with the official Angular UI Widget Library', 7 | iconUrl: 'https://s3-us-west-1.amazonaws.com/angular-university/course-images/angular-material-course-1.jpg', 8 | category: 'BEGINNER', 9 | seqNo: 0, 10 | url: 'angular-material-course', 11 | price: 50, 12 | lessonsCount: 11, 13 | }, 14 | 15 | 19: { 16 | id: 19, 17 | description: 'Angular Forms In Depth', 18 | longDescription: 'Build complex enterprise data forms with the powerful Angular Forms module', 19 | iconUrl: 'https://angular-university.s3-us-west-1.amazonaws.com/course-images/angular-forms-course-small.jpg', 20 | courseListIcon: 'https://angular-academy.s3.amazonaws.com/main-logo/main-page-logo-small-hat.png', 21 | category: 'BEGINNER', 22 | lessonsCount: 10, 23 | seqNo: 1, 24 | url: 'angular-forms-course', 25 | price: 50 26 | }, 27 | 28 | 29 | 18: { 30 | id: 18, 31 | description: 'Angular Router In Depth', 32 | longDescription: 'Build large-scale Single Page Applications with the powerful Angular Router', 33 | iconUrl: 'https://angular-university.s3-us-west-1.amazonaws.com/course-images/angular-router-course.jpg', 34 | courseListIcon: 'https://angular-academy.s3.amazonaws.com/main-logo/main-page-logo-small-hat.png', 35 | category: 'BEGINNER', 36 | lessonsCount: 10, 37 | seqNo: 2, 38 | url: 'angular-router-course', 39 | price: 50 40 | }, 41 | 42 | 17: { 43 | id: 17, 44 | description: 'Reactive Angular Course', 45 | longDescription: 'How to build Angular applications in Reactive style using plain RxJs - Patterns and Anti-Patterns', 46 | iconUrl: 'https://angular-university.s3-us-west-1.amazonaws.com/course-images/reactive-angular-course.jpg', 47 | courseListIcon: 'https://angular-academy.s3.amazonaws.com/main-logo/main-page-logo-small-hat.png', 48 | category: 'BEGINNER', 49 | lessonsCount: 10, 50 | seqNo: 3, 51 | url: 'reactive-angular-course', 52 | price: 50 53 | 54 | }, 55 | 3: { 56 | id: 3, 57 | description: 'RxJs In Practice Course', 58 | longDescription: 'Understand the RxJs Observable pattern, learn the RxJs Operators via practical examples', 59 | iconUrl: 'https://s3-us-west-1.amazonaws.com/angular-university/course-images/rxjs-in-practice-course.png', 60 | courseListIcon: 'https://angular-academy.s3.amazonaws.com/main-logo/main-page-logo-small-hat.png', 61 | category: 'BEGINNER', 62 | lessonsCount: 10, 63 | seqNo: 4, 64 | url: 'rxjs-course', 65 | price: 50 66 | }, 67 | 68 | 4: { 69 | id: 4, 70 | description: 'NgRx (with NgRx Data) - The Complete Guide', 71 | longDescription: 'Learn the modern Ngrx Ecosystem, including NgRx Data, Store, Effects, Router Store, Ngrx Entity, and Dev Tools.', 72 | iconUrl: 'https://angular-university.s3-us-west-1.amazonaws.com/course-images/ngrx-v2.png', 73 | category: 'BEGINNER', 74 | lessonsCount: 10, 75 | seqNo: 5, 76 | url: 'ngrx-course', 77 | promo: false, 78 | price: 50 79 | }, 80 | 81 | 82 | 2: { 83 | id: 2, 84 | description: 'Angular Core Deep Dive', 85 | longDescription: 'A detailed walk-through of the most important part of Angular - the Core and Common modules', 86 | iconUrl: 'https://s3-us-west-1.amazonaws.com/angular-university/course-images/angular-core-in-depth-small.png', 87 | lessonsCount: 10, 88 | category: 'BEGINNER', 89 | seqNo: 6, 90 | url: 'angular-core-course', 91 | price: 50 92 | }, 93 | 94 | 95 | 5: { 96 | id: 5, 97 | 98 | description: 'Angular for Beginners', 99 | longDescription: 'Establish a solid layer of fundamentals, learn what\'s under the hood of Angular', 100 | iconUrl: 'https://angular-academy.s3.amazonaws.com/thumbnails/angular2-for-beginners-small-v2.png', 101 | courseListIcon: 'https://angular-academy.s3.amazonaws.com/main-logo/main-page-logo-small-hat.png', 102 | category: 'BEGINNER', 103 | lessonsCount: 10, 104 | seqNo: 7, 105 | url: 'angular-for-beginners', 106 | price: 50 107 | }, 108 | 109 | 12: { 110 | id: 12, 111 | description: 'Angular Testing Course', 112 | longDescription: 'In-depth guide to Unit Testing and E2E Testing of Angular Applications', 113 | iconUrl: 'https://s3-us-west-1.amazonaws.com/angular-university/course-images/angular-testing-small.png', 114 | category: 'BEGINNER', 115 | seqNo: 8, 116 | url: 'angular-testing-course', 117 | lessonsCount: 10, 118 | promo: false, 119 | price: 50 120 | }, 121 | 122 | 123 | 1: { 124 | id: 1, 125 | description: 'Serverless Angular with Firebase Course', 126 | longDescription: 'Serveless Angular with Firestore, Firebase Storage & Hosting, Firebase Cloud Functions & AngularFire', 127 | iconUrl: 'https://s3-us-west-1.amazonaws.com/angular-university/course-images/serverless-angular-small.png', 128 | lessonsCount: 10, 129 | category: 'BEGINNER', 130 | seqNo: 9, 131 | url: 'serverless-angular', 132 | price: 50 133 | }, 134 | 135 | 16: { 136 | id: 16, 137 | description: 'Stripe Payments In Practice', 138 | longDescription: 'Build your own ecommerce store & membership website with Firebase, Stripe and Express', 139 | iconUrl: 'https://angular-university.s3-us-west-1.amazonaws.com/course-images/stripe-course.jpg', 140 | lessonsCount: 10, 141 | category: 'BEGINNER', 142 | seqNo: 10, 143 | url: 'stripe-course', 144 | price: 50 145 | }, 146 | 147 | 148 | 14: { 149 | id: 14, 150 | description: 'NestJs In Practice (with MongoDB)', 151 | longDescription: 'Build a modern REST backend using Typescript, MongoDB and the familiar Angular API.', 152 | iconUrl: 'https://angular-university.s3-us-west-1.amazonaws.com/course-images/nestjs-v2.png', 153 | category: 'BEGINNER', 154 | lessonsCount: 10, 155 | seqNo: 11, 156 | url: 'nestjs-course', 157 | promo: false, 158 | price: 50 159 | }, 160 | 161 | 162 | 6: { 163 | id: 6, 164 | description: 'Angular Security Course - Web Security Fundamentals', 165 | longDescription: 'Learn Web Security Fundamentals and apply them to defend an Angular / Node Application from multiple types of attacks.', 166 | iconUrl: 'https://s3-us-west-1.amazonaws.com/angular-university/course-images/security-cover-small-v2.png', 167 | courseListIcon: 'https://s3-us-west-1.amazonaws.com/angular-university/course-images/lock-v2.png', 168 | category: 'ADVANCED', 169 | lessonsCount: 11, 170 | seqNo: 12, 171 | url: 'angular-security-course', 172 | price: 50 173 | }, 174 | 175 | 7: { 176 | id: 7, 177 | description: 'Angular PWA - Progressive Web Apps Course', 178 | longDescription: 'Learn Angular Progressive Web Applications, build the future of the Web Today.', 179 | iconUrl: 'https://s3-us-west-1.amazonaws.com/angular-university/course-images/angular-pwa-course.png', 180 | courseListIcon: 'https://s3-us-west-1.amazonaws.com/angular-university/course-images/alien.png', 181 | category: 'ADVANCED', 182 | lessonsCount: 8, 183 | seqNo: 14, 184 | url: 'angular-pwa-course', 185 | price: 50 186 | }, 187 | 188 | 8: { 189 | id: 8, 190 | description: 'Angular Advanced Library Laboratory: Build Your Own Library', 191 | longDescription: 'Learn Advanced Angular functionality typically used in Library Development. Advanced Components, Directives, Testing, Npm', 192 | iconUrl: 'https://angular-academy.s3.amazonaws.com/thumbnails/advanced_angular-small-v3.png', 193 | courseListIcon: 'https://angular-academy.s3.amazonaws.com/thumbnails/angular-advanced-lesson-icon.png', 194 | category: 'ADVANCED', 195 | seqNo: 15, 196 | url: 'angular-advanced-course', 197 | price: 50 198 | }, 199 | 200 | 9: { 201 | id: 9, 202 | description: 'The Complete Typescript Course', 203 | longDescription: 'Complete Guide to Typescript From Scratch: Learn the language in-depth and use it to build a Node REST API.', 204 | iconUrl: 'https://angular-academy.s3.amazonaws.com/thumbnails/typescript-2-small.png', 205 | courseListIcon: 'https://angular-academy.s3.amazonaws.com/thumbnails/typescript-2-lesson.png', 206 | category: 'BEGINNER', 207 | seqNo: 16, 208 | url: 'typescript-course', 209 | price: 50 210 | }, 211 | 212 | }; 213 | 214 | export const LESSONS = { 215 | 216 | 1: { 217 | id: 1, 218 | 'description': 'Angular Tutorial For Beginners - Build Your First App - Hello World Step By Step', 219 | 'duration': '4:17', 220 | 'seqNo': 1, 221 | courseId: 5 222 | }, 223 | 2: { 224 | id: 2, 225 | 'description': 'Building Your First Component - Component Composition', 226 | 'duration': '2:07', 227 | 'seqNo': 2, 228 | courseId: 5 229 | }, 230 | 3: { 231 | id: 3, 232 | 'description': 'Component @Input - How To Pass Input Data To an Component', 233 | 'duration': '2:33', 234 | 'seqNo': 3, 235 | courseId: 5 236 | }, 237 | 4: { 238 | id: 4, 239 | 'description': ' Component Events - Using @Output to create custom events', 240 | 'duration': '4:44', 241 | 'seqNo': 4, 242 | courseId: 5 243 | }, 244 | 5: { 245 | id: 5, 246 | 'description': ' Component Templates - Inline Vs External', 247 | 'duration': '2:55', 248 | 'seqNo': 5, 249 | courseId: 5 250 | }, 251 | 6: { 252 | id: 6, 253 | 'description': 'Styling Components - Learn About Component Style Isolation', 254 | 'duration': '3:27', 255 | 'seqNo': 6, 256 | courseId: 5 257 | }, 258 | 7: { 259 | id: 7, 260 | 'description': ' Component Interaction - Extended Components Example', 261 | 'duration': '9:22', 262 | 'seqNo': 7, 263 | courseId: 5 264 | }, 265 | 8: { 266 | id: 8, 267 | 'description': ' Components Tutorial For Beginners - Components Exercise !', 268 | 'duration': '1:26', 269 | 'seqNo': 8, 270 | courseId: 5 271 | }, 272 | 9: { 273 | id: 9, 274 | 'description': ' Components Tutorial For Beginners - Components Exercise Solution Inside', 275 | 'duration': '2:08', 276 | 'seqNo': 9, 277 | courseId: 5 278 | }, 279 | 10: { 280 | id: 10, 281 | 'description': ' Directives - Inputs, Output Event Emitters and How To Export Template References', 282 | 'duration': '4:01', 283 | 'seqNo': 10, 284 | courseId: 5 285 | }, 286 | 287 | 288 | // Security Course 289 | 11: { 290 | id: 11, 291 | 'description': 'Course Helicopter View', 292 | 'duration': '08:19', 293 | 'seqNo': 1, 294 | courseId: 6 295 | }, 296 | 297 | 12: { 298 | id: 12, 299 | 'description': 'Installing Git, Node, NPM and Choosing an IDE', 300 | 'duration': '04:17', 301 | 'seqNo': 2, 302 | courseId: 6 303 | }, 304 | 305 | 13: { 306 | id: 13, 307 | 'description': 'Installing The Lessons Code - Learn Why Its Essential To Use NPM 5', 308 | 'duration': '06:05', 309 | 'seqNo': 3, 310 | courseId: 6 311 | }, 312 | 313 | 14: { 314 | id: 14, 315 | 'description': 'How To Run Node In TypeScript With Hot Reloading', 316 | 'duration': '03:57', 317 | 'seqNo': 4, 318 | courseId: 6 319 | }, 320 | 321 | 15: { 322 | id: 15, 323 | 'description': 'Guided Tour Of The Sample Application', 324 | 'duration': '06:00', 325 | 'seqNo': 5, 326 | courseId: 6 327 | }, 328 | 16: { 329 | id: 16, 330 | 'description': 'Client Side Authentication Service - API Design', 331 | 'duration': '04:53', 332 | 'seqNo': 6, 333 | courseId: 6 334 | }, 335 | 17: { 336 | id: 17, 337 | 'description': 'Client Authentication Service - Design and Implementation', 338 | 'duration': '09:14', 339 | 'seqNo': 7, 340 | courseId: 6 341 | }, 342 | 18: { 343 | id: 18, 344 | 'description': 'The New Angular HTTP Client - Doing a POST Call To The Server', 345 | 'duration': '06:08', 346 | 'seqNo': 8, 347 | courseId: 6 348 | }, 349 | 19: { 350 | id: 19, 351 | 'description': 'User Sign Up Server-Side Implementation in Express', 352 | 'duration': '08:50', 353 | 'seqNo': 9, 354 | courseId: 6 355 | }, 356 | 20: { 357 | id: 20, 358 | 'description': 'Introduction To Cryptographic Hashes - A Running Demo', 359 | 'duration': '05:46', 360 | 'seqNo': 10, 361 | courseId: 6 362 | }, 363 | 21: { 364 | id: 21, 365 | 'description': 'Some Interesting Properties Of Hashing Functions - Validating Passwords', 366 | 'duration': '06:31', 367 | 'seqNo': 11, 368 | courseId: 6 369 | }, 370 | 371 | 372 | // PWA course 373 | 374 | 22: { 375 | id: 22, 376 | 'description': 'Course Kick-Off - Install Node, NPM, IDE And Service Workers Section Code', 377 | 'duration': '07:19', 378 | 'seqNo': 1, 379 | courseId: 7 380 | }, 381 | 23: { 382 | id: 23, 383 | 'description': 'Service Workers In a Nutshell - Service Worker Registration', 384 | 'duration': '6:59', 385 | 'seqNo': 2, 386 | courseId: 7 387 | }, 388 | 24: { 389 | id: 24, 390 | 'description': 'Service Workers Hello World - Lifecycle Part 1 and PWA Chrome Dev Tools', 391 | 'duration': '7:28', 392 | 'seqNo': 3, 393 | courseId: 7 394 | }, 395 | 25: { 396 | id: 25, 397 | 'description': 'Service Workers and Application Versioning - Install & Activate Lifecycle Phases', 398 | 'duration': '10:17', 399 | 'seqNo': 4, 400 | courseId: 7 401 | }, 402 | 403 | 26: { 404 | id: 26, 405 | 'description': 'Downloading The Offline Page - The Service Worker Installation Phase', 406 | 'duration': '09:50', 407 | 'seqNo': 5, 408 | courseId: 7 409 | }, 410 | 27: { 411 | id: 27, 412 | 'description': 'Introduction to the Cache Storage PWA API', 413 | 'duration': '04:44', 414 | 'seqNo': 6, 415 | courseId: 7 416 | }, 417 | 28: { 418 | id: 28, 419 | 'description': 'View Service Workers HTTP Interception Features In Action', 420 | 'duration': '06:07', 421 | 'seqNo': 7, 422 | courseId: 7 423 | }, 424 | 29: { 425 | id: 29, 426 | 'description': 'Service Workers Error Handling - Serving The Offline Page', 427 | 'duration': '5:38', 428 | 'seqNo': 8, 429 | courseId: 7 430 | }, 431 | 432 | // Serverless Angular with Firebase Course 433 | 434 | 30: { 435 | id: 30, 436 | description: 'Development Environment Setup', 437 | 'duration': '5:38', 438 | 'seqNo': 1, 439 | courseId: 1 440 | }, 441 | 442 | 31: { 443 | id: 31, 444 | description: 'Introduction to the Firebase Ecosystem', 445 | 'duration': '5:12', 446 | 'seqNo': 2, 447 | courseId: 1 448 | }, 449 | 450 | 32: { 451 | id: 32, 452 | description: 'Importing Data into Firestore', 453 | 'duration': '4:07', 454 | 'seqNo': 3, 455 | courseId: 1 456 | }, 457 | 458 | 33: { 459 | id: 33, 460 | description: 'Firestore Documents in Detail', 461 | 'duration': '7:32', 462 | 'seqNo': 4, 463 | courseId: 1 464 | }, 465 | 466 | 34: { 467 | id: 34, 468 | description: 'Firestore Collections in Detail', 469 | 'duration': '6:28', 470 | 'seqNo': 5, 471 | courseId: 1 472 | }, 473 | 474 | 35: { 475 | id: 35, 476 | description: 'Firestore Unique Identifiers', 477 | 'duration': '4:38', 478 | 'seqNo': 6, 479 | courseId: 1 480 | }, 481 | 482 | 36: { 483 | id: 36, 484 | description: 'Querying Firestore Collections', 485 | 'duration': '7:54', 486 | 'seqNo': 7, 487 | courseId: 1 488 | }, 489 | 490 | 37: { 491 | id: 37, 492 | description: 'Firebase Security Rules In Detail', 493 | 'duration': '5:31', 494 | 'seqNo': 8, 495 | courseId: 1 496 | }, 497 | 498 | 38: { 499 | id: 38, 500 | description: 'Firebase Cloud Functions In Detail', 501 | 'duration': '8:19', 502 | 'seqNo': 9, 503 | courseId: 1 504 | }, 505 | 506 | 39: { 507 | id: 39, 508 | description: 'Firebase Storage In Detail', 509 | 'duration': '7:05', 510 | 'seqNo': 10, 511 | courseId: 1 512 | }, 513 | 514 | 515 | // Angular Testing Course 516 | 517 | 40: { 518 | id: 40, 519 | description: 'Angular Testing Course - Helicopter View', 520 | 'duration': '5:38', 521 | 'seqNo': 1, 522 | courseId: 12 523 | }, 524 | 525 | 41: { 526 | id: 41, 527 | description: 'Setting Up the Development Environment', 528 | 'duration': '5:12', 529 | 'seqNo': 2, 530 | courseId: 12 531 | }, 532 | 533 | 42: { 534 | id: 42, 535 | description: 'Introduction to Jasmine, Spies and specs', 536 | 'duration': '4:07', 537 | 'seqNo': 3, 538 | courseId: 12 539 | }, 540 | 541 | 43: { 542 | id: 43, 543 | description: 'Introduction to Service Testing', 544 | 'duration': '7:32', 545 | 'seqNo': 4, 546 | courseId: 12 547 | }, 548 | 549 | 44: { 550 | id: 44, 551 | description: 'Settting up the Angular TestBed', 552 | 'duration': '6:28', 553 | 'seqNo': 5, 554 | courseId: 12 555 | }, 556 | 557 | 45: { 558 | id: 45, 559 | description: 'Mocking Angular HTTP requests', 560 | 'duration': '4:38', 561 | 'seqNo': 6, 562 | courseId: 12 563 | }, 564 | 565 | 46: { 566 | id: 46, 567 | description: 'Simulating Failing HTTP Requests', 568 | 'duration': '7:54', 569 | 'seqNo': 7, 570 | courseId: 12 571 | }, 572 | 573 | 47: { 574 | id: 47, 575 | description: 'Introduction to Angular Component Testing', 576 | 'duration': '5:31', 577 | 'seqNo': 8, 578 | courseId: 12 579 | }, 580 | 581 | 48: { 582 | id: 48, 583 | description: 'Testing Angular Components without the DOM', 584 | 'duration': '8:19', 585 | 'seqNo': 9, 586 | courseId: 12 587 | }, 588 | 589 | 49: { 590 | id: 49, 591 | description: 'Testing Angular Components with the DOM', 592 | 'duration': '7:05', 593 | 'seqNo': 10, 594 | courseId: 12 595 | }, 596 | 597 | 598 | // Ngrx Course 599 | 50: { 600 | id: 50, 601 | 'description': 'Welcome to the Angular Ngrx Course', 602 | 'duration': '6:53', 603 | 'seqNo': 1, 604 | courseId: 4 605 | 606 | }, 607 | 51: { 608 | id: 51, 609 | 'description': 'The Angular Ngrx Architecture Course - Helicopter View', 610 | 'duration': '5:52', 611 | 'seqNo': 2, 612 | courseId: 4 613 | }, 614 | 52: { 615 | id: 52, 616 | 'description': 'The Origins of Flux - Understanding the Famous Facebook Bug Problem', 617 | 'duration': '8:17', 618 | 'seqNo': 3, 619 | courseId: 4 620 | }, 621 | 53: { 622 | id: 53, 623 | 'description': 'Custom Global Events - Why Don\'t They Scale In Complexity?', 624 | 'duration': '7:47', 625 | 'seqNo': 4, 626 | courseId: 4 627 | }, 628 | 54: { 629 | id: 54, 630 | 'description': 'The Flux Architecture - How Does it Solve Facebook Counter Problem?', 631 | 'duration': '9:22', 632 | 'seqNo': 5, 633 | courseId: 4 634 | }, 635 | 55: { 636 | id: 55, 637 | 'description': 'Unidirectional Data Flow And The Angular Development Mode', 638 | 'duration': '7:07', 639 | 'seqNo': 6, 640 | courseId: 4 641 | }, 642 | 643 | 56: { 644 | id: 56, 645 | 'description': 'Dispatching an Action - Implementing the Login Component', 646 | 'duration': '4:39', 647 | 'seqNo': 7, 648 | courseId: 4 649 | }, 650 | 57: { 651 | id: 57, 652 | 'description': 'Setting Up the Ngrx DevTools - Demo', 653 | 'duration': '4:44', 654 | 'seqNo': 8, 655 | courseId: 4 656 | }, 657 | 58: { 658 | id: 58, 659 | 'description': 'Understanding Reducers - Writing Our First Reducer', 660 | 'duration': '9:10', 661 | 'seqNo': 9, 662 | courseId: 4 663 | }, 664 | 59: { 665 | id: 59, 666 | 'description': 'How To Define the Store Initial State', 667 | 'duration': '9:10', 668 | 'seqNo': 10, 669 | courseId: 4 670 | }, 671 | 672 | // NestJs Course 673 | 674 | 60: { 675 | id: 60, 676 | 'description': 'Introduction to NestJs', 677 | 'duration': '4:29', 678 | 'seqNo': 1, 679 | courseId: 14 680 | }, 681 | 61: { 682 | id: 61, 683 | 'description': 'Development Environment Setup', 684 | 'duration': '6:37', 685 | 'seqNo': 2, 686 | courseId: 14 687 | }, 688 | 62: { 689 | id: 62, 690 | 'description': 'Setting up a MongoDB Database', 691 | 'duration': '6:38', 692 | 'seqNo': 3, 693 | courseId: 14 694 | }, 695 | 63: { 696 | id: 63, 697 | 'description': 'CRUD with NestJs - Controllers and Repositories', 698 | 'duration': '12:12', 699 | 'seqNo': 4, 700 | courseId: 14 701 | }, 702 | 64: { 703 | id: 64, 704 | 'description': 'First REST endpoint - Get All Courses', 705 | 'duration': '3:42', 706 | 'seqNo': 5, 707 | courseId: 14 708 | }, 709 | 65: { 710 | id: 65, 711 | 'description': 'Error Handling', 712 | 'duration': '5:15', 713 | 'seqNo': 6, 714 | courseId: 14 715 | }, 716 | 66: { 717 | id: 66, 718 | 'description': 'NestJs Middleware', 719 | 'duration': '7:08', 720 | 'seqNo': 7, 721 | courseId: 14 722 | }, 723 | 67: { 724 | id: 67, 725 | 'description': 'Authentication in NestJs', 726 | 'duration': '13:22', 727 | 'seqNo': 8, 728 | courseId: 14 729 | }, 730 | 68: { 731 | id: 68, 732 | 'description': 'Authorization in NestJs', 733 | 'duration': '6:43', 734 | 'seqNo': 9, 735 | courseId: 14 736 | }, 737 | 69: { 738 | id: 69, 739 | 'description': 'Guards & Interceptors', 740 | 'duration': '8:16', 741 | 'seqNo': 10, 742 | courseId: 14 743 | }, 744 | 745 | // Stripe Course 746 | 747 | 70: { 748 | id: 70, 749 | 'description': 'Introduction to Stripe Payments', 750 | 'duration': '03:45', 751 | 'seqNo': 0, 752 | courseId: 16 753 | }, 754 | 71: { 755 | id: 71, 756 | 'description': 'The advantages of Stripe Checkout', 757 | 'duration': '08:36', 758 | 'seqNo': 1, 759 | courseId: 16 760 | }, 761 | 72: { 762 | id: 72, 763 | 'description': 'Setting up the development environment', 764 | 'duration': '09:10', 765 | 'seqNo': 2, 766 | courseId: 16 767 | }, 768 | 73: { 769 | id: 73, 770 | 'description': 'Creating a server Checkout Session', 771 | 'duration': '07:20', 772 | 'seqNo': 3, 773 | courseId: 16 774 | }, 775 | 74: { 776 | id: 74, 777 | 'description': 'Redirecting to the Stripe Checkout page', 778 | 'duration': '11:47', 779 | 'seqNo': 4, 780 | courseId: 16 781 | }, 782 | 75: { 783 | id: 75, 784 | 'description': 'Order fulfillment webhook', 785 | 'duration': '06:30', 786 | 'seqNo': 5, 787 | courseId: 16 788 | }, 789 | 76: { 790 | id: 76, 791 | 'description': 'Installing the Stripe CLI', 792 | 'duration': '4:13', 793 | 'seqNo': 6, 794 | courseId: 16 795 | }, 796 | 77: { 797 | id: 77, 798 | 'description': 'Firestore Security Rules for protecting Premium content', 799 | 'duration': '05:47', 800 | 'seqNo': 7, 801 | courseId: 16 802 | }, 803 | 78: { 804 | id: 78, 805 | 'description': 'Stripe Subscriptions with Stripe Checkout', 806 | 'duration': '05:17', 807 | 'seqNo': 8, 808 | courseId: 16 809 | }, 810 | 79: { 811 | id: 79, 812 | 'description': 'Stripe Subscription Fulfillment', 813 | 'duration': '07:50', 814 | 'seqNo': 9, 815 | courseId: 16 816 | }, 817 | 818 | 819 | // Reactive Angular Course 820 | 821 | 80: { 822 | id: 80, 823 | 'description': 'Introduction to Reactive Programming', 824 | 'duration': '03:45', 825 | 'seqNo': 0, 826 | courseId: 17, 827 | videoId: 'Df1QnesgB_s', 828 | }, 829 | 81: { 830 | id: 81, 831 | 'description': 'Introduction to RxJs', 832 | 'duration': '08:36', 833 | 'seqNo': 1, 834 | courseId: 17, 835 | videoId: '8m5RrAtqlyw', 836 | }, 837 | 82: { 838 | id: 82, 839 | 'description': 'Setting up the development environment', 840 | 'duration': '09:10', 841 | 'seqNo': 2, 842 | courseId: 17, 843 | videoId: '3fDbUB-nKqc', 844 | }, 845 | 83: { 846 | id: 83, 847 | 'description': 'Designing and building a Service Layer', 848 | 'duration': '07:20', 849 | 'seqNo': 3, 850 | courseId: 17, 851 | videoId: '', 852 | }, 853 | 84: { 854 | id: 84, 855 | 'description': 'Stateless Observable Services', 856 | 'duration': '11:47', 857 | 'seqNo': 4, 858 | courseId: 17, 859 | videoId: 'qvDPnRs_ZPA', 860 | }, 861 | 85: { 862 | id: 85, 863 | 'description': 'Smart vs Presentational Components', 864 | 'duration': '06:30', 865 | 'seqNo': 5, 866 | courseId: 17, 867 | videoId: '5bsZJGAelFM', 868 | }, 869 | 86: { 870 | id: 86, 871 | 'description': 'Lightweight state management', 872 | 'duration': '4:13', 873 | 'seqNo': 6, 874 | courseId: 17, 875 | videoId: '9m3_HHeP9Ko', 876 | }, 877 | 87: { 878 | id: 87, 879 | 'description': 'Event bubbling anti-pattern', 880 | 'duration': '05:47', 881 | 'seqNo': 7, 882 | courseId: 17, 883 | videoId: 'PRQCAL_RMVo', 884 | }, 885 | 88: { 886 | id: 88, 887 | 'description': 'Master detail with cached master table', 888 | 'duration': '05:17', 889 | 'seqNo': 8, 890 | courseId: 17, 891 | videoId: 'du4ib4jBUG0' 892 | }, 893 | 89: { 894 | id: 89, 895 | 'description': 'Error handling', 896 | 'duration': '07:50', 897 | 'seqNo': 9, 898 | courseId: 17, 899 | videoId: '8m5RrAtqlyw' 900 | }, 901 | 902 | 903 | 904 | // Angular Router Course 905 | 90: { 906 | id: 90, 907 | 'description': 'What is a Single Page Application?', 908 | 'duration': '04:00', 909 | 'seqNo': 1, 910 | courseId: 18, 911 | videoId: 'VES1eTNxi1s' 912 | }, 913 | 91: { 914 | id: 91, 915 | 'description': 'Setting Up The Development Environment', 916 | 'duration': '06:05', 917 | 'seqNo': 2, 918 | courseId: 18, 919 | videoId: 'ANfplcxnl78' 920 | }, 921 | 92: { 922 | id: 92, 923 | 'description': 'Angular Router Setup', 924 | 'duration': '02:36', 925 | 'seqNo': 3, 926 | courseId: 18, 927 | videoId: '9ez72LAd6mM' 928 | }, 929 | 93: { 930 | id: 93, 931 | 'description': 'Configuring a Home Route and Fallback Route', 932 | 'duration': '02:55', 933 | 'seqNo': 4, 934 | courseId: 18, 935 | videoId: 'Clj-jZpl64w' 936 | }, 937 | 94: { 938 | id: 94, 939 | 'description': 'Styling Active Routes With The routerLinkActive And routerLinkActiveOptions', 940 | 'duration': '07:50', 941 | 'seqNo': 5, 942 | courseId: 18, 943 | videoId: 'zcgnsmPVc30' 944 | }, 945 | 95: { 946 | id: 95, 947 | 'description': 'Child Routes - How To Setup a Master Detail Route', 948 | 'duration': '04:10', 949 | 'seqNo': 6, 950 | courseId: 18, 951 | videoId: 'zcgnsmPVc30' 952 | }, 953 | 96: { 954 | id: 96, 955 | 'description': 'Programmatic Router Navigation via the Router API ', 956 | 'duration': '03:59', 957 | 'seqNo': 7, 958 | courseId: 18, 959 | videoId: 'VES1eTNxi1s' 960 | }, 961 | 97: { 962 | id: 97, 963 | 'description': 'Relative And Absolute Router Navigation', 964 | 'duration': '04:58', 965 | 'seqNo': 8, 966 | courseId: 18, 967 | videoId: 'MQl9Zs3QqGM' 968 | }, 969 | 98: { 970 | id: 98, 971 | 'description': 'Master Detail Navigation And Route Parameters', 972 | 'duration': '06:03', 973 | 'seqNo': 9, 974 | courseId: 18, 975 | videoId: 'ANfplcxnl78' 976 | }, 977 | 978 | 99: { 979 | id: 99, 980 | 'description': 'The Route Parameters Observable', 981 | 'duration': '06:50', 982 | 'seqNo': 10, 983 | courseId: 18, 984 | videoId: 'zcgnsmPVc30' 985 | }, 986 | 100: { 987 | id: 100, 988 | 'description': 'Optional Route Query Parameters', 989 | 'duration': '03:03', 990 | 'seqNo': 11, 991 | courseId: 18, 992 | videoId: '0Qsg8fyKwO4' 993 | }, 994 | 101: { 995 | id: 101, 996 | 'description': 'The queryParams Directive and the Query Parameters Observable', 997 | 'duration': '07:50', 998 | 'seqNo': 12, 999 | courseId: 18, 1000 | videoId: 'VES1eTNxi1s' 1001 | }, 1002 | 102: { 1003 | id: 102, 1004 | 'description': 'Exiting an Angular Route - How To Prevent Memory Leaks', 1005 | 'duration': '07:50', 1006 | 'seqNo': 13, 1007 | courseId: 18, 1008 | videoId: 'ANfplcxnl78' 1009 | }, 1010 | 103: { 1011 | id: 103, 1012 | 'description': 'CanDeactivate Route Guard', 1013 | 'duration': '04:50', 1014 | 'seqNo': 14, 1015 | courseId: 18, 1016 | videoId: '9ez72LAd6mM' 1017 | }, 1018 | 104: { 1019 | id: 104, 1020 | 'description': 'CanActivate Route Guard - An Example of An Asynchronous Route Guard', 1021 | 'duration': '03:32', 1022 | 'seqNo': 15, 1023 | courseId: 18, 1024 | videoId: 'Clj-jZpl64w' 1025 | }, 1026 | 1027 | 1028 | 105: { 1029 | id: 105, 1030 | 'description': 'Configure Auxiliary Routes in the Angular Router', 1031 | 'duration': '05:16', 1032 | 'seqNo': 16, 1033 | courseId: 18, 1034 | videoId: 'zcgnsmPVc30' 1035 | }, 1036 | 1037 | 106: { 1038 | id: 106, 1039 | 'description': 'Angular Auxiliary Routes - How To Pass Router Parameters', 1040 | 'duration': '07:50', 1041 | 'seqNo': 17, 1042 | courseId: 18, 1043 | videoId: 'yjQUkNHb1Is' 1044 | }, 1045 | 107: { 1046 | id: 107, 1047 | 'description': 'Angular Router Redirects and Path Matching', 1048 | 'duration': '02:59', 1049 | 'seqNo': 18, 1050 | courseId: 18, 1051 | videoId: 'VES1eTNxi1s' 1052 | }, 1053 | 108: { 1054 | id: 108, 1055 | 'description': 'Angular Router Hash Location Strategy', 1056 | 'duration': '07:50', 1057 | 'seqNo': 19, 1058 | courseId: 18, 1059 | videoId: 'MQl9Zs3QqGM' 1060 | }, 1061 | 109: { 1062 | id: 109, 1063 | 'description': 'Angular Router Lazy Loading and Shared Modules', 1064 | 'duration': '08:45', 1065 | 'seqNo': 20, 1066 | courseId: 18, 1067 | videoId: '0Qsg8fyKwO4' 1068 | }, 1069 | 110: { 1070 | id: 110, 1071 | 'description': 'Exercise - Implement a Widget Dashboard', 1072 | 'duration': '07:50', 1073 | 'seqNo': 21, 1074 | courseId: 18, 1075 | videoId: 'VES1eTNxi1s' 1076 | }, 1077 | 111: { 1078 | id: 111, 1079 | 'description': 'Exercise Solution ', 1080 | 'duration': '07:50', 1081 | 'seqNo': 22, 1082 | courseId: 18, 1083 | videoId: '0Qsg8fyKwO4' 1084 | }, 1085 | 1086 | // Angular Material In Depth 1087 | 1088 | 120: { 1089 | id: 120, 1090 | 'description': 'Introduction to Angular Material', 1091 | 'duration': '4:17', 1092 | 'seqNo': 1, 1093 | courseId: 11, 1094 | longDescription: "A quick introduction to the Angular Material library." 1095 | }, 1096 | 121: { 1097 | id: 121, 1098 | 'description': 'Navigation and Containers', 1099 | 'duration': '6:37', 1100 | 'seqNo': 2, 1101 | courseId: 11, 1102 | longDescription: "Guided tour of navigation elements and container." 1103 | }, 1104 | 122: { 1105 | id: 122, 1106 | 'description': 'Data Tables', 1107 | 'duration': '8:03', 1108 | 'seqNo': 3, 1109 | courseId: 11, 1110 | longDescription: "Angular Material Data Tables in detail." 1111 | }, 1112 | 123: { 1113 | id: 123, 1114 | 'description': 'Dialogs', 1115 | 'duration': '11:46', 1116 | 'seqNo': 4, 1117 | courseId: 11, 1118 | longDescription: "Modal elements and how to use them." 1119 | }, 1120 | 124: { 1121 | id: 124, 1122 | 'description': 'Commonly used Form Controls', 1123 | 'duration': '7:17', 1124 | 'seqNo': 5, 1125 | courseId: 11, 1126 | longDescription: "All sorts of commonly needed form controls." 1127 | }, 1128 | 125: { 1129 | id: 125, 1130 | 'description': 'Drag and Drop', 1131 | 'duration': '8:16', 1132 | 'seqNo': 6, 1133 | courseId: 11, 1134 | longDescription: "How to use drag and drop." 1135 | }, 1136 | 126: { 1137 | id: 126, 1138 | 'description': 'Responsive Design', 1139 | 'duration': '7:28', 1140 | 'seqNo': 7, 1141 | courseId: 11, 1142 | longDescription: "Everything about making our screens responsive." 1143 | }, 1144 | 127: { 1145 | id: 127, 1146 | 'description': 'Tree Component', 1147 | 'duration': '11:09', 1148 | 'seqNo': 8, 1149 | courseId: 11, 1150 | longDescription: "All about the Angular Material Tree component." 1151 | }, 1152 | 128: { 1153 | id: 128, 1154 | 'description': 'Virtual Scrolling', 1155 | 'duration': '3:44', 1156 | 'seqNo': 9, 1157 | courseId: 11, 1158 | longDescription: "How to use virtual scrolling to handle large amounts of data." 1159 | }, 1160 | 129: { 1161 | id: 129, 1162 | 'description': 'Custom Themes', 1163 | 'duration': '8:55', 1164 | 'seqNo': 10, 1165 | courseId: 11, 1166 | longDescription: "How to build your own custom Angular Material theme." 1167 | }, 1168 | 130: { 1169 | id: 130, 1170 | 'description': 'Changing Theme at Runtime', 1171 | 'duration': '12:37', 1172 | 'seqNo': 11, 1173 | courseId: 11, 1174 | longDescription: "" 1175 | } 1176 | }; 1177 | 1178 | 1179 | export const USERS = { 1180 | 1: { 1181 | id: 1, 1182 | email: 'test@angular-university.io', 1183 | password: 'test', 1184 | pictureUrl: 'https://angular-academy.s3.amazonaws.com/main-logo/main-page-logo-small-hat.png' 1185 | } 1186 | 1187 | }; 1188 | 1189 | 1190 | export function findCourseById(courseId: number) { 1191 | return COURSES[courseId]; 1192 | } 1193 | 1194 | export function findLessonsForCourse(courseId: number) { 1195 | return Object.values(LESSONS).filter(lesson => lesson.courseId == courseId); 1196 | } 1197 | 1198 | export function authenticate(email: string, password: string) { 1199 | 1200 | const user: any = Object.values(USERS).find(user => user.email === email); 1201 | 1202 | if (user && user.password == password) { 1203 | return user; 1204 | } else { 1205 | return undefined; 1206 | } 1207 | 1208 | } 1209 | -------------------------------------------------------------------------------- /server/get-courses.route.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import {Request, Response} from 'express'; 4 | import {COURSES} from "./db-data"; 5 | 6 | 7 | 8 | export function getAllCourses(req: Request, res: Response) { 9 | 10 | res.status(200).json({payload:Object.values(COURSES).sort((c1:any, c2:any) => c1.seqNo - c2.seqNo)}); 11 | 12 | } 13 | 14 | 15 | export function getCourseById(req: Request, res: Response) { 16 | 17 | const courseId = req.params["id"]; 18 | 19 | const courses:any = Object.values(COURSES); 20 | 21 | const course = courses.find(course => course.id == courseId); 22 | 23 | res.status(200).json(course); 24 | } 25 | -------------------------------------------------------------------------------- /server/search-lessons.route.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | import {Request, Response} from 'express'; 5 | import {LESSONS} from "./db-data"; 6 | import {setTimeout} from "timers"; 7 | 8 | 9 | 10 | export function searchLessons(req: Request, res: Response) { 11 | 12 | const queryParams = req.query as any; 13 | 14 | const courseId = queryParams.courseId, 15 | filter = queryParams.filter || '', 16 | sortOrder = queryParams.sortOrder, 17 | pageNumber = parseInt(queryParams.pageNumber) || 0, 18 | pageSize = parseInt(queryParams.pageSize), 19 | sortColumn = queryParams.sortColumn ?? "seqNo"; 20 | 21 | let lessons = Object.values(LESSONS) 22 | .filter(lesson => lesson.courseId == courseId) 23 | .sort((l1, l2) => { 24 | if (l1[sortColumn] > l2[sortColumn]) { 25 | return 1; 26 | } 27 | else if (l1[sortColumn] < l2[sortColumn]) { 28 | return -1; 29 | } 30 | else { 31 | return 0; 32 | } 33 | }); 34 | 35 | if (filter) { 36 | lessons = lessons.filter(lesson => lesson.description.trim().toLowerCase().search(filter.toLowerCase()) >= 0); 37 | } 38 | 39 | if (sortOrder == "desc") { 40 | lessons = lessons.reverse(); 41 | } 42 | 43 | const initialPos = pageNumber * pageSize; 44 | 45 | const lessonsPage = lessons.slice(initialPos, initialPos + pageSize); 46 | 47 | setTimeout(() => { 48 | res.status(200).json({payload: lessonsPage}); 49 | },1000); 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /server/server.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import * as express from 'express'; 4 | import {Application} from "express"; 5 | import {getAllCourses, getCourseById} from "./get-courses.route"; 6 | import {searchLessons} from "./search-lessons.route"; 7 | 8 | 9 | const app: Application = express(); 10 | 11 | const cors = require('cors'); 12 | 13 | app.use(cors({origin: true})); 14 | 15 | app.route('/api/courses').get(getAllCourses); 16 | 17 | app.route('/api/courses/:id').get(getCourseById); 18 | 19 | app.route('/api/lessons').get(searchLessons); 20 | 21 | 22 | 23 | 24 | const httpServer:any = app.listen(9000, () => { 25 | console.log("HTTP REST API Server running at http://localhost:" + httpServer.address().port); 26 | }); 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /server/server.tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "lib": ["es2017"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/_mixins.scss: -------------------------------------------------------------------------------- 1 | 2 | 3 | @mixin drag-drop { 4 | 5 | .cdk-drag-preview { 6 | box-sizing: border-box; 7 | border-radius: 4px; 8 | font-family: Roboto; 9 | box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 10 | 0 8px 10px 1px rgba(0, 0, 0, 0.14), 11 | 0 3px 14px 2px rgba(0, 0, 0, 0.12); 12 | } 13 | 14 | .cdk-drag-animating { 15 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 16 | } 17 | 18 | .lesson:last-child { 19 | border: none; 20 | } 21 | 22 | .drag-drop-list.cdk-drop-list-dragging .drag-drop-item:not(.cdk-drag-placeholder) { 23 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 24 | } 25 | 26 | .drop-placeholder { 27 | background: #ccc; 28 | border: dotted 3px #999; 29 | min-height: 60px; 30 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 31 | } 32 | 33 | } 34 | 35 | @mixin responsive-dialogs { 36 | 37 | @media screen and (max-width: 959px) { 38 | 39 | .modal-panel { 40 | min-width: 0 !important; 41 | max-width: 100% !important; 42 | max-height: 100% !important; 43 | width: 100% !important; 44 | height: 100% !important; 45 | 46 | .mat-dialog-container { 47 | border-radius: 0; 48 | padding: 14px !important; 49 | } 50 | 51 | } 52 | 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/app/about/about.component.css: -------------------------------------------------------------------------------- 1 | 2 | .about-container { 3 | margin: 30px auto; 4 | max-width: 400px; 5 | } 6 | 7 | .course-image { 8 | max-width: 200px; 9 | } 10 | 11 | :host { 12 | font-family: Roboto; 13 | } 14 | -------------------------------------------------------------------------------- /src/app/about/about.component.html: -------------------------------------------------------------------------------- 1 | 2 |
Welcome to the Angular Material In Depth Course
7 | 8 |
22 |
23 | |
33 |
34 |
35 |
36 | |
43 |
44 | # | 49 | 50 |{{lesson.seqNo}} | 51 | 52 |Description | 57 | 58 |{{lesson.description}} | 59 | 60 |Duration | 65 | 66 |{{lesson.duration}} | 67 | 68 |73 | {{lesson.longDescription}} 74 | | 75 | 76 |
---|
{{course.longDescription}}
24 | 25 |