├── .eslintignore ├── .eslintrc.cjs ├── .gitignore ├── .prettierignore ├── README.md ├── backend ├── db-data.ts ├── delete-course.route.ts ├── get-courses.route.ts ├── search.route.ts ├── server.ts └── server.tsconfig.json ├── package-lock.json ├── package.json ├── public ├── favicon.svg ├── manifest.json └── robots.txt ├── src ├── components │ ├── header │ │ ├── header.css │ │ └── header.tsx │ ├── hello-message │ │ ├── hello-message.css │ │ └── hello-message.tsx │ └── router-head │ │ └── router-head.tsx ├── entry.dev.tsx ├── entry.preview.tsx ├── entry.ssr.tsx ├── global.css ├── models │ ├── course.ts │ └── lesson.ts ├── root.tsx └── routes │ ├── head-links.tsx │ ├── index.tsx │ ├── layout.tsx │ ├── resources │ └── index.tsx │ ├── service-worker.ts │ └── stores │ └── index.tsx ├── tsconfig.json └── vite.config.ts /.eslintignore: -------------------------------------------------------------------------------- 1 | **/*.log 2 | **/.DS_Store 3 | *. 4 | .vscode/settings.json 5 | .history 6 | .yarn 7 | bazel-* 8 | bazel-bin 9 | bazel-out 10 | bazel-qwik 11 | bazel-testlogs 12 | dist 13 | dist-dev 14 | lib 15 | lib-types 16 | etc 17 | external 18 | node_modules 19 | temp 20 | tsc-out 21 | tsdoc-metadata.json 22 | target 23 | output 24 | rollup.config.js 25 | build 26 | .cache 27 | .vscode 28 | .rollup.cache 29 | dist 30 | tsconfig.tsbuildinfo 31 | vite.config.ts 32 | *.spec.tsx 33 | *.spec.ts 34 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | es2021: true, 6 | node: true, 7 | }, 8 | extends: [ 9 | 'eslint:recommended', 10 | 'plugin:@typescript-eslint/recommended', 11 | 'plugin:qwik/recommended', 12 | ], 13 | parser: '@typescript-eslint/parser', 14 | parserOptions: { 15 | tsconfigRootDir: __dirname, 16 | project: ['./tsconfig.json'], 17 | ecmaVersion: 2021, 18 | sourceType: 'module', 19 | ecmaFeatures: { 20 | jsx: true, 21 | }, 22 | }, 23 | plugins: ['@typescript-eslint'], 24 | rules: { 25 | '@typescript-eslint/no-explicit-any': 'off', 26 | '@typescript-eslint/explicit-module-boundary-types': 'off', 27 | '@typescript-eslint/no-inferrable-types': 'off', 28 | '@typescript-eslint/no-non-null-assertion': 'off', 29 | '@typescript-eslint/no-empty-interface': 'off', 30 | '@typescript-eslint/no-namespace': 'off', 31 | '@typescript-eslint/no-empty-function': 'off', 32 | '@typescript-eslint/no-this-alias': 'off', 33 | '@typescript-eslint/ban-types': 'off', 34 | '@typescript-eslint/ban-ts-comment': 'off', 35 | 'prefer-spread': 'off', 36 | 'no-case-declarations': 'off', 37 | 'no-console': 'off', 38 | '@typescript-eslint/no-unused-vars': ['error'], 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build 2 | /dist 3 | /lib 4 | /lib-types 5 | /server 6 | 7 | # Development 8 | node_modules 9 | 10 | # Cache 11 | .cache 12 | .mf 13 | .vscode 14 | .rollup.cache 15 | tsconfig.tsbuildinfo 16 | 17 | # Logs 18 | logs 19 | *.log 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | pnpm-debug.log* 24 | lerna-debug.log* 25 | 26 | # Editor 27 | !.vscode/extensions.json 28 | .idea 29 | .DS_Store 30 | *.suo 31 | *.ntvs* 32 | *.njsproj 33 | *.sln 34 | *.sw? 35 | 36 | # Yarn 37 | .yarn/* 38 | !.yarn/releases 39 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Files Prettier should not format 2 | **/*.log 3 | **/.DS_Store 4 | *. 5 | dist 6 | node_modules 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Qwik For Beginners Course 3 | 4 | This repository contains the code of the [Qwik For Beginners Course](https://qwikacademy.io/courses/qwik-course): 5 | 6 | ![Qwik For Beginners Course](https://firebasestorage.googleapis.com/v0/b/onlinecoursehost-prod.appspot.com/o/wxbP3bGlesQVte1BCicdlqLot3B3%2FlYM0Ut6wXB42UdCwA75F%2Fthumbnail%2Fthumb_VmnNgc68mOeArrWZXNNV.jpg?alt=media) 7 | 8 | IMPORTANT: please use Node 18 (Long-term support version) to take this course. 9 | 10 | # Instructions for taking this course 11 | 12 | If you want, you can install the course code and code along as you go through the course. 13 | 14 | Notice that this is optional, the course will still make perfect sense if you choose to just watch it and not code along as you go. 15 | 16 | But coding along is one of the best ways to learn, so I encourage you to try it out. 17 | 18 | This section teaches you how to get your local development environment up and running, so that you can comfortably learn the framework in a stable environment. 19 | 20 | This repository provides you with a playground environment with everything you need, including a backend server that you will use during the course. 21 | 22 | This repository contains a few branches: 23 | 24 | - the 1-start branch is the starting point of the course 25 | 26 | - the main branch contains the finished version of the code. You should refer to this branch in case of doubts with any of the imports, and to compare your code with the finished code. 27 | 28 | # How To install the course code 29 | 30 | You can find the course repository in the following url: 31 | 32 | https://github.com/qwik-courses/qwik-course 33 | 34 | Start by cloning the repository: 35 | 36 | git clone https://github.com/qwik-courses/qwik-course.git 37 | cd qwik-course 38 | 39 | Then switch to the 1-start branch: 40 | 41 | git checkout 1-start 42 | 43 | Install your dependencies: 44 | 45 | npm install 46 | 47 | # To Run the Development Backend Server 48 | 49 | We can start the sample application backend with the following command: 50 | 51 | npm run server 52 | 53 | This is a small Node REST API server. 54 | 55 | # To run the Development UI Server 56 | 57 | To run the frontend part of our code, run the following command: 58 | 59 | npm start 60 | 61 | # Other Courses 62 | 63 | Here are some other courses from the same author ([Vasco Cavalheiro](https://www.linkedin.com/in/vascocavalheiro/)). 64 | 65 | # Angular Forms In Depth 66 | 67 | 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: 68 | 69 | ![Angular Forms In Depth](https://angular-university.s3-us-west-1.amazonaws.com/course-images/angular-forms-course-small.jpg) 70 | 71 | # Angular Router In Depth 72 | 73 | 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: 74 | 75 | ![Angular Router In Depth](https://angular-university.s3-us-west-1.amazonaws.com/course-images/angular-router-course.jpg) 76 | 77 | # NgRx (with NgRx Data) - The Complete Guide 78 | 79 | 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: 80 | 81 | ![Ngrx (with NgRx Data) - The Complete Guide](https://angular-university.s3-us-west-1.amazonaws.com/course-images/ngrx-v2.png) 82 | 83 | 84 | # Angular Core Deep Dive Course 85 | 86 | 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: 87 | 88 | ![Angular Core Deep Dive](https://s3-us-west-1.amazonaws.com/angular-university/course-images/angular-core-in-depth-small.png) 89 | 90 | # RxJs In Practice 91 | 92 | 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: 93 | 94 | ![RxJs In Practice Course](https://s3-us-west-1.amazonaws.com/angular-university/course-images/rxjs-in-practice-course.png) 95 | 96 | # NestJs In Practice (with MongoDB) 97 | 98 | 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: 99 | 100 | ![NestJs In Practice Course](https://angular-university.s3-us-west-1.amazonaws.com/course-images/nestjs-v2.png) 101 | 102 | # Angular Testing Course 103 | 104 | 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: 105 | 106 | ![Angular Testing Course](https://s3-us-west-1.amazonaws.com/angular-university/course-images/angular-testing-small.png) 107 | 108 | # Serverless Angular with Firebase Course 109 | 110 | 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: 111 | 112 | ![Serverless Angular with Firebase Course](https://s3-us-west-1.amazonaws.com/angular-university/course-images/serverless-angular-small.png) 113 | 114 | # Angular Universal Course 115 | 116 | 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: 117 | 118 | ![Angular Universal Course](https://s3-us-west-1.amazonaws.com/angular-university/course-images/angular-universal-small.png) 119 | 120 | # Angular PWA Course 121 | 122 | 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: 123 | 124 | ![Angular PWA Course - Build the future of the Web Today](https://s3-us-west-1.amazonaws.com/angular-university/course-images/angular-pwa-course.png) 125 | 126 | # Angular Security Masterclass 127 | 128 | 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: 129 | 130 | [Angular Security Masterclass](https://github.com/angular-university/angular-security-course). 131 | 132 | ![Angular Security Masterclass](https://s3-us-west-1.amazonaws.com/angular-university/course-images/security-cover-small-v2.png) 133 | 134 | # Angular Advanced Library Laboratory Course 135 | 136 | If you are looking for the Angular Advanced Course, the repo with the full code can be found here: 137 | 138 | [Angular Advanced Library Laboratory Course: Build Your Own Library](https://angular-university.io/course/angular-advanced-course). 139 | 140 | ![Angular Advanced Library Laboratory Course: Build Your Own Library](https://angular-academy.s3.amazonaws.com/thumbnails/advanced_angular-small-v3.png) 141 | 142 | 143 | ## RxJs and Reactive Patterns Angular Architecture Course 144 | 145 | If you are looking for the RxJs and Reactive Patterns Angular Architecture Course code, the repo with the full code can be found here: 146 | 147 | [RxJs and Reactive Patterns Angular Architecture Course](https://angular-university.io/course/reactive-angular-architecture-course) 148 | 149 | ![RxJs and Reactive Patterns Angular Architecture Course](https://s3-us-west-1.amazonaws.com/angular-academy/blog/images/rxjs-reactive-patterns-small.png) 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /backend/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 [courseUrl] 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 | -------------------------------------------------------------------------------- /backend/delete-course.route.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import {Request, Response} from 'express'; 4 | import {COURSES} from "./db-data"; 5 | 6 | 7 | 8 | 9 | export function deleteCourseById(req: Request, res: Response) { 10 | 11 | const courseId = req.params["id"]; 12 | 13 | console.log(`Deleting course with id ${courseId}`); 14 | 15 | delete COURSES[courseId]; 16 | 17 | res.status(200).json({ status: "OK" }); 18 | } 19 | -------------------------------------------------------------------------------- /backend/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 | // throw "Error"; 11 | 12 | setTimeout(() => { 13 | 14 | res.status(200).json(Object.values(COURSES).sort((c1:any, c2:any) => c1.seqNo - c2.seqNo)); 15 | 16 | }, 2000); 17 | 18 | 19 | } 20 | 21 | 22 | export function getCourseById(req: Request, res: Response) { 23 | 24 | const courseId = req.params["id"]; 25 | 26 | const courses:any = Object.values(COURSES); 27 | 28 | const course = courses.find(course => course.id == courseId); 29 | 30 | res.status(200).json(course); 31 | } 32 | -------------------------------------------------------------------------------- /backend/search.route.ts: -------------------------------------------------------------------------------- 1 | 2 | import {Request, Response} from 'express'; 3 | import {LESSONS} from "./db-data"; 4 | import {setTimeout} from "timers"; 5 | 6 | export function searchLessons(req: Request, res: Response) { 7 | 8 | const queryParams = req.query as any; 9 | 10 | const courseId = queryParams.courseId, 11 | filter = queryParams.filter || '', 12 | sortOrder = queryParams.sortOrder, 13 | pageNumber = parseInt(queryParams.pageNumber) || 0, 14 | pageSize = parseInt(queryParams.pageSize), 15 | sortColumn = queryParams.sortColumn ?? "seqNo"; 16 | 17 | let lessons = Object.values(LESSONS) 18 | .filter(lesson => lesson.courseId == courseId) 19 | .sort((l1, l2) => { 20 | if (l1[sortColumn] > l2[sortColumn]) { 21 | return 1; 22 | } 23 | else if (l1[sortColumn] < l2[sortColumn]) { 24 | return -1; 25 | } 26 | else { 27 | return 0; 28 | } 29 | }); 30 | 31 | if (filter) { 32 | lessons = lessons.filter(lesson => lesson.description.trim().toLowerCase().search(filter.toLowerCase()) >= 0); 33 | } 34 | 35 | if (sortOrder == "desc") { 36 | lessons = lessons.reverse(); 37 | } 38 | 39 | const initialPos = pageNumber * pageSize; 40 | 41 | const lessonsPage = lessons.slice(initialPos, initialPos + pageSize); 42 | 43 | setTimeout(() => { 44 | res.status(200).json({payload: lessonsPage}); 45 | },1000); 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /backend/server.ts: -------------------------------------------------------------------------------- 1 | import * as express from 'express'; 2 | import {Application} from "express"; 3 | import {getAllCourses, getCourseById} from "./get-courses.route"; 4 | import {searchLessons} from "./search.route"; 5 | import { deleteCourseById } from "./delete-course.route"; 6 | 7 | 8 | const app: Application = express(); 9 | 10 | const cors = require('cors'); 11 | 12 | app.use(cors({origin: true})); 13 | 14 | app.route('/').get((req, res) => { 15 | res.status(200).send(`

Server is up and running.

`); 16 | }); 17 | 18 | app.route('/api/courses').get(getAllCourses); 19 | 20 | app.route('/api/courses/:id').get(getCourseById); 21 | 22 | app.route('/api/courses/:id').delete(deleteCourseById); 23 | 24 | app.route('/api/lessons').get(searchLessons); 25 | 26 | 27 | 28 | 29 | const httpServer:any = app.listen(9000, () => { 30 | console.log("HTTP REST API Server running at http://localhost:" + httpServer.address().port); 31 | }); 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /backend/server.tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "lib": ["es2017"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "qwik-course", 3 | "description": "App with Routing built-in (recommended)", 4 | "engines": { 5 | "node": ">=15.0.0" 6 | }, 7 | "private": true, 8 | "scripts": { 9 | "server": "ts-node -P ./backend/server.tsconfig.json ./backend/server.ts", 10 | "build": "qwik build", 11 | "build.client": "vite build", 12 | "build.preview": "vite build --ssr src/entry.preview.tsx", 13 | "build.types": "tsc --incremental --noEmit", 14 | "deploy": "echo 'Run \"npm run qwik add\" to install a server adaptor'", 15 | "dev": "vite --mode ssr", 16 | "dev.debug": "node --inspect-brk ./node_modules/vite/bin/vite.js --mode ssr --force", 17 | "fmt": "prettier --write .", 18 | "fmt.check": "prettier --check .", 19 | "lint": "eslint \"src/**/*.ts*\"", 20 | "preview": "qwik build preview && vite preview --open", 21 | "start": "vite --open --mode ssr", 22 | "qwik": "qwik" 23 | }, 24 | "devDependencies": { 25 | "@builder.io/qwik": "0.16.1", 26 | "@builder.io/qwik-city": "0.0.128", 27 | "@types/eslint": "8.4.10", 28 | "@types/node": "latest", 29 | "@typescript-eslint/eslint-plugin": "5.45.0", 30 | "@typescript-eslint/parser": "5.45.0", 31 | "autoprefixer": "10.4.11", 32 | "eslint": "8.28.0", 33 | "eslint-plugin-qwik": "0.15.2", 34 | "node-fetch": "3.3.0", 35 | "prettier": "2.8.0", 36 | "ts-node": "^10.9.1", 37 | "typescript": "4.9.3", 38 | "vite": "3.2.4", 39 | "vite-tsconfig-paths": "3.5.0" 40 | }, 41 | "dependencies": { 42 | "@types/express": "^4.17.15", 43 | "cors": "^2.8.5", 44 | "express": "^4.18.2", 45 | "material-components-web": "^14.0.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /public/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/web-manifest-combined.json", 3 | "name": "qwik-project-name", 4 | "short_name": "Welcome to Qwik", 5 | "start_url": ".", 6 | "display": "standalone", 7 | "background_color": "#fff", 8 | "description": "A Qwik project app." 9 | } 10 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qwik-courses/qwik-course/2c0c7ef823e728d6e691e7d5c1d06472e2e1da70/public/robots.txt -------------------------------------------------------------------------------- /src/components/header/header.css: -------------------------------------------------------------------------------- 1 | header { 2 | display: flex; 3 | } 4 | 5 | header ul { 6 | list-style: none; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /src/components/header/header.tsx: -------------------------------------------------------------------------------- 1 | import { component$, useStylesScoped$ } from '@builder.io/qwik'; 2 | import styles from './header.css?inline'; 3 | import { Link } from "@builder.io/qwik-city"; 4 | 5 | export default component$(() => { 6 | useStylesScoped$(styles); 7 | 8 | return ( 9 |
10 | 13 |
14 | ); 15 | }); 16 | -------------------------------------------------------------------------------- /src/components/hello-message/hello-message.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .hello-message { 4 | font-size: 30px; 5 | font-weight: bold; 6 | color:blue; 7 | } 8 | 9 | .hello-message.highlighted { 10 | color: orange; 11 | } 12 | -------------------------------------------------------------------------------- /src/components/hello-message/hello-message.tsx: -------------------------------------------------------------------------------- 1 | import { component$, PropFunction, useStylesScoped$ } from "@builder.io/qwik"; 2 | 3 | import styles from './hello-message.css?inline'; 4 | 5 | interface HelloMessageProps { 6 | message:string, 7 | courseVersion?:number, 8 | showButton: boolean, 9 | onShowMessage: PropFunction<(message:string) => void>; 10 | } 11 | 12 | export const HelloMessage = component$((props: HelloMessageProps) => { 13 | 14 | useStylesScoped$(styles); 15 | 16 | const {message, courseVersion, onShowMessage, showButton} = props; 17 | 18 | const cssClasses = ['hello-message']; 19 | 20 | if (courseVersion == 1) { 21 | cssClasses.push('highlighted'); 22 | } 23 | 24 | const customStyles = (courseVersion == 2) ? { 25 | color: 'red', 26 | 'text-decoration': 'underline' 27 | } : {}; 28 | 29 | return ( 30 |
31 | { 32 | <> 33 | 34 |
{message}: version {courseVersion}
35 | 36 | {showButton && ( 37 | 38 | )} 39 | 40 | 41 | } 42 |
43 | ); 44 | }); 45 | -------------------------------------------------------------------------------- /src/components/router-head/router-head.tsx: -------------------------------------------------------------------------------- 1 | import { component$ } from '@builder.io/qwik'; 2 | import { useDocumentHead, useLocation } from '@builder.io/qwik-city'; 3 | 4 | /** 5 | * The RouterHead component is placed inside of the document `` element. 6 | */ 7 | export const RouterHead = component$(() => { 8 | const head = useDocumentHead(); 9 | const loc = useLocation(); 10 | 11 | return ( 12 | <> 13 | {head.title} 14 | 15 | 16 | 17 | 18 | 19 | {head.meta.map((m) => ( 20 | 21 | ))} 22 | 23 | {head.links.map((l) => ( 24 | 25 | ))} 26 | 27 | {head.styles.map((s) => ( 28 |