diff --git a/.all-contributorsrc b/.all-contributorsrc index 7a9fd3886..6e8d23087 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -29,6 +29,10 @@ "translation-ru": { "symbol": "🇷🇺", "description": "Translate in Russian" + }, + "translation-ch": { + "symbol": "🇨🇳", + "description": "Translate in Chinese" } }, "contributors": [ @@ -304,6 +308,70 @@ "contributions": [ "bug" ] + }, + { + "login": "EnochGao", + "name": "Enoch Gao", + "avatar_url": "https://avatars.githubusercontent.com/u/41459067?v=4", + "profile": "https://enochgao.github.io/", + "contributions": [ + "doc", + "translation-ch" + ] + }, + { + "login": "fpalmab", + "name": "Francisco Palma", + "avatar_url": "https://avatars.githubusercontent.com/u/7729812?v=4", + "profile": "https://github.com/fpalmab", + "contributions": [ + "bug" + ] + }, + { + "login": "michalgrzegorczyk-dev", + "name": "Michał Grzegorczyk", + "avatar_url": "https://avatars.githubusercontent.com/u/47832176?v=4", + "profile": "https://github.com/michalgrzegorczyk-dev", + "contributions": [ + "doc" + ] + }, + { + "login": "tamim36", + "name": "Tamim Arefin Anik", + "avatar_url": "https://avatars.githubusercontent.com/u/42251521?v=4", + "profile": "https://github.com/tamim36", + "contributions": [ + "bug" + ] + }, + { + "login": "WhoisBsa", + "name": "Matheus B.", + "avatar_url": "https://avatars.githubusercontent.com/u/36895235?v=4", + "profile": "https://github.com/WhoisBsa", + "contributions": [ + "bug" + ] + }, + { + "login": "StefH", + "name": "Stef Heyenrath", + "avatar_url": "https://avatars.githubusercontent.com/u/249938?v=4", + "profile": "https://sourcerer.io/stefh", + "contributions": [ + "doc" + ] + }, + { + "login": "mathisvester", + "name": "mathisvester", + "avatar_url": "https://avatars.githubusercontent.com/u/26164587?v=4", + "profile": "https://github.com/mathisvester", + "contributions": [ + "bug" + ] } ], "contributorsPerLine": 7, diff --git a/.eslintrc.json b/.eslintrc.json index 3bd2a22fb..de9b234b6 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -6,6 +6,7 @@ { "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], "rules": { + "@angular-eslint/no-host-metadata-property": "off", "@nx/enforce-module-boundaries": [ "error", { @@ -18,12 +19,6 @@ } ] } - ], - "@angular-eslint/no-host-metadata-property": [ - "error", - { - "allowStatic": true - } ] } }, diff --git a/.github/copilot-guidelines.md b/.github/copilot-guidelines.md new file mode 100644 index 000000000..e1b11d43a --- /dev/null +++ b/.github/copilot-guidelines.md @@ -0,0 +1,46 @@ +# GitHub Copilot Custom Guidelines + +You are an expert in TypeScript, Angular, and scalable web application development. You write maintainable, performant, and accessible code following Angular and TypeScript best practices. + +## TypeScript Best Practices + +- Use strict type checking +- Prefer type inference when the type is obvious +- Avoid the `any` type; use `unknown` when type is uncertain + +## Angular Best Practices + +- Always use standalone components over NgModules +- Don't use explicit `standalone: true` (it is implied by default) +- Use signals for state management +- Implement lazy loading for feature routes +- Use `NgOptimizedImage` for all static images. + +## Components + +- Keep components small and focused on a single responsibility +- Use `input()` and `output()` functions instead of decorators +- Use `computed()` for derived state +- Set `changeDetection: ChangeDetectionStrategy.OnPush` in `@Component` decorator +- Prefer inline templates for small components +- Prefer Reactive forms instead of Template-driven ones +- Do NOT use `ngClass`, use `class` bindings instead +- DO NOT use `ngStyle`, use `style` bindings instead + +## State Management + +- Use signals for local component state +- Use `computed()` for derived state +- Keep state transformations pure and predictable + +## Templates + +- Keep templates simple and avoid complex logic +- Use native control flow (`@if`, `@for`, `@switch`) instead of `*ngIf`, `*ngFor`, `*ngSwitch` +- Use the async pipe to handle observables + +## Services + +- Design services around a single responsibility +- Use the `providedIn: 'root'` option for singleton services +- Use the `inject()` function instead of constructor injection diff --git a/.github/github-action/contributors.js b/.github/github-action/contributors.js index 50d4c3917..9555e6633 100644 --- a/.github/github-action/contributors.js +++ b/.github/github-action/contributors.js @@ -9,9 +9,24 @@ const contributors = [ 'kabrunko-dev', 'Sanjar1304', 'tsironis13', + 'EnochGao', ]; -const sponsors = ['ddotx', 'LMFinney', 'alannelucq', 'SidV2']; +const sponsors = [ + 'ddotx', + 'LMFinney', + 'alannelucq', + 'SidV2', + 'fpalmab', + 'CivilEngeneer', + 'apalaio', + 'amosISA', + 'michalgrzegorczyk-dev', + 'zealotrahl', + 'DzoeL123', + 'allan1989', + 'pchessah', +]; module.exports = { contributors, diff --git a/.github/workflows/close-inactive-pr.yml b/.github/workflows/close-inactive-pr.yml index 66d984d5f..d5e0e9c95 100644 --- a/.github/workflows/close-inactive-pr.yml +++ b/.github/workflows/close-inactive-pr.yml @@ -17,8 +17,8 @@ jobs: stale-issue-label: 'stale' stale-issue-message: 'This issue is stale because it has been open for 15 days with no activity.' exempt-issue-labels: 'long-term' - days-before-pr-stale: 10 - days-before-pr-close: 2 + days-before-pr-stale: 20 + days-before-pr-close: 7 stale-pr-label: 'stale' stale-pr-message: 'This pull request is stale because it has been open for 15 days with no activity.' close-pr-message: 'This pull request was closed because it has been inactive for 5 days since being marked as stale.' diff --git a/.github/workflows/label-issue.yml b/.github/workflows/label-issue.yml index 6918818fd..a5fd40632 100644 --- a/.github/workflows/label-issue.yml +++ b/.github/workflows/label-issue.yml @@ -2,9 +2,25 @@ name: Add Labels on: pull_request_target: - types: [ opened ] + types: [ opened, edited, synchronize ] jobs: + check-title: + runs-on: ubuntu-latest + steps: + - name: Check PR title + env: + PR_TITLE: ${{ github.event.pull_request.title }} + run: | + echo "Checking PR Title: '$PR_TITLE'" + if [[ ! "$PR_TITLE" =~ ^Answer: ]]; then + echo "❌ PR title should start with 'Answer:[#challenge number]'" + echo "### ❌ PR title should start with 'Answer:[#challenge number]'" >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo "✅ PR title format is correct." + echo "### ✅ PR title format is correct." >> $GITHUB_STEP_SUMMARY + fi add_labels: runs-on: ubuntu-latest if: ${{ startsWith(github.event.pull_request.title, 'Answer') }} diff --git a/.gitignore b/.gitignore index 5472d6bdf..0647ff22f 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,8 @@ Thumbs.db TODO.md .nx/cache +.nx/workspace-data + +.cursorrules +.cursor/rules/nx-rules.mdc +.github/instructions/nx.instructions.md diff --git a/.prettierignore b/.prettierignore index ab639d33d..a4684ea74 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,4 +5,5 @@ .angular -/.nx/cache \ No newline at end of file +/.nx/cache +/.nx/workspace-data \ No newline at end of file diff --git a/README.md b/README.md index 9b4cd8e6d..6ae4aa91b 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ If you would like to propose a challenge, this project is open source, so feel f ## Challenges -Check [all 55 challenges](https://angular-challenges.vercel.app/) +Check [all 60 challenges](https://angular-challenges.vercel.app/) ## Contributors ✨ @@ -69,6 +69,15 @@ Check [all 55 challenges](https://angular-challenges.vercel.app/) fixed_michal
fixed_michal

🐛 Tenessy
Tenessy

🐛 + + Enoch Gao
Enoch Gao

📖 🇨🇳 + Francisco Palma
Francisco Palma

🐛 + Michał Grzegorczyk
Michał Grzegorczyk

📖 + Tamim Arefin Anik
Tamim Arefin Anik

🐛 + Matheus B.
Matheus B.

🐛 + Stef Heyenrath
Stef Heyenrath

📖 + mathisvester
mathisvester

🐛 + diff --git a/apps/angular/1-projection/project.json b/apps/angular/1-projection/project.json index df91001f3..42ed1604f 100644 --- a/apps/angular/1-projection/project.json +++ b/apps/angular/1-projection/project.json @@ -7,12 +7,12 @@ "tags": [], "targets": { "build": { - "executor": "@angular-devkit/build-angular:application", + "executor": "@angular-devkit/build-angular:browser", "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/apps/angular/1-projection", "index": "apps/angular/1-projection/src/index.html", - "browser": "apps/angular/1-projection/src/main.ts", + "main": "apps/angular/1-projection/src/main.ts", "polyfills": ["apps/angular/1-projection/src/polyfills.ts"], "tsConfig": "apps/angular/1-projection/tsconfig.app.json", "inlineStyleLanguage": "scss", @@ -41,7 +41,9 @@ "outputHashing": "all" }, "development": { + "buildOptimizer": false, "optimization": false, + "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true @@ -59,7 +61,8 @@ "buildTarget": "angular-projection:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", @@ -68,10 +71,14 @@ } }, "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], "options": { - "jestConfig": "apps/angular/1-projection/jest.config.ts" + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } } } } diff --git a/apps/angular/1-projection/src/app/app.component.ts b/apps/angular/1-projection/src/app/app.component.ts index b1d076a9f..df654bbc2 100644 --- a/apps/angular/1-projection/src/app/app.component.ts +++ b/apps/angular/1-projection/src/app/app.component.ts @@ -7,12 +7,11 @@ import { TeacherCardComponent } from './component/teacher-card/teacher-card.comp selector: 'app-root', template: `
- - - + + +
`, - standalone: true, imports: [TeacherCardComponent, StudentCardComponent, CityCardComponent], }) export class AppComponent {} diff --git a/apps/angular/1-projection/src/app/component/city-card/city-card.component.ts b/apps/angular/1-projection/src/app/component/city-card/city-card.component.ts index 30c8f88ec..8895c8c84 100644 --- a/apps/angular/1-projection/src/app/component/city-card/city-card.component.ts +++ b/apps/angular/1-projection/src/app/component/city-card/city-card.component.ts @@ -1,13 +1,9 @@ -import { Component, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; @Component({ selector: 'app-city-card', template: 'TODO City', - standalone: true, imports: [], + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class CityCardComponent implements OnInit { - constructor() {} - - ngOnInit(): void {} -} +export class CityCardComponent {} diff --git a/apps/angular/1-projection/src/app/component/student-card/student-card.component.ts b/apps/angular/1-projection/src/app/component/student-card/student-card.component.ts index 441cda189..bdfa4abd4 100644 --- a/apps/angular/1-projection/src/app/component/student-card/student-card.component.ts +++ b/apps/angular/1-projection/src/app/component/student-card/student-card.component.ts @@ -1,19 +1,22 @@ -import { Component, OnInit } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + inject, + OnInit, +} from '@angular/core'; import { FakeHttpService } from '../../data-access/fake-http.service'; import { StudentStore } from '../../data-access/student.store'; import { CardType } from '../../model/card.model'; -import { Student } from '../../model/student.model'; import { CardComponent } from '../../ui/card/card.component'; @Component({ selector: 'app-student-card', template: ` + customClass="bg-light-green" /> `, - standalone: true, styles: [ ` ::ng-deep .bg-light-green { @@ -22,19 +25,16 @@ import { CardComponent } from '../../ui/card/card.component'; `, ], imports: [CardComponent], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class StudentCardComponent implements OnInit { - students: Student[] = []; - cardType = CardType.STUDENT; + private http = inject(FakeHttpService); + private store = inject(StudentStore); - constructor( - private http: FakeHttpService, - private store: StudentStore, - ) {} + students = this.store.students; + cardType = CardType.STUDENT; ngOnInit(): void { this.http.fetchStudents$.subscribe((s) => this.store.addAll(s)); - - this.store.students$.subscribe((s) => (this.students = s)); } } diff --git a/apps/angular/1-projection/src/app/component/teacher-card/teacher-card.component.ts b/apps/angular/1-projection/src/app/component/teacher-card/teacher-card.component.ts index 995cb7c2f..adf0ad3c1 100644 --- a/apps/angular/1-projection/src/app/component/teacher-card/teacher-card.component.ts +++ b/apps/angular/1-projection/src/app/component/teacher-card/teacher-card.component.ts @@ -1,15 +1,14 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, inject, OnInit } from '@angular/core'; import { FakeHttpService } from '../../data-access/fake-http.service'; import { TeacherStore } from '../../data-access/teacher.store'; import { CardType } from '../../model/card.model'; -import { Teacher } from '../../model/teacher.model'; import { CardComponent } from '../../ui/card/card.component'; @Component({ selector: 'app-teacher-card', template: ` `, @@ -20,21 +19,16 @@ import { CardComponent } from '../../ui/card/card.component'; } `, ], - standalone: true, imports: [CardComponent], }) export class TeacherCardComponent implements OnInit { - teachers: Teacher[] = []; - cardType = CardType.TEACHER; + private http = inject(FakeHttpService); + private store = inject(TeacherStore); - constructor( - private http: FakeHttpService, - private store: TeacherStore, - ) {} + teachers = this.store.teachers; + cardType = CardType.TEACHER; ngOnInit(): void { this.http.fetchTeachers$.subscribe((t) => this.store.addAll(t)); - - this.store.teachers$.subscribe((t) => (this.teachers = t)); } } diff --git a/apps/angular/1-projection/src/app/data-access/city.store.ts b/apps/angular/1-projection/src/app/data-access/city.store.ts index 711dad1d7..a8b523569 100644 --- a/apps/angular/1-projection/src/app/data-access/city.store.ts +++ b/apps/angular/1-projection/src/app/data-access/city.store.ts @@ -1,23 +1,21 @@ -import { Injectable } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; +import { Injectable, signal } from '@angular/core'; import { City } from '../model/city.model'; @Injectable({ providedIn: 'root', }) export class CityStore { - private cities = new BehaviorSubject([]); - cities$ = this.cities.asObservable(); + private cities = signal([]); addAll(cities: City[]) { - this.cities.next(cities); + this.cities.set(cities); } - addOne(student: City) { - this.cities.next([...this.cities.value, student]); + addOne(city: City) { + this.cities.set([...this.cities(), city]); } deleteOne(id: number) { - this.cities.next(this.cities.value.filter((s) => s.id !== id)); + this.cities.set(this.cities().filter((s) => s.id !== id)); } } diff --git a/apps/angular/1-projection/src/app/data-access/student.store.ts b/apps/angular/1-projection/src/app/data-access/student.store.ts index 7918118c3..6e7f57022 100644 --- a/apps/angular/1-projection/src/app/data-access/student.store.ts +++ b/apps/angular/1-projection/src/app/data-access/student.store.ts @@ -1,23 +1,21 @@ -import { Injectable } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; +import { Injectable, signal } from '@angular/core'; import { Student } from '../model/student.model'; @Injectable({ providedIn: 'root', }) export class StudentStore { - private students = new BehaviorSubject([]); - students$ = this.students.asObservable(); + public students = signal([]); addAll(students: Student[]) { - this.students.next(students); + this.students.set(students); } addOne(student: Student) { - this.students.next([...this.students.value, student]); + this.students.set([...this.students(), student]); } deleteOne(id: number) { - this.students.next(this.students.value.filter((s) => s.id !== id)); + this.students.set(this.students().filter((s) => s.id !== id)); } } diff --git a/apps/angular/1-projection/src/app/data-access/teacher.store.ts b/apps/angular/1-projection/src/app/data-access/teacher.store.ts index 93f68c4b1..5f6dae989 100644 --- a/apps/angular/1-projection/src/app/data-access/teacher.store.ts +++ b/apps/angular/1-projection/src/app/data-access/teacher.store.ts @@ -1,23 +1,21 @@ -import { Injectable } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; +import { Injectable, signal } from '@angular/core'; import { Teacher } from '../model/teacher.model'; @Injectable({ providedIn: 'root', }) export class TeacherStore { - private teachers = new BehaviorSubject([]); - teachers$ = this.teachers.asObservable(); + public teachers = signal([]); addAll(teachers: Teacher[]) { - this.teachers.next(teachers); + this.teachers.set(teachers); } addOne(teacher: Teacher) { - this.teachers.next([...this.teachers.value, teacher]); + this.teachers.set([...this.teachers(), teacher]); } deleteOne(id: number) { - this.teachers.next(this.teachers.value.filter((t) => t.id !== id)); + this.teachers.set(this.teachers().filter((t) => t.id !== id)); } } diff --git a/apps/angular/1-projection/src/app/ui/card/card.component.ts b/apps/angular/1-projection/src/app/ui/card/card.component.ts index f06c9ae00..1a6c3648c 100644 --- a/apps/angular/1-projection/src/app/ui/card/card.component.ts +++ b/apps/angular/1-projection/src/app/ui/card/card.component.ts @@ -1,5 +1,5 @@ -import { NgFor, NgIf } from '@angular/common'; -import { Component, Input } from '@angular/core'; +import { NgOptimizedImage } from '@angular/common'; +import { Component, inject, input } from '@angular/core'; import { randStudent, randTeacher } from '../../data-access/fake-http.service'; import { StudentStore } from '../../data-access/student.store'; import { TeacherStore } from '../../data-access/teacher.store'; @@ -11,22 +11,21 @@ import { ListItemComponent } from '../list-item/list-item.component'; template: `
- - + [class]="customClass()"> + @if (type() === CardType.TEACHER) { + + } + @if (type() === CardType.STUDENT) { + + }
- + @for (item of list(); track item) { + + }
`, - standalone: true, - imports: [NgIf, NgFor, ListItemComponent], + imports: [ListItemComponent, NgOptimizedImage], }) export class CardComponent { - @Input() list: any[] | null = null; - @Input() type!: CardType; - @Input() customClass = ''; + private teacherStore = inject(TeacherStore); + private studentStore = inject(StudentStore); - CardType = CardType; + readonly list = input(null); + readonly type = input.required(); + readonly customClass = input(''); - constructor( - private teacherStore: TeacherStore, - private studentStore: StudentStore, - ) {} + CardType = CardType; addNewItem() { - if (this.type === CardType.TEACHER) { + const type = this.type(); + if (type === CardType.TEACHER) { this.teacherStore.addOne(randTeacher()); - } else if (this.type === CardType.STUDENT) { + } else if (type === CardType.STUDENT) { this.studentStore.addOne(randStudent()); } } diff --git a/apps/angular/1-projection/src/app/ui/list-item/list-item.component.ts b/apps/angular/1-projection/src/app/ui/list-item/list-item.component.ts index c0f9cff7f..5d504f372 100644 --- a/apps/angular/1-projection/src/app/ui/list-item/list-item.component.ts +++ b/apps/angular/1-projection/src/app/ui/list-item/list-item.component.ts @@ -1,4 +1,9 @@ -import { Component, Input } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + inject, + input, +} from '@angular/core'; import { StudentStore } from '../../data-access/student.store'; import { TeacherStore } from '../../data-access/teacher.store'; import { CardType } from '../../model/card.model'; @@ -7,28 +12,27 @@ import { CardType } from '../../model/card.model'; selector: 'app-list-item', template: `
- {{ name }} -
`, - standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, }) export class ListItemComponent { - @Input() id!: number; - @Input() name!: string; - @Input() type!: CardType; + private teacherStore = inject(TeacherStore); + private studentStore = inject(StudentStore); - constructor( - private teacherStore: TeacherStore, - private studentStore: StudentStore, - ) {} + readonly id = input.required(); + readonly name = input.required(); + readonly type = input.required(); delete(id: number) { - if (this.type === CardType.TEACHER) { + const type = this.type(); + if (type === CardType.TEACHER) { this.teacherStore.deleteOne(id); - } else if (this.type === CardType.STUDENT) { + } else if (type === CardType.STUDENT) { this.studentStore.deleteOne(id); } } diff --git a/apps/angular/1-projection/src/main.ts b/apps/angular/1-projection/src/main.ts index 9cd15da95..31c5da482 100644 --- a/apps/angular/1-projection/src/main.ts +++ b/apps/angular/1-projection/src/main.ts @@ -1,4 +1,4 @@ import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from './app/app.component'; -bootstrapApplication(AppComponent); +bootstrapApplication(AppComponent).catch((err) => console.error(err)); diff --git a/apps/angular/1-projection/tsconfig.app.json b/apps/angular/1-projection/tsconfig.app.json index 7a4dbc47e..2a1ca1b8d 100644 --- a/apps/angular/1-projection/tsconfig.app.json +++ b/apps/angular/1-projection/tsconfig.app.json @@ -4,7 +4,8 @@ "outDir": "../../../dist/out-tsc", "types": [], "target": "ES2022", - "useDefineForClassFields": false + "useDefineForClassFields": false, + "moduleResolution": "bundler" }, "files": ["src/main.ts", "src/polyfills.ts"], "include": ["src/**/*.d.ts"], diff --git a/apps/angular/10-utility-wrapper-pipe/project.json b/apps/angular/10-utility-wrapper-pipe/project.json index 233fc183d..37a204043 100644 --- a/apps/angular/10-utility-wrapper-pipe/project.json +++ b/apps/angular/10-utility-wrapper-pipe/project.json @@ -60,7 +60,8 @@ "buildTarget": "angular-utility-wrapper-pipe:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", diff --git a/apps/angular/10-utility-wrapper-pipe/src/app/app.component.ts b/apps/angular/10-utility-wrapper-pipe/src/app/app.component.ts index d91fc7436..9a8156a5f 100644 --- a/apps/angular/10-utility-wrapper-pipe/src/app/app.component.ts +++ b/apps/angular/10-utility-wrapper-pipe/src/app/app.component.ts @@ -1,20 +1,21 @@ -import { NgFor } from '@angular/common'; import { Component } from '@angular/core'; import { PersonUtils } from './person.utils'; @Component({ - standalone: true, - imports: [NgFor], selector: 'app-root', template: ` -
+ @for (activity of activities; track activity.name) { {{ activity.name }} : -
+ @for ( + person of persons; + track person.name; + let index = $index; + let isFirst = $first + ) { {{ showName(person.name, index) }} {{ isAllowed(person.age, isFirst, activity.minimumAge) }} -
-
+ } + } `, }) export class AppComponent { diff --git a/apps/angular/10-utility-wrapper-pipe/tsconfig.app.json b/apps/angular/10-utility-wrapper-pipe/tsconfig.app.json index 7a4dbc47e..2a1ca1b8d 100644 --- a/apps/angular/10-utility-wrapper-pipe/tsconfig.app.json +++ b/apps/angular/10-utility-wrapper-pipe/tsconfig.app.json @@ -4,7 +4,8 @@ "outDir": "../../../dist/out-tsc", "types": [], "target": "ES2022", - "useDefineForClassFields": false + "useDefineForClassFields": false, + "moduleResolution": "bundler" }, "files": ["src/main.ts", "src/polyfills.ts"], "include": ["src/**/*.d.ts"], diff --git a/apps/angular/13-highly-customizable-css/project.json b/apps/angular/13-highly-customizable-css/project.json index 8bdda5dba..c20d3bb48 100644 --- a/apps/angular/13-highly-customizable-css/project.json +++ b/apps/angular/13-highly-customizable-css/project.json @@ -60,7 +60,8 @@ "buildTarget": "angular-highly-customizable-css:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", diff --git a/apps/angular/13-highly-customizable-css/src/app/page.component.ts b/apps/angular/13-highly-customizable-css/src/app/page.component.ts index 067453294..029ca52d2 100644 --- a/apps/angular/13-highly-customizable-css/src/app/page.component.ts +++ b/apps/angular/13-highly-customizable-css/src/app/page.component.ts @@ -5,7 +5,6 @@ import { TextComponent } from './text.component'; @Component({ selector: 'page', - standalone: true, imports: [TextStaticComponent, TextComponent], template: ` diff --git a/apps/angular/13-highly-customizable-css/src/app/static-text.component.ts b/apps/angular/13-highly-customizable-css/src/app/static-text.component.ts index cdfd1c19f..703e2a538 100644 --- a/apps/angular/13-highly-customizable-css/src/app/static-text.component.ts +++ b/apps/angular/13-highly-customizable-css/src/app/static-text.component.ts @@ -1,33 +1,38 @@ /* eslint-disable @angular-eslint/component-selector */ -import { Component, Input } from '@angular/core'; +import { Component, computed, input } from '@angular/core'; import { TextComponent } from './text.component'; export type StaticTextType = 'normal' | 'warning' | 'error'; @Component({ selector: 'static-text', - standalone: true, imports: [TextComponent], template: ` - This is a static text + This is a static text `, }) export class TextStaticComponent { - @Input() set type(type: StaticTextType) { - switch (type) { - case 'error': { - this.font = 30; - this.color = 'red'; - break; - } - case 'warning': { - this.font = 25; - this.color = 'orange'; - break; - } + type = input('normal'); + + font = computed(() => { + switch (this.type()) { + case 'error': + return 30; + case 'warning': + return 25; + default: + return 10; } - } + }); - font = 10; - color = 'black'; + color = computed(() => { + switch (this.type()) { + case 'error': + return 'red'; + case 'warning': + return 'orange'; + default: + return 'black'; + } + }); } diff --git a/apps/angular/13-highly-customizable-css/src/app/text.component.ts b/apps/angular/13-highly-customizable-css/src/app/text.component.ts index 452e76a8e..07e3e6255 100644 --- a/apps/angular/13-highly-customizable-css/src/app/text.component.ts +++ b/apps/angular/13-highly-customizable-css/src/app/text.component.ts @@ -1,16 +1,15 @@ /* eslint-disable @angular-eslint/component-selector */ -import { Component, Input } from '@angular/core'; +import { Component, input } from '@angular/core'; @Component({ selector: 'text', - standalone: true, template: `

- +

`, }) export class TextComponent { - @Input() font = 10; - @Input() color = 'black'; + font = input(10); + color = input('black'); } diff --git a/apps/angular/13-highly-customizable-css/tsconfig.app.json b/apps/angular/13-highly-customizable-css/tsconfig.app.json index 58220429a..8b5631268 100644 --- a/apps/angular/13-highly-customizable-css/tsconfig.app.json +++ b/apps/angular/13-highly-customizable-css/tsconfig.app.json @@ -2,7 +2,8 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../dist/out-tsc", - "types": [] + "types": [], + "moduleResolution": "bundler" }, "files": ["src/main.ts"], "include": ["src/**/*.d.ts"], diff --git a/apps/angular/16-master-dependency-injection/project.json b/apps/angular/16-master-dependency-injection/project.json index 280ed3df4..4eb6bd95e 100644 --- a/apps/angular/16-master-dependency-injection/project.json +++ b/apps/angular/16-master-dependency-injection/project.json @@ -62,7 +62,8 @@ "buildTarget": "angular-master-dependency-injection:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", diff --git a/apps/angular/16-master-dependency-injection/src/app/app.component.ts b/apps/angular/16-master-dependency-injection/src/app/app.component.ts index f80851729..332ec9877 100644 --- a/apps/angular/16-master-dependency-injection/src/app/app.component.ts +++ b/apps/angular/16-master-dependency-injection/src/app/app.component.ts @@ -1,6 +1,6 @@ import { TableComponent } from '@angular-challenges/shared/ui'; -import { AsyncPipe, NgFor } from '@angular/common'; -import { Component, Directive } from '@angular/core'; +import { AsyncPipe } from '@angular/common'; +import { ChangeDetectionStrategy, Component, Directive } from '@angular/core'; import { CurrencyPipe } from './currency.pipe'; import { CurrencyService } from './currency.service'; import { Product, products } from './product.model'; @@ -11,7 +11,6 @@ interface ProductContext { @Directive({ selector: 'ng-template[product]', - standalone: true, }) export class ProductDirective { static ngTemplateContextGuard( @@ -23,17 +22,18 @@ export class ProductDirective { } @Component({ - standalone: true, - imports: [TableComponent, CurrencyPipe, AsyncPipe, NgFor, ProductDirective], + imports: [TableComponent, CurrencyPipe, AsyncPipe, ProductDirective], providers: [CurrencyService], selector: 'app-root', template: ` - + @for (col of displayedColumns; track $index) { + + } @@ -46,6 +46,7 @@ export class ProductDirective {
- {{ col }} - + {{ col }} +
`, + changeDetection: ChangeDetectionStrategy.OnPush, }) export class AppComponent { products = products; diff --git a/apps/angular/16-master-dependency-injection/src/app/currency.pipe.ts b/apps/angular/16-master-dependency-injection/src/app/currency.pipe.ts index 9d90e52f1..efa408eb8 100644 --- a/apps/angular/16-master-dependency-injection/src/app/currency.pipe.ts +++ b/apps/angular/16-master-dependency-injection/src/app/currency.pipe.ts @@ -4,7 +4,6 @@ import { CurrencyService } from './currency.service'; @Pipe({ name: 'currency', - standalone: true, }) export class CurrencyPipe implements PipeTransform { currencyService = inject(CurrencyService); diff --git a/apps/angular/16-master-dependency-injection/tsconfig.app.json b/apps/angular/16-master-dependency-injection/tsconfig.app.json index 58220429a..8b5631268 100644 --- a/apps/angular/16-master-dependency-injection/tsconfig.app.json +++ b/apps/angular/16-master-dependency-injection/tsconfig.app.json @@ -2,7 +2,8 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../dist/out-tsc", - "types": [] + "types": [], + "moduleResolution": "bundler" }, "files": ["src/main.ts"], "include": ["src/**/*.d.ts"], diff --git a/apps/angular/21-anchor-navigation/project.json b/apps/angular/21-anchor-navigation/project.json index dd4c732c1..782bb1ec4 100644 --- a/apps/angular/21-anchor-navigation/project.json +++ b/apps/angular/21-anchor-navigation/project.json @@ -60,7 +60,8 @@ "buildTarget": "angular-anchor-navigation:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", @@ -69,10 +70,14 @@ } }, "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], "options": { - "jestConfig": "apps/angular/21-anchor-navigation/jest.config.ts" + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } } } } diff --git a/apps/angular/21-anchor-navigation/src/app/app.component.ts b/apps/angular/21-anchor-navigation/src/app/app.component.ts index 3fb7c5df0..5caca0271 100644 --- a/apps/angular/21-anchor-navigation/src/app/app.component.ts +++ b/apps/angular/21-anchor-navigation/src/app/app.component.ts @@ -2,7 +2,6 @@ import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; @Component({ - standalone: true, imports: [RouterOutlet], selector: 'app-root', template: ` diff --git a/apps/angular/21-anchor-navigation/src/app/foo.component.ts b/apps/angular/21-anchor-navigation/src/app/foo.component.ts index 87f9b59d9..6744c3662 100644 --- a/apps/angular/21-anchor-navigation/src/app/foo.component.ts +++ b/apps/angular/21-anchor-navigation/src/app/foo.component.ts @@ -2,7 +2,6 @@ import { Component } from '@angular/core'; import { NavButtonComponent } from './nav-button.component'; @Component({ - standalone: true, imports: [NavButtonComponent], selector: 'app-foo', template: ` diff --git a/apps/angular/21-anchor-navigation/src/app/home.component.ts b/apps/angular/21-anchor-navigation/src/app/home.component.ts index 0f24ff6e7..6ef9bc2b6 100644 --- a/apps/angular/21-anchor-navigation/src/app/home.component.ts +++ b/apps/angular/21-anchor-navigation/src/app/home.component.ts @@ -2,7 +2,6 @@ import { Component } from '@angular/core'; import { NavButtonComponent } from './nav-button.component'; @Component({ - standalone: true, imports: [NavButtonComponent], selector: 'app-home', template: ` diff --git a/apps/angular/21-anchor-navigation/src/app/nav-button.component.ts b/apps/angular/21-anchor-navigation/src/app/nav-button.component.ts index 9e3b6d42f..7a22c7f38 100644 --- a/apps/angular/21-anchor-navigation/src/app/nav-button.component.ts +++ b/apps/angular/21-anchor-navigation/src/app/nav-button.component.ts @@ -1,11 +1,11 @@ /* eslint-disable @angular-eslint/component-selector */ -import { Component, Input } from '@angular/core'; +import { Component, input } from '@angular/core'; + @Component({ selector: 'nav-button', - standalone: true, template: ` - - + + `, host: { @@ -13,5 +13,5 @@ import { Component, Input } from '@angular/core'; }, }) export class NavButtonComponent { - @Input() href = ''; + href = input(''); } diff --git a/apps/angular/21-anchor-navigation/tsconfig.app.json b/apps/angular/21-anchor-navigation/tsconfig.app.json index 58220429a..8b5631268 100644 --- a/apps/angular/21-anchor-navigation/tsconfig.app.json +++ b/apps/angular/21-anchor-navigation/tsconfig.app.json @@ -2,7 +2,8 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../dist/out-tsc", - "types": [] + "types": [], + "moduleResolution": "bundler" }, "files": ["src/main.ts"], "include": ["src/**/*.d.ts"], diff --git a/apps/angular/22-router-input/project.json b/apps/angular/22-router-input/project.json index 58cd889c5..d0cd43a08 100644 --- a/apps/angular/22-router-input/project.json +++ b/apps/angular/22-router-input/project.json @@ -59,7 +59,8 @@ "buildTarget": "angular-router-input:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", diff --git a/apps/angular/22-router-input/src/app/app.component.ts b/apps/angular/22-router-input/src/app/app.component.ts index 1ef7e32aa..9dfc11200 100644 --- a/apps/angular/22-router-input/src/app/app.component.ts +++ b/apps/angular/22-router-input/src/app/app.component.ts @@ -3,7 +3,6 @@ import { FormControl, ReactiveFormsModule } from '@angular/forms'; import { RouterLink, RouterModule } from '@angular/router'; @Component({ - standalone: true, imports: [RouterLink, RouterModule, ReactiveFormsModule], selector: 'app-root', template: ` diff --git a/apps/angular/22-router-input/src/app/home.component.ts b/apps/angular/22-router-input/src/app/home.component.ts index 2ef8c5eb4..0ddc1501d 100644 --- a/apps/angular/22-router-input/src/app/home.component.ts +++ b/apps/angular/22-router-input/src/app/home.component.ts @@ -1,7 +1,6 @@ import { Component } from '@angular/core'; @Component({ selector: 'app-home', - standalone: true, imports: [], template: `
Home
diff --git a/apps/angular/22-router-input/src/app/test.component.ts b/apps/angular/22-router-input/src/app/test.component.ts index 88c1465f3..747ab4483 100644 --- a/apps/angular/22-router-input/src/app/test.component.ts +++ b/apps/angular/22-router-input/src/app/test.component.ts @@ -5,7 +5,6 @@ import { map } from 'rxjs'; @Component({ selector: 'app-subscription', - standalone: true, imports: [AsyncPipe], template: `
TestId: {{ testId$ | async }}
diff --git a/apps/angular/22-router-input/tsconfig.app.json b/apps/angular/22-router-input/tsconfig.app.json index 58220429a..8b5631268 100644 --- a/apps/angular/22-router-input/tsconfig.app.json +++ b/apps/angular/22-router-input/tsconfig.app.json @@ -2,7 +2,8 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../dist/out-tsc", - "types": [] + "types": [], + "moduleResolution": "bundler" }, "files": ["src/main.ts"], "include": ["src/**/*.d.ts"], diff --git a/apps/angular/3-directive-enhancement/project.json b/apps/angular/3-directive-enhancement/project.json deleted file mode 100644 index f16c4d7a1..000000000 --- a/apps/angular/3-directive-enhancement/project.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "name": "angular-directive-enhancement", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/angular/3-directive-enhancement/src", - "prefix": "app", - "tags": [], - "targets": { - "build": { - "executor": "@angular-devkit/build-angular:browser", - "outputs": ["{options.outputPath}"], - "options": { - "outputPath": "dist/apps/angular/3-directive-enhancement", - "index": "apps/angular/3-directive-enhancement/src/index.html", - "main": "apps/angular/3-directive-enhancement/src/main.ts", - "polyfills": "apps/angular/3-directive-enhancement/src/polyfills.ts", - "tsConfig": "apps/angular/3-directive-enhancement/tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - "apps/angular/3-directive-enhancement/src/favicon.ico", - "apps/angular/3-directive-enhancement/src/assets" - ], - "styles": ["apps/angular/3-directive-enhancement/src/styles.scss"], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ], - "outputHashing": "all" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "executor": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "angular-directive-enhancement:build:production" - }, - "development": { - "buildTarget": "angular-directive-enhancement:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "executor": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "angular-directive-enhancement:build" - } - } - } -} diff --git a/apps/angular/3-directive-enhancement/src/app/app.component.ts b/apps/angular/3-directive-enhancement/src/app/app.component.ts deleted file mode 100644 index cd1d5f23c..000000000 --- a/apps/angular/3-directive-enhancement/src/app/app.component.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { NgFor, NgIf } from '@angular/common'; -import { ChangeDetectionStrategy, Component } from '@angular/core'; - -interface Person { - name: string; -} - -@Component({ - standalone: true, - imports: [NgFor, NgIf], - selector: 'app-root', - template: ` - -
- {{ person.name }} -
-
- The list is empty !! - `, - styles: [], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class AppComponent { - persons: Person[] = []; -} diff --git a/apps/angular/3-directive-enhancement/src/assets/.gitkeep b/apps/angular/3-directive-enhancement/src/assets/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/apps/angular/3-directive-enhancement/src/main.ts b/apps/angular/3-directive-enhancement/src/main.ts deleted file mode 100644 index 31c5da482..000000000 --- a/apps/angular/3-directive-enhancement/src/main.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { bootstrapApplication } from '@angular/platform-browser'; -import { AppComponent } from './app/app.component'; - -bootstrapApplication(AppComponent).catch((err) => console.error(err)); diff --git a/apps/angular/3-directive-enhancement/src/polyfills.ts b/apps/angular/3-directive-enhancement/src/polyfills.ts deleted file mode 100644 index e4555ed11..000000000 --- a/apps/angular/3-directive-enhancement/src/polyfills.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * This file includes polyfills needed by Angular and is loaded before the app. - * You can add your own extra polyfills to this file. - * - * This file is divided into 2 sections: - * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. - * 2. Application imports. Files imported after ZoneJS that should be loaded before your main - * file. - * - * The current setup is for so-called "evergreen" browsers; the last versions of browsers that - * automatically update themselves. This includes recent versions of Safari, Chrome (including - * Opera), Edge on the desktop, and iOS and Chrome on mobile. - * - * Learn more in https://angular.io/guide/browser-support - */ - -/*************************************************************************************************** - * BROWSER POLYFILLS - */ - -/** - * By default, zone.js will patch all possible macroTask and DomEvents - * user can disable parts of macroTask/DomEvents patch by setting following flags - * because those flags need to be set before `zone.js` being loaded, and webpack - * will put import in the top of bundle, so user need to create a separate file - * in this directory (for example: zone-flags.ts), and put the following flags - * into that file, and then add the following code before importing zone.js. - * import './zone-flags'; - * - * The flags allowed in zone-flags.ts are listed here. - * - * The following flags will work for all browsers. - * - * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame - * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick - * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames - * - * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js - * with the following flag, it will bypass `zone.js` patch for IE/Edge - * - * (window as any).__Zone_enable_cross_context_check = true; - * - */ - -/*************************************************************************************************** - * Zone JS is required by default for Angular itself. - */ -import 'zone.js'; // Included with Angular CLI. - -/*************************************************************************************************** - * APPLICATION IMPORTS - */ diff --git a/apps/angular/3-directive-enhancement/tsconfig.app.json b/apps/angular/3-directive-enhancement/tsconfig.app.json deleted file mode 100644 index 7a4dbc47e..000000000 --- a/apps/angular/3-directive-enhancement/tsconfig.app.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "../../../dist/out-tsc", - "types": [], - "target": "ES2022", - "useDefineForClassFields": false - }, - "files": ["src/main.ts", "src/polyfills.ts"], - "include": ["src/**/*.d.ts"], - "exclude": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts"] -} diff --git a/apps/angular/3-directive-enhancement/tsconfig.editor.json b/apps/angular/3-directive-enhancement/tsconfig.editor.json deleted file mode 100644 index 0f9036b03..000000000 --- a/apps/angular/3-directive-enhancement/tsconfig.editor.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["**/*.ts"], - "compilerOptions": { - "types": [] - } -} diff --git a/apps/angular/31-module-to-standalone/project.json b/apps/angular/31-module-to-standalone/project.json index c1a51b5ed..b02e0a0a8 100644 --- a/apps/angular/31-module-to-standalone/project.json +++ b/apps/angular/31-module-to-standalone/project.json @@ -59,7 +59,8 @@ "buildTarget": "angular-module-to-standalone:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", diff --git a/apps/angular/31-module-to-standalone/src/app/app.component.ts b/apps/angular/31-module-to-standalone/src/app/app.component.ts index 95eeab5b9..986df84b5 100644 --- a/apps/angular/31-module-to-standalone/src/app/app.component.ts +++ b/apps/angular/31-module-to-standalone/src/app/app.component.ts @@ -25,5 +25,6 @@ import { Component } from '@angular/core'; host: { class: 'flex flex-col p-4 gap-3', }, + standalone: false, }) export class AppComponent {} diff --git a/apps/angular/31-module-to-standalone/tsconfig.app.json b/apps/angular/31-module-to-standalone/tsconfig.app.json index 58220429a..8b5631268 100644 --- a/apps/angular/31-module-to-standalone/tsconfig.app.json +++ b/apps/angular/31-module-to-standalone/tsconfig.app.json @@ -2,7 +2,8 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../dist/out-tsc", - "types": [] + "types": [], + "moduleResolution": "bundler" }, "files": ["src/main.ts"], "include": ["src/**/*.d.ts"], diff --git a/apps/angular/32-change-detection-bug/project.json b/apps/angular/32-change-detection-bug/project.json index 41b4090cc..977b76334 100644 --- a/apps/angular/32-change-detection-bug/project.json +++ b/apps/angular/32-change-detection-bug/project.json @@ -59,7 +59,8 @@ "buildTarget": "angular-change-detection-bug:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", @@ -68,10 +69,14 @@ } }, "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], "options": { - "jestConfig": "apps/angular/32-change-detection-bug/jest.config.ts" + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } } } } diff --git a/apps/angular/32-change-detection-bug/src/app/app.component.ts b/apps/angular/32-change-detection-bug/src/app/app.component.ts index 046492f9b..217999c3a 100644 --- a/apps/angular/32-change-detection-bug/src/app/app.component.ts +++ b/apps/angular/32-change-detection-bug/src/app/app.component.ts @@ -2,7 +2,6 @@ import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; @Component({ - standalone: true, imports: [RouterOutlet], selector: 'app-root', template: ` diff --git a/apps/angular/32-change-detection-bug/src/app/bar.component.ts b/apps/angular/32-change-detection-bug/src/app/bar.component.ts index b7a53924e..81981f99d 100644 --- a/apps/angular/32-change-detection-bug/src/app/bar.component.ts +++ b/apps/angular/32-change-detection-bug/src/app/bar.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; + @Component({ selector: 'app-bar', - standalone: true, template: ` BarComponent `, diff --git a/apps/angular/32-change-detection-bug/src/app/foo.component.ts b/apps/angular/32-change-detection-bug/src/app/foo.component.ts index a5b1207b1..1fcb24326 100644 --- a/apps/angular/32-change-detection-bug/src/app/foo.component.ts +++ b/apps/angular/32-change-detection-bug/src/app/foo.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; + @Component({ selector: 'app-foo', - standalone: true, template: ` Foo Component `, diff --git a/apps/angular/32-change-detection-bug/src/app/main-navigation.component.ts b/apps/angular/32-change-detection-bug/src/app/main-navigation.component.ts index c8a6f6d22..1a3a5e93a 100644 --- a/apps/angular/32-change-detection-bug/src/app/main-navigation.component.ts +++ b/apps/angular/32-change-detection-bug/src/app/main-navigation.component.ts @@ -1,5 +1,5 @@ -import { AsyncPipe, NgFor, NgIf } from '@angular/common'; -import { Component, Input, inject } from '@angular/core'; +import { Component, inject, input } from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; import { RouterLink, RouterLinkActive } from '@angular/router'; import { FakeServiceService } from './fake.service'; @@ -10,17 +10,16 @@ interface MenuItem { @Component({ selector: 'app-nav', - standalone: true, - imports: [RouterLink, RouterLinkActive, NgFor], + imports: [RouterLink, RouterLinkActive], template: ` - + @for (menu of menus(); track menu.path) { {{ menu.name }} - + } `, styles: [ ` @@ -34,29 +33,26 @@ interface MenuItem { }, }) export class NavigationComponent { - @Input() menus!: MenuItem[]; + menus = input.required(); } @Component({ - standalone: true, - imports: [NavigationComponent, NgIf, AsyncPipe], + imports: [NavigationComponent], template: ` - - - - - - - + @if (info() !== null) { + + } @else { - + } `, host: {}, }) export class MainNavigationComponent { private fakeBackend = inject(FakeServiceService); - readonly info$ = this.fakeBackend.getInfoFromBackend(); + readonly info = toSignal(this.fakeBackend.getInfoFromBackend(), { + initialValue: null, + }); getMenu(prop: string) { return [ diff --git a/apps/angular/32-change-detection-bug/tsconfig.app.json b/apps/angular/32-change-detection-bug/tsconfig.app.json index 58220429a..8b5631268 100644 --- a/apps/angular/32-change-detection-bug/tsconfig.app.json +++ b/apps/angular/32-change-detection-bug/tsconfig.app.json @@ -2,7 +2,8 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../dist/out-tsc", - "types": [] + "types": [], + "moduleResolution": "bundler" }, "files": ["src/main.ts"], "include": ["src/**/*.d.ts"], diff --git a/apps/angular/33-decoupling-components/project.json b/apps/angular/33-decoupling-components/project.json index 762fc01b5..c4140b61b 100644 --- a/apps/angular/33-decoupling-components/project.json +++ b/apps/angular/33-decoupling-components/project.json @@ -59,7 +59,8 @@ "buildTarget": "angular-decoupling-components:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", diff --git a/apps/angular/33-decoupling-components/src/app/app.component.ts b/apps/angular/33-decoupling-components/src/app/app.component.ts index 34082c331..0d78f4d34 100644 --- a/apps/angular/33-decoupling-components/src/app/app.component.ts +++ b/apps/angular/33-decoupling-components/src/app/app.component.ts @@ -3,7 +3,6 @@ import { BtnHelmetDirective } from '@angular-challenges/decoupling/helmet'; import { Component } from '@angular/core'; @Component({ - standalone: true, imports: [BtnDisabledDirective, BtnHelmetDirective], selector: 'app-root', template: ` diff --git a/apps/angular/33-decoupling-components/tsconfig.app.json b/apps/angular/33-decoupling-components/tsconfig.app.json index 58220429a..8b5631268 100644 --- a/apps/angular/33-decoupling-components/tsconfig.app.json +++ b/apps/angular/33-decoupling-components/tsconfig.app.json @@ -2,7 +2,8 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../dist/out-tsc", - "types": [] + "types": [], + "moduleResolution": "bundler" }, "files": ["src/main.ts"], "include": ["src/**/*.d.ts"], diff --git a/apps/angular/39-injection-token/project.json b/apps/angular/39-injection-token/project.json index d5ae3f98e..135c0c0f6 100644 --- a/apps/angular/39-injection-token/project.json +++ b/apps/angular/39-injection-token/project.json @@ -59,7 +59,8 @@ "buildTarget": "angular-injection-token:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", @@ -68,10 +69,14 @@ } }, "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], "options": { - "jestConfig": "apps/angular/39-injection-token/jest.config.ts" + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } } } } diff --git a/apps/angular/39-injection-token/src/app/app.component.ts b/apps/angular/39-injection-token/src/app/app.component.ts index 5ac8087cb..280dc090a 100644 --- a/apps/angular/39-injection-token/src/app/app.component.ts +++ b/apps/angular/39-injection-token/src/app/app.component.ts @@ -2,7 +2,6 @@ import { Component } from '@angular/core'; import { RouterLink, RouterOutlet } from '@angular/router'; @Component({ - standalone: true, imports: [RouterOutlet, RouterLink], selector: 'app-root', template: ` diff --git a/apps/angular/39-injection-token/src/app/phone.component.ts b/apps/angular/39-injection-token/src/app/phone.component.ts index a58b3cd99..41ee3cfc0 100644 --- a/apps/angular/39-injection-token/src/app/phone.component.ts +++ b/apps/angular/39-injection-token/src/app/phone.component.ts @@ -3,7 +3,6 @@ import { TimerContainerComponent } from './timer-container.component'; @Component({ selector: 'app-phone', - standalone: true, imports: [TimerContainerComponent], template: `
diff --git a/apps/angular/39-injection-token/src/app/timer-container.component.ts b/apps/angular/39-injection-token/src/app/timer-container.component.ts index 30af69354..67db6059a 100644 --- a/apps/angular/39-injection-token/src/app/timer-container.component.ts +++ b/apps/angular/39-injection-token/src/app/timer-container.component.ts @@ -3,7 +3,6 @@ import { DEFAULT_TIMER } from './data'; import { TimerComponent } from './timer.component'; @Component({ selector: 'timer-container', - standalone: true, imports: [TimerComponent], template: `
diff --git a/apps/angular/39-injection-token/src/app/timer.component.ts b/apps/angular/39-injection-token/src/app/timer.component.ts index 95707ec61..335a077bf 100644 --- a/apps/angular/39-injection-token/src/app/timer.component.ts +++ b/apps/angular/39-injection-token/src/app/timer.component.ts @@ -5,7 +5,6 @@ import { DEFAULT_TIMER } from './data'; @Component({ selector: 'timer', - standalone: true, template: ` Timer running {{ timer() }} `, diff --git a/apps/angular/39-injection-token/src/app/video.component.ts b/apps/angular/39-injection-token/src/app/video.component.ts index 2c218071a..ba0a218b4 100644 --- a/apps/angular/39-injection-token/src/app/video.component.ts +++ b/apps/angular/39-injection-token/src/app/video.component.ts @@ -3,7 +3,6 @@ import { TimerContainerComponent } from './timer-container.component'; @Component({ selector: 'app-video', - standalone: true, imports: [TimerContainerComponent], template: `
diff --git a/apps/angular/39-injection-token/tsconfig.app.json b/apps/angular/39-injection-token/tsconfig.app.json index 58220429a..8b5631268 100644 --- a/apps/angular/39-injection-token/tsconfig.app.json +++ b/apps/angular/39-injection-token/tsconfig.app.json @@ -2,7 +2,8 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../dist/out-tsc", - "types": [] + "types": [], + "moduleResolution": "bundler" }, "files": ["src/main.ts"], "include": ["src/**/*.d.ts"], diff --git a/apps/angular/4-typed-context-outlet/project.json b/apps/angular/4-typed-context-outlet/project.json index ccebf62c7..273bca97d 100644 --- a/apps/angular/4-typed-context-outlet/project.json +++ b/apps/angular/4-typed-context-outlet/project.json @@ -60,7 +60,8 @@ "buildTarget": "angular-typed-context-outlet:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", diff --git a/apps/angular/4-typed-context-outlet/src/app/app.component.ts b/apps/angular/4-typed-context-outlet/src/app/app.component.ts index 2683ba9d4..d608bec2c 100644 --- a/apps/angular/4-typed-context-outlet/src/app/app.component.ts +++ b/apps/angular/4-typed-context-outlet/src/app/app.component.ts @@ -1,11 +1,9 @@ -import { NgTemplateOutlet } from '@angular/common'; import { ChangeDetectionStrategy, Component } from '@angular/core'; import { ListComponent } from './list.component'; import { PersonComponent } from './person.component'; @Component({ - standalone: true, - imports: [NgTemplateOutlet, PersonComponent, ListComponent], + imports: [PersonComponent, ListComponent], selector: 'app-root', template: ` diff --git a/apps/angular/4-typed-context-outlet/src/app/list.component.ts b/apps/angular/4-typed-context-outlet/src/app/list.component.ts index 5212a425e..57fa4e361 100644 --- a/apps/angular/4-typed-context-outlet/src/app/list.component.ts +++ b/apps/angular/4-typed-context-outlet/src/app/list.component.ts @@ -1,32 +1,30 @@ -import { CommonModule } from '@angular/common'; +import { NgTemplateOutlet } from '@angular/common'; import { ChangeDetectionStrategy, Component, - ContentChild, - Input, + contentChild, + input, TemplateRef, } from '@angular/core'; @Component({ selector: 'list', - standalone: true, - imports: [CommonModule], template: ` -
+ @for (item of list(); track $index) { -
+ listTemplateRef() || emptyRef; + context: { $implicit: item, appList: item, index: $index } + " /> + } No Template `, changeDetection: ChangeDetectionStrategy.OnPush, + imports: [NgTemplateOutlet], }) export class ListComponent { - @Input() list!: TItem[]; + list = input.required(); - @ContentChild('listRef', { read: TemplateRef }) - listTemplateRef!: TemplateRef; + listTemplateRef = contentChild('listRef', { read: TemplateRef }); } diff --git a/apps/angular/4-typed-context-outlet/src/app/person.component.ts b/apps/angular/4-typed-context-outlet/src/app/person.component.ts index 1550cf274..d9f5e7520 100644 --- a/apps/angular/4-typed-context-outlet/src/app/person.component.ts +++ b/apps/angular/4-typed-context-outlet/src/app/person.component.ts @@ -1,28 +1,21 @@ import { NgTemplateOutlet } from '@angular/common'; -import { Component, ContentChild, Input, TemplateRef } from '@angular/core'; - -interface Person { - name: string; - age: number; -} +import { Component, contentChild, input, TemplateRef } from '@angular/core'; @Component({ - standalone: true, imports: [NgTemplateOutlet], selector: 'person', template: ` + personTemplateRef() || emptyRef; + context: { $implicit: person().name, age: person().age } + " /> No Template `, }) export class PersonComponent { - @Input() person!: Person; + person = input.required<{ name: string; age: number }>(); - @ContentChild('#personRef', { read: TemplateRef }) - personTemplateRef!: TemplateRef; + personTemplateRef = contentChild('personRef', { read: TemplateRef }); } diff --git a/apps/angular/4-typed-context-outlet/tsconfig.app.json b/apps/angular/4-typed-context-outlet/tsconfig.app.json index 7a4dbc47e..2a1ca1b8d 100644 --- a/apps/angular/4-typed-context-outlet/tsconfig.app.json +++ b/apps/angular/4-typed-context-outlet/tsconfig.app.json @@ -4,7 +4,8 @@ "outDir": "../../../dist/out-tsc", "types": [], "target": "ES2022", - "useDefineForClassFields": false + "useDefineForClassFields": false, + "moduleResolution": "bundler" }, "files": ["src/main.ts", "src/polyfills.ts"], "include": ["src/**/*.d.ts"], diff --git a/apps/angular/44-view-transition/project.json b/apps/angular/44-view-transition/project.json index 9f4046607..4104b5de9 100644 --- a/apps/angular/44-view-transition/project.json +++ b/apps/angular/44-view-transition/project.json @@ -57,7 +57,8 @@ "buildTarget": "angular-view-transition:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", diff --git a/apps/angular/44-view-transition/src/app/app.component.ts b/apps/angular/44-view-transition/src/app/app.component.ts index da56c04c0..1fcb0c548 100644 --- a/apps/angular/44-view-transition/src/app/app.component.ts +++ b/apps/angular/44-view-transition/src/app/app.component.ts @@ -2,7 +2,6 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; @Component({ - standalone: true, imports: [RouterOutlet], selector: 'app-root', template: ` diff --git a/apps/angular/44-view-transition/src/app/blog/blog.component.ts b/apps/angular/44-view-transition/src/app/blog/blog.component.ts index 68ffcde50..29291d21e 100644 --- a/apps/angular/44-view-transition/src/app/blog/blog.component.ts +++ b/apps/angular/44-view-transition/src/app/blog/blog.component.ts @@ -4,7 +4,6 @@ import { ThumbnailComponent } from './thumbnail.component'; @Component({ selector: 'blog', - standalone: true, imports: [ThumbnailComponent], template: `
diff --git a/apps/angular/44-view-transition/src/app/blog/thumbnail.component.ts b/apps/angular/44-view-transition/src/app/blog/thumbnail.component.ts index 6263e97ab..dd2e25e26 100644 --- a/apps/angular/44-view-transition/src/app/blog/thumbnail.component.ts +++ b/apps/angular/44-view-transition/src/app/blog/thumbnail.component.ts @@ -6,7 +6,6 @@ import { ThumbnailHeaderComponent } from './thumbnail-header.component'; @Component({ selector: 'blog-thumbnail', - standalone: true, imports: [NgOptimizedImage, ThumbnailHeaderComponent, RouterLinkWithHref], template: ` diff --git a/apps/angular/44-view-transition/src/app/post/post-header.component.ts b/apps/angular/44-view-transition/src/app/post/post-header.component.ts index 8b62a6c48..6d5f30e54 100644 --- a/apps/angular/44-view-transition/src/app/post/post-header.component.ts +++ b/apps/angular/44-view-transition/src/app/post/post-header.component.ts @@ -3,7 +3,6 @@ import { Component, input } from '@angular/core'; @Component({ selector: 'post-header', - standalone: true, imports: [NgOptimizedImage], template: `
diff --git a/apps/angular/44-view-transition/src/app/post/post.component.ts b/apps/angular/44-view-transition/src/app/post/post.component.ts index 1e1c6fd89..edb87f780 100644 --- a/apps/angular/44-view-transition/src/app/post/post.component.ts +++ b/apps/angular/44-view-transition/src/app/post/post.component.ts @@ -12,7 +12,6 @@ import { PostHeaderComponent } from './post-header.component'; @Component({ selector: 'post', - standalone: true, imports: [ ThumbnailHeaderComponent, NgOptimizedImage, diff --git a/apps/angular/44-view-transition/tsconfig.app.json b/apps/angular/44-view-transition/tsconfig.app.json index 58220429a..8b5631268 100644 --- a/apps/angular/44-view-transition/tsconfig.app.json +++ b/apps/angular/44-view-transition/tsconfig.app.json @@ -2,7 +2,8 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../dist/out-tsc", - "types": [] + "types": [], + "moduleResolution": "bundler" }, "files": ["src/main.ts"], "include": ["src/**/*.d.ts"], diff --git a/apps/angular/45-react-in-angular/project.json b/apps/angular/45-react-in-angular/project.json index b2fe821a1..8a3bf02fc 100644 --- a/apps/angular/45-react-in-angular/project.json +++ b/apps/angular/45-react-in-angular/project.json @@ -57,7 +57,8 @@ "buildTarget": "angular-react-in-angular:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", @@ -66,10 +67,14 @@ } }, "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], "options": { - "jestConfig": "apps/angular/45-react-in-angular/jest.config.ts" + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } } } } diff --git a/apps/angular/45-react-in-angular/src/app/app.component.ts b/apps/angular/45-react-in-angular/src/app/app.component.ts index a914cc65f..87b9675cc 100644 --- a/apps/angular/45-react-in-angular/src/app/app.component.ts +++ b/apps/angular/45-react-in-angular/src/app/app.component.ts @@ -4,7 +4,6 @@ import { PostComponent } from './react/post.component'; type Post = { title: string; description: string }; @Component({ - standalone: true, imports: [PostComponent], selector: 'app-root', template: ` diff --git a/apps/angular/45-react-in-angular/src/app/react/post.component.ts b/apps/angular/45-react-in-angular/src/app/react/post.component.ts index 2a35eaca6..d5eb2cedf 100644 --- a/apps/angular/45-react-in-angular/src/app/react/post.component.ts +++ b/apps/angular/45-react-in-angular/src/app/react/post.component.ts @@ -3,7 +3,6 @@ import { Component, EventEmitter, input, Output } from '@angular/core'; type Post = { title: string; description: string; pictureLink: string }; @Component({ - standalone: true, selector: 'app-post', template: `
diff --git a/apps/angular/45-react-in-angular/tsconfig.app.json b/apps/angular/45-react-in-angular/tsconfig.app.json index 58220429a..8b5631268 100644 --- a/apps/angular/45-react-in-angular/tsconfig.app.json +++ b/apps/angular/45-react-in-angular/tsconfig.app.json @@ -2,7 +2,8 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../dist/out-tsc", - "types": [] + "types": [], + "moduleResolution": "bundler" }, "files": ["src/main.ts"], "include": ["src/**/*.d.ts"], diff --git a/apps/angular/46-simple-animations/project.json b/apps/angular/46-simple-animations/project.json index 70e17fd1e..34e44c7b3 100644 --- a/apps/angular/46-simple-animations/project.json +++ b/apps/angular/46-simple-animations/project.json @@ -57,7 +57,8 @@ "buildTarget": "angular-simple-animations:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", @@ -66,10 +67,14 @@ } }, "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], "options": { - "jestConfig": "apps/angular/46-simple-animations/jest.config.ts" + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } } } } diff --git a/apps/angular/46-simple-animations/src/app/app.component.ts b/apps/angular/46-simple-animations/src/app/app.component.ts index 9f537b3fb..ae63db419 100644 --- a/apps/angular/46-simple-animations/src/app/app.component.ts +++ b/apps/angular/46-simple-animations/src/app/app.component.ts @@ -1,7 +1,6 @@ import { Component } from '@angular/core'; @Component({ - standalone: true, imports: [], selector: 'app-root', styles: ` diff --git a/apps/angular/46-simple-animations/tsconfig.app.json b/apps/angular/46-simple-animations/tsconfig.app.json index 58220429a..8b5631268 100644 --- a/apps/angular/46-simple-animations/tsconfig.app.json +++ b/apps/angular/46-simple-animations/tsconfig.app.json @@ -2,7 +2,8 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../dist/out-tsc", - "types": [] + "types": [], + "moduleResolution": "bundler" }, "files": ["src/main.ts"], "include": ["src/**/*.d.ts"], diff --git a/apps/angular/5-crud-application/project.json b/apps/angular/5-crud-application/project.json index c9cca6fb6..a31bd62a7 100644 --- a/apps/angular/5-crud-application/project.json +++ b/apps/angular/5-crud-application/project.json @@ -64,7 +64,8 @@ "buildTarget": "angular-crud-application:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", @@ -73,10 +74,14 @@ } }, "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], "options": { - "jestConfig": "apps/angular/5-crud-application/jest.config.ts" + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } } } } diff --git a/apps/angular/5-crud-application/src/app/app.component.ts b/apps/angular/5-crud-application/src/app/app.component.ts index 8c3d1b8ae..73ba0dc34 100644 --- a/apps/angular/5-crud-application/src/app/app.component.ts +++ b/apps/angular/5-crud-application/src/app/app.component.ts @@ -1,24 +1,22 @@ -import { CommonModule } from '@angular/common'; import { HttpClient } from '@angular/common/http'; -import { Component, OnInit } from '@angular/core'; +import { Component, inject, OnInit } from '@angular/core'; import { randText } from '@ngneat/falso'; @Component({ - standalone: true, - imports: [CommonModule], + imports: [], selector: 'app-root', template: ` -
+ @for (todo of todos; track todo.id) { {{ todo.title }} -
+ } `, styles: [], }) export class AppComponent implements OnInit { - todos!: any[]; + private http = inject(HttpClient); - constructor(private http: HttpClient) {} + todos!: any[]; ngOnInit(): void { this.http diff --git a/apps/angular/5-crud-application/tsconfig.app.json b/apps/angular/5-crud-application/tsconfig.app.json index 7a4dbc47e..2a1ca1b8d 100644 --- a/apps/angular/5-crud-application/tsconfig.app.json +++ b/apps/angular/5-crud-application/tsconfig.app.json @@ -4,7 +4,8 @@ "outDir": "../../../dist/out-tsc", "types": [], "target": "ES2022", - "useDefineForClassFields": false + "useDefineForClassFields": false, + "moduleResolution": "bundler" }, "files": ["src/main.ts", "src/polyfills.ts"], "include": ["src/**/*.d.ts"], diff --git a/apps/angular/52-lazy-load-component/project.json b/apps/angular/52-lazy-load-component/project.json index 7767de52c..933dd17e5 100644 --- a/apps/angular/52-lazy-load-component/project.json +++ b/apps/angular/52-lazy-load-component/project.json @@ -57,7 +57,8 @@ "buildTarget": "angular-lazy-load-component:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", @@ -65,14 +66,15 @@ "buildTarget": "angular-lazy-load-component:build" } }, - "lint": { - "executor": "@nx/eslint:lint" - }, "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], "options": { - "jestConfig": "apps/angular/52-lazy-load-component/jest.config.ts" + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } } } } diff --git a/apps/angular/52-lazy-load-component/src/app/app.component.ts b/apps/angular/52-lazy-load-component/src/app/app.component.ts index ccad6321c..6d8c03d29 100644 --- a/apps/angular/52-lazy-load-component/src/app/app.component.ts +++ b/apps/angular/52-lazy-load-component/src/app/app.component.ts @@ -16,6 +16,7 @@ import { Component, signal } from '@angular/core'; }
`, + standalone: false, }) export class AppComponent { topLoaded = signal(false); diff --git a/apps/angular/52-lazy-load-component/src/app/placeholder.component.ts b/apps/angular/52-lazy-load-component/src/app/placeholder.component.ts index 051e39491..cbb2b5fa6 100644 --- a/apps/angular/52-lazy-load-component/src/app/placeholder.component.ts +++ b/apps/angular/52-lazy-load-component/src/app/placeholder.component.ts @@ -13,5 +13,6 @@ import { Component } from '@angular/core'; height: 50%; } `, + standalone: false, }) export class PlaceholderComponent {} diff --git a/apps/angular/52-lazy-load-component/src/app/top.component.ts b/apps/angular/52-lazy-load-component/src/app/top.component.ts index d9104bef3..e1ca9012c 100644 --- a/apps/angular/52-lazy-load-component/src/app/top.component.ts +++ b/apps/angular/52-lazy-load-component/src/app/top.component.ts @@ -13,5 +13,6 @@ import { Component } from '@angular/core'; height: 50%; } `, + standalone: false, }) export class TopComponent {} diff --git a/apps/angular/52-lazy-load-component/tsconfig.app.json b/apps/angular/52-lazy-load-component/tsconfig.app.json index 58220429a..8b5631268 100644 --- a/apps/angular/52-lazy-load-component/tsconfig.app.json +++ b/apps/angular/52-lazy-load-component/tsconfig.app.json @@ -2,7 +2,8 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../dist/out-tsc", - "types": [] + "types": [], + "moduleResolution": "bundler" }, "files": ["src/main.ts"], "include": ["src/**/*.d.ts"], diff --git a/apps/angular/55-back-button-navigation/project.json b/apps/angular/55-back-button-navigation/project.json index 62c7b2773..e6c824729 100644 --- a/apps/angular/55-back-button-navigation/project.json +++ b/apps/angular/55-back-button-navigation/project.json @@ -62,7 +62,8 @@ "buildTarget": "angular-back-button-navigation:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", @@ -70,14 +71,15 @@ "buildTarget": "angular-back-button-navigation:build" } }, - "lint": { - "executor": "@nx/eslint:lint" - }, "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], "options": { - "jestConfig": "apps/angular/55-back-button-navigation/jest.config.ts" + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } } } } diff --git a/apps/angular/55-back-button-navigation/src/app/app.component.ts b/apps/angular/55-back-button-navigation/src/app/app.component.ts index 22c15ea54..baffdae25 100644 --- a/apps/angular/55-back-button-navigation/src/app/app.component.ts +++ b/apps/angular/55-back-button-navigation/src/app/app.component.ts @@ -2,7 +2,6 @@ import { Component } from '@angular/core'; import { RouterLink, RouterOutlet } from '@angular/router'; @Component({ - standalone: true, imports: [RouterOutlet, RouterLink], selector: 'app-root', templateUrl: './app.component.html', diff --git a/apps/angular/55-back-button-navigation/src/app/dialog/dialog.component.ts b/apps/angular/55-back-button-navigation/src/app/dialog/dialog.component.ts index e1142967e..9a9dd0fef 100644 --- a/apps/angular/55-back-button-navigation/src/app/dialog/dialog.component.ts +++ b/apps/angular/55-back-button-navigation/src/app/dialog/dialog.component.ts @@ -11,7 +11,6 @@ import { @Component({ selector: 'app-dialog-dialog', templateUrl: './dialog.component.html', - standalone: true, imports: [ MatButtonModule, MatDialogActions, diff --git a/apps/angular/55-back-button-navigation/src/app/home/home.component.ts b/apps/angular/55-back-button-navigation/src/app/home/home.component.ts index 0e68bbd3d..18c4147b1 100644 --- a/apps/angular/55-back-button-navigation/src/app/home/home.component.ts +++ b/apps/angular/55-back-button-navigation/src/app/home/home.component.ts @@ -3,7 +3,6 @@ import { MatButtonModule } from '@angular/material/button'; import { RouterLink } from '@angular/router'; @Component({ - standalone: true, imports: [MatButtonModule, RouterLink], selector: 'app-home', templateUrl: './home.component.html', diff --git a/apps/angular/55-back-button-navigation/src/app/sensitive-action/sensitive-action.component.ts b/apps/angular/55-back-button-navigation/src/app/sensitive-action/sensitive-action.component.ts index a44a1ea8b..a97282c33 100644 --- a/apps/angular/55-back-button-navigation/src/app/sensitive-action/sensitive-action.component.ts +++ b/apps/angular/55-back-button-navigation/src/app/sensitive-action/sensitive-action.component.ts @@ -4,7 +4,6 @@ import { MatDialog } from '@angular/material/dialog'; import { DialogComponent } from '../dialog/dialog.component'; @Component({ - standalone: true, imports: [MatButtonModule], selector: 'app-sensitive-action', templateUrl: './sensitive-action.component.html', diff --git a/apps/angular/55-back-button-navigation/src/app/simple-action/simple-action.component.ts b/apps/angular/55-back-button-navigation/src/app/simple-action/simple-action.component.ts index 458e0b102..fe97e7368 100644 --- a/apps/angular/55-back-button-navigation/src/app/simple-action/simple-action.component.ts +++ b/apps/angular/55-back-button-navigation/src/app/simple-action/simple-action.component.ts @@ -4,7 +4,6 @@ import { MatDialog } from '@angular/material/dialog'; import { DialogComponent } from '../dialog/dialog.component'; @Component({ - standalone: true, imports: [MatButtonModule], selector: 'app-simple-action', templateUrl: './simple-action.component.html', diff --git a/apps/angular/55-back-button-navigation/src/styles.scss b/apps/angular/55-back-button-navigation/src/styles.scss index b20428941..acd290007 100644 --- a/apps/angular/55-back-button-navigation/src/styles.scss +++ b/apps/angular/55-back-button-navigation/src/styles.scss @@ -6,7 +6,8 @@ @tailwind components; @tailwind utilities; -@include mat.core(); +@include mat.elevation-classes(); +@include mat.app-background(); $theme-primary: mat.m2-define-palette(mat.$m2-indigo-palette); $theme-accent: mat.m2-define-palette(mat.$m2-pink-palette, A200, A100, A400); diff --git a/apps/angular/55-back-button-navigation/tsconfig.app.json b/apps/angular/55-back-button-navigation/tsconfig.app.json index 58220429a..8b5631268 100644 --- a/apps/angular/55-back-button-navigation/tsconfig.app.json +++ b/apps/angular/55-back-button-navigation/tsconfig.app.json @@ -2,7 +2,8 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../dist/out-tsc", - "types": [] + "types": [], + "moduleResolution": "bundler" }, "files": ["src/main.ts"], "include": ["src/**/*.d.ts"], diff --git a/apps/angular/3-directive-enhancement/.eslintrc.json b/apps/angular/57-content-projection-default/.eslintrc.json similarity index 100% rename from apps/angular/3-directive-enhancement/.eslintrc.json rename to apps/angular/57-content-projection-default/.eslintrc.json diff --git a/apps/ngrx/2-effect-vs-selector/README.md b/apps/angular/57-content-projection-default/README.md similarity index 53% rename from apps/ngrx/2-effect-vs-selector/README.md rename to apps/angular/57-content-projection-default/README.md index 44640737e..fc4579558 100644 --- a/apps/ngrx/2-effect-vs-selector/README.md +++ b/apps/angular/57-content-projection-default/README.md @@ -1,13 +1,13 @@ -# Effect vs Selector +# Content Projection Default > author: thomas-laforge ### Run Application ```bash -npx nx serve ngrx-effect-vs-selector +npx nx serve angular-content-projection-default ``` ### Documentation and Instruction -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/ngrx/2-effect-selector/). +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/57-content-projection-default/). diff --git a/apps/angular/57-content-projection-default/project.json b/apps/angular/57-content-projection-default/project.json new file mode 100644 index 000000000..0630925f4 --- /dev/null +++ b/apps/angular/57-content-projection-default/project.json @@ -0,0 +1,82 @@ +{ + "name": "angular-content-projection-default", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/angular/57-content-projection-default/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/57-content-projection-default", + "index": "apps/angular/57-content-projection-default/src/index.html", + "browser": "apps/angular/57-content-projection-default/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/angular/57-content-projection-default/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + { + "glob": "**/*", + "input": "apps/angular/57-content-projection-default/public" + } + ], + "styles": [ + "apps/angular/57-content-projection-default/src/styles.scss" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "4kb", + "maximumError": "8kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-content-projection-default:build:production" + }, + "development": { + "buildTarget": "angular-content-projection-default:build:development" + } + }, + "defaultConfiguration": "development", + "continuous": true + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-content-projection-default:build" + } + }, + "serve-static": { + "executor": "@nx/web:file-server", + "options": { + "buildTarget": "angular-content-projection-default:build", + "staticFilePath": "dist/apps/angular/57-content-projection-default/browser", + "spa": true + } + } + } +} diff --git a/apps/angular/3-directive-enhancement/src/favicon.ico b/apps/angular/57-content-projection-default/public/favicon.ico similarity index 100% rename from apps/angular/3-directive-enhancement/src/favicon.ico rename to apps/angular/57-content-projection-default/public/favicon.ico diff --git a/apps/angular/57-content-projection-default/src/app/app.component.ts b/apps/angular/57-content-projection-default/src/app/app.component.ts new file mode 100644 index 000000000..b3e370a34 --- /dev/null +++ b/apps/angular/57-content-projection-default/src/app/app.component.ts @@ -0,0 +1,16 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { CardComponent } from './card.component'; + +@Component({ + imports: [CardComponent], + selector: 'app-root', + template: ` + + + `, + host: { + class: 'p-4 block flex flex-col gap-1', + }, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AppComponent {} diff --git a/apps/angular/57-content-projection-default/src/app/app.config.ts b/apps/angular/57-content-projection-default/src/app/app.config.ts new file mode 100644 index 000000000..034603cfd --- /dev/null +++ b/apps/angular/57-content-projection-default/src/app/app.config.ts @@ -0,0 +1,5 @@ +import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; + +export const appConfig: ApplicationConfig = { + providers: [provideZoneChangeDetection({ eventCoalescing: true })], +}; diff --git a/apps/angular/57-content-projection-default/src/app/card.component.ts b/apps/angular/57-content-projection-default/src/app/card.component.ts new file mode 100644 index 000000000..851a6619d --- /dev/null +++ b/apps/angular/57-content-projection-default/src/app/card.component.ts @@ -0,0 +1,22 @@ +import { ChangeDetectionStrategy, Component, input } from '@angular/core'; + +@Component({ + selector: 'app-card', + imports: [], + template: ` +
{{ title() }}
+ @if (message()) { +
{{ message() }}
+ } @else { +
Aucun message
+ } + `, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { + class: 'p-4 border border-grey rounded-sm flex flex-col w-[200px]', + }, +}) +export class CardComponent { + title = input.required(); + message = input(undefined); +} diff --git a/apps/ngrx/2-effect-vs-selector/src/index.html b/apps/angular/57-content-projection-default/src/index.html similarity index 84% rename from apps/ngrx/2-effect-vs-selector/src/index.html rename to apps/angular/57-content-projection-default/src/index.html index d3a909e3b..8b3015d4b 100644 --- a/apps/ngrx/2-effect-vs-selector/src/index.html +++ b/apps/angular/57-content-projection-default/src/index.html @@ -2,7 +2,7 @@ - ngrx-effect-vs-selector + angular-content-projection-default diff --git a/apps/ngrx/7-power-of-effect/src/main.ts b/apps/angular/57-content-projection-default/src/main.ts similarity index 99% rename from apps/ngrx/7-power-of-effect/src/main.ts rename to apps/angular/57-content-projection-default/src/main.ts index 6f91f21a3..f3a7223da 100644 --- a/apps/ngrx/7-power-of-effect/src/main.ts +++ b/apps/angular/57-content-projection-default/src/main.ts @@ -1,8 +1,6 @@ -import { appConfig } from './app/app.config'; - import { bootstrapApplication } from '@angular/platform-browser'; - import { AppComponent } from './app/app.component'; +import { appConfig } from './app/app.config'; bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err), diff --git a/apps/angular/3-directive-enhancement/src/styles.scss b/apps/angular/57-content-projection-default/src/styles.scss similarity index 57% rename from apps/angular/3-directive-enhancement/src/styles.scss rename to apps/angular/57-content-projection-default/src/styles.scss index 90d4ee007..77e408aa8 100644 --- a/apps/angular/3-directive-enhancement/src/styles.scss +++ b/apps/angular/57-content-projection-default/src/styles.scss @@ -1 +1,5 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + /* You can add global styles to this file, and also import other style files */ diff --git a/apps/angular/57-content-projection-default/tailwind.config.js b/apps/angular/57-content-projection-default/tailwind.config.js new file mode 100644 index 000000000..38183db2c --- /dev/null +++ b/apps/angular/57-content-projection-default/tailwind.config.js @@ -0,0 +1,14 @@ +const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind'); +const { join } = require('path'); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/apps/angular/57-content-projection-default/tsconfig.app.json b/apps/angular/57-content-projection-default/tsconfig.app.json new file mode 100644 index 000000000..8b5631268 --- /dev/null +++ b/apps/angular/57-content-projection-default/tsconfig.app.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [], + "moduleResolution": "bundler" + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/angular/57-content-projection-default/tsconfig.editor.json b/apps/angular/57-content-projection-default/tsconfig.editor.json new file mode 100644 index 000000000..a8ac182c0 --- /dev/null +++ b/apps/angular/57-content-projection-default/tsconfig.editor.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": {}, + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/angular/3-directive-enhancement/tsconfig.json b/apps/angular/57-content-projection-default/tsconfig.json similarity index 92% rename from apps/angular/3-directive-enhancement/tsconfig.json rename to apps/angular/57-content-projection-default/tsconfig.json index b2dbbf22e..1b86db04e 100644 --- a/apps/angular/3-directive-enhancement/tsconfig.json +++ b/apps/angular/57-content-projection-default/tsconfig.json @@ -1,17 +1,7 @@ { - "extends": "../../../tsconfig.base.json", - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.app.json" - }, - { - "path": "./tsconfig.editor.json" - } - ], "compilerOptions": { - "target": "es2020", + "target": "es2022", + "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "noImplicitOverride": true, @@ -19,6 +9,17 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.editor.json" + }, + { + "path": "./tsconfig.app.json" + } + ], + "extends": "../../../tsconfig.base.json", "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, "strictInjectionParameters": true, diff --git a/apps/ngrx/2-effect-vs-selector/.eslintrc.json b/apps/angular/58-content-projection-condition/.eslintrc.json similarity index 100% rename from apps/ngrx/2-effect-vs-selector/.eslintrc.json rename to apps/angular/58-content-projection-condition/.eslintrc.json diff --git a/apps/angular/58-content-projection-condition/README.md b/apps/angular/58-content-projection-condition/README.md new file mode 100644 index 000000000..755bd8854 --- /dev/null +++ b/apps/angular/58-content-projection-condition/README.md @@ -0,0 +1,13 @@ +# Content Projection Condition + +> author: thomas-laforge + +### Run Application + +```bash +npx nx serve angular-content-projection-condition +``` + +### Documentation and Instruction + +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/58-content-projection-condition/). diff --git a/apps/angular/58-content-projection-condition/project.json b/apps/angular/58-content-projection-condition/project.json new file mode 100644 index 000000000..b268984a2 --- /dev/null +++ b/apps/angular/58-content-projection-condition/project.json @@ -0,0 +1,85 @@ +{ + "name": "angular-content-projection-condition", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/angular/58-content-projection-condition/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/58-content-projection-condition", + "index": "apps/angular/58-content-projection-condition/src/index.html", + "browser": "apps/angular/58-content-projection-condition/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/angular/58-content-projection-condition/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + { + "glob": "**/*", + "input": "apps/angular/58-content-projection-condition/public" + } + ], + "styles": [ + "apps/angular/58-content-projection-condition/src/styles.scss" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "4kb", + "maximumError": "8kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-content-projection-condition:build:production" + }, + "development": { + "buildTarget": "angular-content-projection-condition:build:development" + } + }, + "defaultConfiguration": "development", + "continuous": true + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-content-projection-condition:build" + } + }, + "lint": { + "executor": "@nx/eslint:lint" + }, + "serve-static": { + "executor": "@nx/web:file-server", + "options": { + "buildTarget": "angular-content-projection-condition:build", + "staticFilePath": "dist/apps/angular/58-content-projection-condition/browser", + "spa": true + } + } + } +} diff --git a/apps/ngrx/2-effect-vs-selector/src/favicon.ico b/apps/angular/58-content-projection-condition/public/favicon.ico similarity index 100% rename from apps/ngrx/2-effect-vs-selector/src/favicon.ico rename to apps/angular/58-content-projection-condition/public/favicon.ico diff --git a/apps/angular/58-content-projection-condition/src/app/app.component.ts b/apps/angular/58-content-projection-condition/src/app/app.component.ts new file mode 100644 index 000000000..afad56f22 --- /dev/null +++ b/apps/angular/58-content-projection-condition/src/app/app.component.ts @@ -0,0 +1,22 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { CardComponent } from './card.component'; + +@Component({ + imports: [CardComponent], + selector: 'app-root', + template: ` + +
Card 1
+
Message 1
+
+ +
Card 2
+
Message 2
+
+ `, + host: { + class: 'p-4 block flex flex-col gap-1', + }, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AppComponent {} diff --git a/apps/angular/58-content-projection-condition/src/app/app.config.ts b/apps/angular/58-content-projection-condition/src/app/app.config.ts new file mode 100644 index 000000000..034603cfd --- /dev/null +++ b/apps/angular/58-content-projection-condition/src/app/app.config.ts @@ -0,0 +1,5 @@ +import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; + +export const appConfig: ApplicationConfig = { + providers: [provideZoneChangeDetection({ eventCoalescing: true })], +}; diff --git a/apps/angular/58-content-projection-condition/src/app/card.component.ts b/apps/angular/58-content-projection-condition/src/app/card.component.ts new file mode 100644 index 000000000..46925977c --- /dev/null +++ b/apps/angular/58-content-projection-condition/src/app/card.component.ts @@ -0,0 +1,25 @@ +import { ChangeDetectionStrategy, Component, input } from '@angular/core'; + +@Component({ + selector: 'app-card', + template: ` + @if (small()) { + + + } @else { +
+
+ +
+ +
+ } + `, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { + class: 'p-4 border border-grey rounded-sm flex flex-col w-[200px]', + }, +}) +export class CardComponent { + small = input(false); +} diff --git a/apps/angular/58-content-projection-condition/src/index.html b/apps/angular/58-content-projection-condition/src/index.html new file mode 100644 index 000000000..f1768ec71 --- /dev/null +++ b/apps/angular/58-content-projection-condition/src/index.html @@ -0,0 +1,13 @@ + + + + + angular-content-projection-condition + + + + + + + + diff --git a/apps/ngrx/2-effect-vs-selector/src/main.ts b/apps/angular/58-content-projection-condition/src/main.ts similarity index 65% rename from apps/ngrx/2-effect-vs-selector/src/main.ts rename to apps/angular/58-content-projection-condition/src/main.ts index 31c2a3482..f3a7223da 100644 --- a/apps/ngrx/2-effect-vs-selector/src/main.ts +++ b/apps/angular/58-content-projection-condition/src/main.ts @@ -1,6 +1,7 @@ import { bootstrapApplication } from '@angular/platform-browser'; -import { appConfig } from './app/app.config'; - import { AppComponent } from './app/app.component'; +import { appConfig } from './app/app.config'; -bootstrapApplication(AppComponent, appConfig); +bootstrapApplication(AppComponent, appConfig).catch((err) => + console.error(err), +); diff --git a/apps/ngrx/7-power-of-effect/src/styles.scss b/apps/angular/58-content-projection-condition/src/styles.scss similarity index 57% rename from apps/ngrx/7-power-of-effect/src/styles.scss rename to apps/angular/58-content-projection-condition/src/styles.scss index 90d4ee007..77e408aa8 100644 --- a/apps/ngrx/7-power-of-effect/src/styles.scss +++ b/apps/angular/58-content-projection-condition/src/styles.scss @@ -1 +1,5 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + /* You can add global styles to this file, and also import other style files */ diff --git a/apps/angular/58-content-projection-condition/tailwind.config.js b/apps/angular/58-content-projection-condition/tailwind.config.js new file mode 100644 index 000000000..38183db2c --- /dev/null +++ b/apps/angular/58-content-projection-condition/tailwind.config.js @@ -0,0 +1,14 @@ +const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind'); +const { join } = require('path'); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/apps/angular/58-content-projection-condition/tsconfig.app.json b/apps/angular/58-content-projection-condition/tsconfig.app.json new file mode 100644 index 000000000..8b5631268 --- /dev/null +++ b/apps/angular/58-content-projection-condition/tsconfig.app.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [], + "moduleResolution": "bundler" + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/angular/58-content-projection-condition/tsconfig.editor.json b/apps/angular/58-content-projection-condition/tsconfig.editor.json new file mode 100644 index 000000000..a8ac182c0 --- /dev/null +++ b/apps/angular/58-content-projection-condition/tsconfig.editor.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": {}, + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/ngrx/7-power-of-effect/tsconfig.json b/apps/angular/58-content-projection-condition/tsconfig.json similarity index 92% rename from apps/ngrx/7-power-of-effect/tsconfig.json rename to apps/angular/58-content-projection-condition/tsconfig.json index b2dbbf22e..1b86db04e 100644 --- a/apps/ngrx/7-power-of-effect/tsconfig.json +++ b/apps/angular/58-content-projection-condition/tsconfig.json @@ -1,17 +1,7 @@ { - "extends": "../../../tsconfig.base.json", - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.app.json" - }, - { - "path": "./tsconfig.editor.json" - } - ], "compilerOptions": { - "target": "es2020", + "target": "es2022", + "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "noImplicitOverride": true, @@ -19,6 +9,17 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.editor.json" + }, + { + "path": "./tsconfig.app.json" + } + ], + "extends": "../../../tsconfig.base.json", "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, "strictInjectionParameters": true, diff --git a/libs/power-of-effect/model/.eslintrc.json b/apps/angular/59-content-projection-defer/.eslintrc.json similarity index 86% rename from libs/power-of-effect/model/.eslintrc.json rename to apps/angular/59-content-projection-defer/.eslintrc.json index 9247a6e3c..995177b5b 100644 --- a/libs/power-of-effect/model/.eslintrc.json +++ b/apps/angular/59-content-projection-defer/.eslintrc.json @@ -9,11 +9,12 @@ "plugin:@angular-eslint/template/process-inline-templates" ], "rules": { + "@angular-eslint/component-class-suffix": "off", "@angular-eslint/directive-selector": [ "error", { "type": "attribute", - "prefix": "lib", + "prefix": "app", "style": "camelCase" } ], @@ -21,7 +22,7 @@ "error", { "type": "element", - "prefix": "lib", + "prefix": "app", "style": "kebab-case" } ] diff --git a/apps/angular/3-directive-enhancement/README.md b/apps/angular/59-content-projection-defer/README.md similarity index 54% rename from apps/angular/3-directive-enhancement/README.md rename to apps/angular/59-content-projection-defer/README.md index f89eb48bd..f726842d1 100644 --- a/apps/angular/3-directive-enhancement/README.md +++ b/apps/angular/59-content-projection-defer/README.md @@ -1,13 +1,13 @@ -# Directive Enhancement +# content-projection-defer > author: thomas-laforge ### Run Application ```bash -npx nx serve angular-directive-enhancement +npx nx serve angular-content-projection-defer ``` ### Documentation and Instruction -Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/3-directive-enhancement/). +Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/angular/59-content-projection-defer/). diff --git a/apps/angular/59-content-projection-defer/project.json b/apps/angular/59-content-projection-defer/project.json new file mode 100644 index 000000000..efd270b03 --- /dev/null +++ b/apps/angular/59-content-projection-defer/project.json @@ -0,0 +1,83 @@ +{ + "name": "angular-content-projection-defer", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "app", + "sourceRoot": "apps/angular/59-content-projection-defer/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/angular/59-content-projection-defer", + "index": "apps/angular/59-content-projection-defer/src/index.html", + "browser": "apps/angular/59-content-projection-defer/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/angular/59-content-projection-defer/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + { + "glob": "**/*", + "input": "apps/angular/59-content-projection-defer/public" + } + ], + "styles": ["apps/angular/59-content-projection-defer/src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "4kb", + "maximumError": "8kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "angular-content-projection-defer:build:production" + }, + "development": { + "buildTarget": "angular-content-projection-defer:build:development" + } + }, + "defaultConfiguration": "development", + "continuous": true + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "buildTarget": "angular-content-projection-defer:build" + } + }, + "lint": { + "executor": "@nx/eslint:lint" + }, + "serve-static": { + "executor": "@nx/web:file-server", + "options": { + "buildTarget": "angular-content-projection-defer:build", + "staticFilePath": "dist/apps/angular/59-content-projection-defer/browser", + "spa": true + } + } + } +} diff --git a/apps/ngrx/7-power-of-effect/src/favicon.ico b/apps/angular/59-content-projection-defer/public/favicon.ico similarity index 100% rename from apps/ngrx/7-power-of-effect/src/favicon.ico rename to apps/angular/59-content-projection-defer/public/favicon.ico diff --git a/apps/angular/59-content-projection-defer/src/app/app.component.ts b/apps/angular/59-content-projection-defer/src/app/app.component.ts new file mode 100644 index 000000000..ae40bc880 --- /dev/null +++ b/apps/angular/59-content-projection-defer/src/app/app.component.ts @@ -0,0 +1,23 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { RouterLink, RouterOutlet } from '@angular/router'; + +@Component({ + imports: [RouterOutlet, RouterLink], + selector: 'app-root', + template: ` +
+ + +
+ + `, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { + class: 'flex flex-col gap-2 ', + }, +}) +export class AppComponent {} diff --git a/apps/angular/59-content-projection-defer/src/app/app.config.ts b/apps/angular/59-content-projection-defer/src/app/app.config.ts new file mode 100644 index 000000000..faf4d099a --- /dev/null +++ b/apps/angular/59-content-projection-defer/src/app/app.config.ts @@ -0,0 +1,12 @@ +import { provideHttpClient } from '@angular/common/http'; +import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; +import { provideRouter } from '@angular/router'; +import { appRoutes } from './app.routes'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideZoneChangeDetection({ eventCoalescing: true }), + provideRouter(appRoutes), + provideHttpClient(), + ], +}; diff --git a/apps/angular/59-content-projection-defer/src/app/app.routes.ts b/apps/angular/59-content-projection-defer/src/app/app.routes.ts new file mode 100644 index 000000000..3ca1b67cc --- /dev/null +++ b/apps/angular/59-content-projection-defer/src/app/app.routes.ts @@ -0,0 +1,13 @@ +import { Route } from '@angular/router'; + +export const appRoutes: Route[] = [ + { + path: 'page-1', + loadComponent: () => import('./page-1').then((m) => m.Page1), + }, + { + path: 'page-2', + loadComponent: () => import('./page-2').then((m) => m.Page2), + }, + { path: '**', redirectTo: 'page-1' }, +]; diff --git a/apps/angular/59-content-projection-defer/src/app/expandable-card.ts b/apps/angular/59-content-projection-defer/src/app/expandable-card.ts new file mode 100644 index 000000000..8f446ed80 --- /dev/null +++ b/apps/angular/59-content-projection-defer/src/app/expandable-card.ts @@ -0,0 +1,54 @@ +import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; + +@Component({ + selector: 'app-expandable-card', + template: ` + + +
+ +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { + class: 'flex flex-col gap-2 ', + }, +}) +export class ExpandableCard { + public isExpanded = signal(false); +} diff --git a/apps/angular/59-content-projection-defer/src/app/page-1.ts b/apps/angular/59-content-projection-defer/src/app/page-1.ts new file mode 100644 index 000000000..868d76959 --- /dev/null +++ b/apps/angular/59-content-projection-defer/src/app/page-1.ts @@ -0,0 +1,10 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; + +@Component({ + selector: 'app-page-1', + template: ` + page1 + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class Page1 {} diff --git a/apps/angular/59-content-projection-defer/src/app/page-2.ts b/apps/angular/59-content-projection-defer/src/app/page-2.ts new file mode 100644 index 000000000..5665466d8 --- /dev/null +++ b/apps/angular/59-content-projection-defer/src/app/page-2.ts @@ -0,0 +1,43 @@ +import { httpResource } from '@angular/common/http'; +import { + ChangeDetectionStrategy, + Component, + ResourceStatus, +} from '@angular/core'; +import { ExpandableCard } from './expandable-card'; + +interface Post { + id: number; + title: string; + body: string; + userId: number; +} + +@Component({ + selector: 'app-page-2', + template: ` + page2 + +
Load Post
+
+ @if (postResource.isLoading()) { + Loading... + } @else if (postResource.status() === ResourceStatus.Error) { + Error... + } @else { + @for (post of postResource.value(); track post.id) { +
{{ post.title }}
+ } + } +
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ExpandableCard], +}) +export class Page2 { + public postResource = httpResource( + 'https://jsonplaceholder.typicode.com/posts', + ); + protected readonly ResourceStatus = ResourceStatus; +} diff --git a/apps/angular/3-directive-enhancement/src/index.html b/apps/angular/59-content-projection-defer/src/index.html similarity index 84% rename from apps/angular/3-directive-enhancement/src/index.html rename to apps/angular/59-content-projection-defer/src/index.html index 28def4ef2..79f435fa6 100644 --- a/apps/angular/3-directive-enhancement/src/index.html +++ b/apps/angular/59-content-projection-defer/src/index.html @@ -2,7 +2,7 @@ - angular-directive-enhancement + angular-content-projection-defer diff --git a/apps/angular/59-content-projection-defer/src/main.ts b/apps/angular/59-content-projection-defer/src/main.ts new file mode 100644 index 000000000..f3a7223da --- /dev/null +++ b/apps/angular/59-content-projection-defer/src/main.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; +import { appConfig } from './app/app.config'; + +bootstrapApplication(AppComponent, appConfig).catch((err) => + console.error(err), +); diff --git a/apps/angular/59-content-projection-defer/src/styles.scss b/apps/angular/59-content-projection-defer/src/styles.scss new file mode 100644 index 000000000..77e408aa8 --- /dev/null +++ b/apps/angular/59-content-projection-defer/src/styles.scss @@ -0,0 +1,5 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* You can add global styles to this file, and also import other style files */ diff --git a/apps/angular/59-content-projection-defer/tailwind.config.js b/apps/angular/59-content-projection-defer/tailwind.config.js new file mode 100644 index 000000000..38183db2c --- /dev/null +++ b/apps/angular/59-content-projection-defer/tailwind.config.js @@ -0,0 +1,14 @@ +const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind'); +const { join } = require('path'); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/apps/angular/59-content-projection-defer/tsconfig.app.json b/apps/angular/59-content-projection-defer/tsconfig.app.json new file mode 100644 index 000000000..8b5631268 --- /dev/null +++ b/apps/angular/59-content-projection-defer/tsconfig.app.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [], + "moduleResolution": "bundler" + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/angular/59-content-projection-defer/tsconfig.editor.json b/apps/angular/59-content-projection-defer/tsconfig.editor.json new file mode 100644 index 000000000..a8ac182c0 --- /dev/null +++ b/apps/angular/59-content-projection-defer/tsconfig.editor.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": {}, + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/ngrx/2-effect-vs-selector/tsconfig.json b/apps/angular/59-content-projection-defer/tsconfig.json similarity index 89% rename from apps/ngrx/2-effect-vs-selector/tsconfig.json rename to apps/angular/59-content-projection-defer/tsconfig.json index 52eb4f718..1b86db04e 100644 --- a/apps/ngrx/2-effect-vs-selector/tsconfig.json +++ b/apps/angular/59-content-projection-defer/tsconfig.json @@ -1,20 +1,7 @@ { - "extends": "../../../tsconfig.base.json", - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.app.json" - }, - { - "path": "./tsconfig.spec.json" - }, - { - "path": "./tsconfig.editor.json" - } - ], "compilerOptions": { - "target": "es2020", + "target": "es2022", + "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "noImplicitOverride": true, @@ -22,6 +9,17 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.editor.json" + }, + { + "path": "./tsconfig.app.json" + } + ], + "extends": "../../../tsconfig.base.json", "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, "strictInjectionParameters": true, diff --git a/apps/angular/6-structural-directive/project.json b/apps/angular/6-structural-directive/project.json index f803b0cb5..399418f90 100644 --- a/apps/angular/6-structural-directive/project.json +++ b/apps/angular/6-structural-directive/project.json @@ -60,7 +60,8 @@ "buildTarget": "angular-structural-directive:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", diff --git a/apps/angular/6-structural-directive/src/app/app.component.ts b/apps/angular/6-structural-directive/src/app/app.component.ts index d89a2f579..1fcb0c548 100644 --- a/apps/angular/6-structural-directive/src/app/app.component.ts +++ b/apps/angular/6-structural-directive/src/app/app.component.ts @@ -1,13 +1,12 @@ -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; @Component({ - standalone: true, imports: [RouterOutlet], selector: 'app-root', template: ` - + `, - styles: [], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class AppComponent {} diff --git a/apps/angular/6-structural-directive/src/app/button.component.ts b/apps/angular/6-structural-directive/src/app/button.component.ts index b1d59e8cc..5d1323605 100644 --- a/apps/angular/6-structural-directive/src/app/button.component.ts +++ b/apps/angular/6-structural-directive/src/app/button.component.ts @@ -2,10 +2,9 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; @Component({ - standalone: true, selector: 'button[app-button]', template: ` - + `, host: { class: 'border border-blue-700 bg-blue-400 p-2 rounded-sm text-white', diff --git a/apps/angular/6-structural-directive/src/app/dashboard/admin.component.ts b/apps/angular/6-structural-directive/src/app/dashboard/admin.component.ts index 72e4b3bef..26bb23284 100644 --- a/apps/angular/6-structural-directive/src/app/dashboard/admin.component.ts +++ b/apps/angular/6-structural-directive/src/app/dashboard/admin.component.ts @@ -4,7 +4,6 @@ import { ButtonComponent } from '../button.component'; @Component({ selector: 'app-dashboard', - standalone: true, imports: [RouterLink, ButtonComponent], template: `

dashboard for Admin works!

diff --git a/apps/angular/6-structural-directive/src/app/dashboard/manager.component.ts b/apps/angular/6-structural-directive/src/app/dashboard/manager.component.ts index b92fc7925..60ea7695b 100644 --- a/apps/angular/6-structural-directive/src/app/dashboard/manager.component.ts +++ b/apps/angular/6-structural-directive/src/app/dashboard/manager.component.ts @@ -1,11 +1,8 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { RouterLink } from '@angular/router'; -import { ButtonComponent } from '../button.component'; @Component({ selector: 'app-dashboard', - standalone: true, - imports: [RouterLink, ButtonComponent], + imports: [], template: `

dashboard for Manager works!

diff --git a/apps/angular/6-structural-directive/src/app/information.component.ts b/apps/angular/6-structural-directive/src/app/information.component.ts index e4adeb1b9..ecf937efc 100644 --- a/apps/angular/6-structural-directive/src/app/information.component.ts +++ b/apps/angular/6-structural-directive/src/app/information.component.ts @@ -1,11 +1,8 @@ -import { CommonModule } from '@angular/common'; -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { UserStore } from './user.store'; @Component({ selector: 'app-information', - standalone: true, - imports: [CommonModule], template: `

Information Panel

@@ -19,6 +16,7 @@ import { UserStore } from './user.store'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class InformationComponent { + private readonly userStore = inject(UserStore); + user$ = this.userStore.user$; - constructor(private userStore: UserStore) {} } diff --git a/apps/angular/6-structural-directive/src/app/login.component.ts b/apps/angular/6-structural-directive/src/app/login.component.ts index cd36d9603..f38e5e5ca 100644 --- a/apps/angular/6-structural-directive/src/app/login.component.ts +++ b/apps/angular/6-structural-directive/src/app/login.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { RouterLink } from '@angular/router'; import { ButtonComponent } from './button.component'; import { InformationComponent } from './information.component'; @@ -14,7 +14,6 @@ import { import { UserStore } from './user.store'; @Component({ - standalone: true, imports: [InformationComponent, RouterLink, ButtonComponent], selector: 'app-login', template: ` @@ -29,7 +28,7 @@ import { UserStore } from './user.store'; - + +

Page A (Admin)

+ `, + + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [RouterLink], +}) +export class AdminPage {} diff --git a/apps/angular/60-async-redirect/src/app/app.config.ts b/apps/angular/60-async-redirect/src/app/app.config.ts new file mode 100644 index 000000000..76220a9c0 --- /dev/null +++ b/apps/angular/60-async-redirect/src/app/app.config.ts @@ -0,0 +1,16 @@ +import { + ApplicationConfig, + provideBrowserGlobalErrorListeners, + provideZoneChangeDetection, +} from '@angular/core'; +import { provideRouter } from '@angular/router'; + +import { routes } from './routes'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideBrowserGlobalErrorListeners(), + provideZoneChangeDetection({ eventCoalescing: true }), + provideRouter(routes), + ], +}; diff --git a/apps/angular/60-async-redirect/src/app/app.ts b/apps/angular/60-async-redirect/src/app/app.ts new file mode 100644 index 000000000..aa29b8e76 --- /dev/null +++ b/apps/angular/60-async-redirect/src/app/app.ts @@ -0,0 +1,12 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; + +@Component({ + selector: 'app-root', + template: ` + + `, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [RouterOutlet], +}) +export class App {} diff --git a/apps/angular/60-async-redirect/src/app/dashboard.ts b/apps/angular/60-async-redirect/src/app/dashboard.ts new file mode 100644 index 000000000..377c5e81d --- /dev/null +++ b/apps/angular/60-async-redirect/src/app/dashboard.ts @@ -0,0 +1,34 @@ +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { Router, RouterLink, RouterOutlet } from '@angular/router'; +import { UserProfileService } from './user-profile.service'; + +@Component({ + selector: 'app-dashboard', + template: ` +
+ + +
+ + `, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [RouterOutlet, RouterLink], +}) +export class Dashboard { + private router = inject(Router); + private userProfile = inject(UserProfileService); + + navigate() { + this.userProfile.getProfile().subscribe((profile) => { + void this.router.navigate(['/', profile]); + }); + } +} diff --git a/apps/angular/60-async-redirect/src/app/profile-page.ts b/apps/angular/60-async-redirect/src/app/profile-page.ts new file mode 100644 index 000000000..a92779d07 --- /dev/null +++ b/apps/angular/60-async-redirect/src/app/profile-page.ts @@ -0,0 +1,45 @@ +import { + ChangeDetectionStrategy, + Component, + inject, + signal, +} from '@angular/core'; +import { RouterLink } from '@angular/router'; +import { UserProfileService } from './user-profile.service'; + +@Component({ + selector: 'app-profile-page', + template: ` + +
+ + +
+
+ Current profile: {{ selectedProfile() }} +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [RouterLink], +}) +export class ProfilePage { + private userProfile = inject(UserProfileService); + selectedProfile = signal<'admin' | 'user'>('admin'); + + chooseProfile(profile: 'admin' | 'user') { + this.userProfile.setProfile(profile); + this.selectedProfile.set(profile); + } +} diff --git a/apps/angular/60-async-redirect/src/app/routes.ts b/apps/angular/60-async-redirect/src/app/routes.ts new file mode 100644 index 000000000..4a0f11835 --- /dev/null +++ b/apps/angular/60-async-redirect/src/app/routes.ts @@ -0,0 +1,20 @@ +import { Routes } from '@angular/router'; +import { AdminPage } from './admin-page'; +import { App } from './app'; +import { Dashboard } from './dashboard'; +import { ProfilePage } from './profile-page'; +import { UserPage } from './user-page'; + +export const routes: Routes = [ + { + path: '', + component: App, + children: [ + { path: '', pathMatch: 'full', component: Dashboard }, + { path: 'profile', component: ProfilePage }, + { path: 'admin', component: AdminPage }, + { path: 'user', component: UserPage }, + ], + }, + { path: '**', redirectTo: '', pathMatch: 'full' }, +]; diff --git a/apps/angular/60-async-redirect/src/app/user-page.ts b/apps/angular/60-async-redirect/src/app/user-page.ts new file mode 100644 index 000000000..fdb159bac --- /dev/null +++ b/apps/angular/60-async-redirect/src/app/user-page.ts @@ -0,0 +1,17 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { RouterLink } from '@angular/router'; + +@Component({ + selector: 'app-page-b', + template: ` + +

User page

+ `, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [RouterLink], +}) +export class UserPage {} diff --git a/apps/angular/60-async-redirect/src/app/user-profile.service.ts b/apps/angular/60-async-redirect/src/app/user-profile.service.ts new file mode 100644 index 000000000..b13f611e7 --- /dev/null +++ b/apps/angular/60-async-redirect/src/app/user-profile.service.ts @@ -0,0 +1,16 @@ +import { Injectable, signal } from '@angular/core'; +import { Observable, of } from 'rxjs'; +import { delay } from 'rxjs/operators'; + +@Injectable({ providedIn: 'root' }) +export class UserProfileService { + private profile = signal<'admin' | 'user'>('admin'); + + setProfile(profile: 'admin' | 'user') { + this.profile.set(profile); + } + + getProfile(): Observable<'admin' | 'user'> { + return of(this.profile()).pipe(delay(300)); + } +} diff --git a/apps/ngrx/7-power-of-effect/src/index.html b/apps/angular/60-async-redirect/src/index.html similarity index 87% rename from apps/ngrx/7-power-of-effect/src/index.html rename to apps/angular/60-async-redirect/src/index.html index 91cf31032..e81550c8d 100644 --- a/apps/ngrx/7-power-of-effect/src/index.html +++ b/apps/angular/60-async-redirect/src/index.html @@ -2,7 +2,7 @@ - ngrx-power-of-effect + angular-async-redirect diff --git a/apps/angular/60-async-redirect/src/main.ts b/apps/angular/60-async-redirect/src/main.ts new file mode 100644 index 000000000..2108cd95d --- /dev/null +++ b/apps/angular/60-async-redirect/src/main.ts @@ -0,0 +1,5 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { App } from './app/app'; +import { appConfig } from './app/app.config'; + +bootstrapApplication(App, appConfig).catch((err) => console.error(err)); diff --git a/apps/angular/60-async-redirect/src/styles.scss b/apps/angular/60-async-redirect/src/styles.scss new file mode 100644 index 000000000..77e408aa8 --- /dev/null +++ b/apps/angular/60-async-redirect/src/styles.scss @@ -0,0 +1,5 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* You can add global styles to this file, and also import other style files */ diff --git a/apps/angular/60-async-redirect/tailwind.config.js b/apps/angular/60-async-redirect/tailwind.config.js new file mode 100644 index 000000000..38183db2c --- /dev/null +++ b/apps/angular/60-async-redirect/tailwind.config.js @@ -0,0 +1,14 @@ +const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind'); +const { join } = require('path'); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'), + ...createGlobPatternsForDependencies(__dirname), + ], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/libs/power-of-effect/backend/tsconfig.lib.json b/apps/angular/60-async-redirect/tsconfig.app.json similarity index 54% rename from libs/power-of-effect/backend/tsconfig.lib.json rename to apps/angular/60-async-redirect/tsconfig.app.json index dd189dc2e..5d5af52c7 100644 --- a/libs/power-of-effect/backend/tsconfig.lib.json +++ b/apps/angular/60-async-redirect/tsconfig.app.json @@ -2,16 +2,13 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../dist/out-tsc", - "declaration": true, - "declarationMap": true, - "inlineSources": true, "types": [] }, + "include": ["src/**/*.ts"], "exclude": [ - "src/test-setup.ts", - "**/*.spec.ts", "jest.config.ts", - "**/*.test.ts" - ], - "include": ["**/*.ts"] + "src/test-setup.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts" + ] } diff --git a/libs/shared/ngrx-callstate-store/tsconfig.json b/apps/angular/60-async-redirect/tsconfig.json similarity index 63% rename from libs/shared/ngrx-callstate-store/tsconfig.json rename to apps/angular/60-async-redirect/tsconfig.json index 888e0b0f1..e70623587 100644 --- a/libs/shared/ngrx-callstate-store/tsconfig.json +++ b/apps/angular/60-async-redirect/tsconfig.json @@ -1,32 +1,29 @@ { "extends": "../../../tsconfig.base.json", - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.lib.json" - }, - { - "path": "./tsconfig.lib.prod.json" - }, - { - "path": "./tsconfig.spec.json" - } - ], "compilerOptions": { - "target": "es2022", - "useDefineForClassFields": false, - "forceConsistentCasingInFileNames": true, "strict": true, "noImplicitOverride": true, "noPropertyAccessFromIndexSignature": true, "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true + "noFallthroughCasesInSwitch": true, + "target": "es2022", + "moduleResolution": "bundler", + "isolatedModules": true, + "emitDecoratorMetadata": false, + "module": "preserve" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, "strictInjectionParameters": true, "strictInputAccessModifiers": true, + "typeCheckHostBindings": true, "strictTemplates": true - } + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + } + ] } diff --git a/apps/angular/8-pure-pipe/project.json b/apps/angular/8-pure-pipe/project.json index a684d542f..26f507b63 100644 --- a/apps/angular/8-pure-pipe/project.json +++ b/apps/angular/8-pure-pipe/project.json @@ -60,7 +60,8 @@ "buildTarget": "angular-pure-pipe:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", diff --git a/apps/angular/8-pure-pipe/src/app/app.component.ts b/apps/angular/8-pure-pipe/src/app/app.component.ts index 3c19fa169..930fe1313 100644 --- a/apps/angular/8-pure-pipe/src/app/app.component.ts +++ b/apps/angular/8-pure-pipe/src/app/app.component.ts @@ -1,14 +1,11 @@ -import { NgFor } from '@angular/common'; import { Component } from '@angular/core'; @Component({ - standalone: true, - imports: [NgFor], selector: 'app-root', template: ` -
- {{ heavyComputation(person, index) }} -
+ @for (person of persons; track person) { + {{ heavyComputation(person, $index) }} + } `, }) export class AppComponent { diff --git a/apps/angular/8-pure-pipe/tsconfig.app.json b/apps/angular/8-pure-pipe/tsconfig.app.json index 7a4dbc47e..2a1ca1b8d 100644 --- a/apps/angular/8-pure-pipe/tsconfig.app.json +++ b/apps/angular/8-pure-pipe/tsconfig.app.json @@ -4,7 +4,8 @@ "outDir": "../../../dist/out-tsc", "types": [], "target": "ES2022", - "useDefineForClassFields": false + "useDefineForClassFields": false, + "moduleResolution": "bundler" }, "files": ["src/main.ts", "src/polyfills.ts"], "include": ["src/**/*.d.ts"], diff --git a/apps/angular/9-wrap-function-pipe/project.json b/apps/angular/9-wrap-function-pipe/project.json index dc5c4ab6e..ce32ac0c1 100644 --- a/apps/angular/9-wrap-function-pipe/project.json +++ b/apps/angular/9-wrap-function-pipe/project.json @@ -60,7 +60,8 @@ "buildTarget": "angular-wrap-function-pipe:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", diff --git a/apps/angular/9-wrap-function-pipe/src/app/app.component.ts b/apps/angular/9-wrap-function-pipe/src/app/app.component.ts index d9c163c93..af8b6ff73 100644 --- a/apps/angular/9-wrap-function-pipe/src/app/app.component.ts +++ b/apps/angular/9-wrap-function-pipe/src/app/app.component.ts @@ -1,15 +1,12 @@ -import { NgFor } from '@angular/common'; import { Component } from '@angular/core'; @Component({ - standalone: true, - imports: [NgFor], selector: 'app-root', template: ` -
- {{ showName(person.name, index) }} - {{ isAllowed(person.age, isFirst) }} -
+ @for (person of persons; track person.name) { + {{ showName(person.name, $index) }} + {{ isAllowed(person.age, $first) }} + } `, }) export class AppComponent { diff --git a/apps/angular/9-wrap-function-pipe/tsconfig.app.json b/apps/angular/9-wrap-function-pipe/tsconfig.app.json index 7a4dbc47e..2a1ca1b8d 100644 --- a/apps/angular/9-wrap-function-pipe/tsconfig.app.json +++ b/apps/angular/9-wrap-function-pipe/tsconfig.app.json @@ -4,7 +4,8 @@ "outDir": "../../../dist/out-tsc", "types": [], "target": "ES2022", - "useDefineForClassFields": false + "useDefineForClassFields": false, + "moduleResolution": "bundler" }, "files": ["src/main.ts", "src/polyfills.ts"], "include": ["src/**/*.d.ts"], diff --git a/apps/forms/41-control-value-accessor/project.json b/apps/forms/41-control-value-accessor/project.json index d958888de..e9012bd49 100644 --- a/apps/forms/41-control-value-accessor/project.json +++ b/apps/forms/41-control-value-accessor/project.json @@ -57,7 +57,8 @@ "buildTarget": "forms-control-value-accessor:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", @@ -66,10 +67,14 @@ } }, "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], "options": { - "jestConfig": "apps/forms/41-control-value-accessor/jest.config.ts" + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "coverage": true + } } } } diff --git a/apps/forms/41-control-value-accessor/src/app/app.component.ts b/apps/forms/41-control-value-accessor/src/app/app.component.ts index f56b5d7d9..69134b864 100644 --- a/apps/forms/41-control-value-accessor/src/app/app.component.ts +++ b/apps/forms/41-control-value-accessor/src/app/app.component.ts @@ -2,7 +2,6 @@ import { Component } from '@angular/core'; import { FeedbackFormComponent } from './feedback-form/feedback-form.component'; @Component({ - standalone: true, imports: [FeedbackFormComponent], selector: 'app-root', template: ` diff --git a/apps/forms/41-control-value-accessor/src/app/feedback-form/feedback-form.component.ts b/apps/forms/41-control-value-accessor/src/app/feedback-form/feedback-form.component.ts index d99700db1..4110d6cf7 100644 --- a/apps/forms/41-control-value-accessor/src/app/feedback-form/feedback-form.component.ts +++ b/apps/forms/41-control-value-accessor/src/app/feedback-form/feedback-form.component.ts @@ -8,7 +8,6 @@ import { import { RatingControlComponent } from '../rating-control/rating-control.component'; @Component({ - standalone: true, imports: [RatingControlComponent, ReactiveFormsModule], selector: 'app-feedback-form', templateUrl: 'feedback-form.component.html', diff --git a/apps/forms/41-control-value-accessor/src/app/rating-control/rating-control.component.ts b/apps/forms/41-control-value-accessor/src/app/rating-control/rating-control.component.ts index d6dc31631..5562d1f99 100644 --- a/apps/forms/41-control-value-accessor/src/app/rating-control/rating-control.component.ts +++ b/apps/forms/41-control-value-accessor/src/app/rating-control/rating-control.component.ts @@ -1,7 +1,6 @@ import { Component, EventEmitter, Output } from '@angular/core'; @Component({ - standalone: true, selector: 'app-rating-control', templateUrl: 'rating-control.component.html', styleUrls: ['rating-control.component.scss'], diff --git a/apps/forms/41-control-value-accessor/tsconfig.app.json b/apps/forms/41-control-value-accessor/tsconfig.app.json index 58220429a..8b5631268 100644 --- a/apps/forms/41-control-value-accessor/tsconfig.app.json +++ b/apps/forms/41-control-value-accessor/tsconfig.app.json @@ -2,7 +2,8 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../dist/out-tsc", - "types": [] + "types": [], + "moduleResolution": "bundler" }, "files": ["src/main.ts"], "include": ["src/**/*.d.ts"], diff --git a/apps/forms/48-avoid-losing-form-data/project.json b/apps/forms/48-avoid-losing-form-data/project.json index 9517d3123..5bfe98931 100644 --- a/apps/forms/48-avoid-losing-form-data/project.json +++ b/apps/forms/48-avoid-losing-form-data/project.json @@ -60,7 +60,8 @@ "buildTarget": "forms-avoid-losing-form-data:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", diff --git a/apps/forms/48-avoid-losing-form-data/src/app/app.component.ts b/apps/forms/48-avoid-losing-form-data/src/app/app.component.ts index 6ac465e12..2b5adc443 100644 --- a/apps/forms/48-avoid-losing-form-data/src/app/app.component.ts +++ b/apps/forms/48-avoid-losing-form-data/src/app/app.component.ts @@ -3,7 +3,6 @@ import { RouterOutlet } from '@angular/router'; import { NavComponent } from './ui/nav.component'; @Component({ - standalone: true, imports: [RouterOutlet, NavComponent], selector: 'app-root', template: ` diff --git a/apps/forms/48-avoid-losing-form-data/src/app/pages/join.component.ts b/apps/forms/48-avoid-losing-form-data/src/app/pages/join.component.ts index 6f5fea9af..51449a7fb 100644 --- a/apps/forms/48-avoid-losing-form-data/src/app/pages/join.component.ts +++ b/apps/forms/48-avoid-losing-form-data/src/app/pages/join.component.ts @@ -2,7 +2,6 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; import { FormComponent } from '../ui/form.component'; @Component({ - standalone: true, imports: [FormComponent], template: `
diff --git a/apps/forms/48-avoid-losing-form-data/src/app/pages/page.component.ts b/apps/forms/48-avoid-losing-form-data/src/app/pages/page.component.ts index 13f4e09c2..a1995eff1 100644 --- a/apps/forms/48-avoid-losing-form-data/src/app/pages/page.component.ts +++ b/apps/forms/48-avoid-losing-form-data/src/app/pages/page.component.ts @@ -1,7 +1,6 @@ import { ChangeDetectionStrategy, Component, input } from '@angular/core'; @Component({ - standalone: true, template: `

{{ title() }}

diff --git a/apps/forms/48-avoid-losing-form-data/src/app/ui/dialog.component.ts b/apps/forms/48-avoid-losing-form-data/src/app/ui/dialog.component.ts index 661da9bcf..21db4d56f 100644 --- a/apps/forms/48-avoid-losing-form-data/src/app/ui/dialog.component.ts +++ b/apps/forms/48-avoid-losing-form-data/src/app/ui/dialog.component.ts @@ -3,7 +3,6 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; // NOTE : this is just the dialog content, you need to implement dialog logic @Component({ - standalone: true, template: `