diff --git a/.all-contributorsrc b/.all-contributorsrc index 6f2f57401..6e8d23087 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -363,6 +363,15 @@ "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/.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/.gitignore b/.gitignore index cad0abc01..0647ff22f 100644 --- a/.gitignore +++ b/.gitignore @@ -43,4 +43,6 @@ TODO.md .nx/cache .nx/workspace-data -.cursorrules \ No newline at end of file +.cursorrules +.cursor/rules/nx-rules.mdc +.github/instructions/nx.instructions.md diff --git a/README.md b/README.md index 2965ecc08..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 59 challenges](https://angular-challenges.vercel.app/) +Check [all 60 challenges](https://angular-challenges.vercel.app/) ## Contributors ✨ @@ -76,6 +76,7 @@ Check [all 59 challenges](https://angular-challenges.vercel.app/) 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 d40912c35..42ed1604f 100644 --- a/apps/angular/1-projection/project.json +++ b/apps/angular/1-projection/project.json @@ -61,7 +61,8 @@ "buildTarget": "angular-projection:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", 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 cffabb451..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 @@ -18,7 +18,6 @@ import { CardType } from '../../model/card.model'; `, - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, }) export class ListItemComponent { 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/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/static-text.component.ts b/apps/angular/13-highly-customizable-css/src/app/static-text.component.ts index 70d57d9a3..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,5 +1,5 @@ /* 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'; @@ -8,25 +8,31 @@ export type StaticTextType = 'normal' | 'warning' | 'error'; selector: 'static-text', 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 5bb91c2b2..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,16 +22,18 @@ export class ProductDirective { } @Component({ - imports: [TableComponent, CurrencyPipe, AsyncPipe, NgFor, ProductDirective], + imports: [TableComponent, CurrencyPipe, AsyncPipe, ProductDirective], providers: [CurrencyService], selector: 'app-root', template: ` - + @for (col of displayedColumns; track $index) { + + } @@ -45,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 b8ba74b04..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", 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/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/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/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 48eb9ef1e..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", 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 bc32fbaa0..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 } from '@angular/common'; -import { Component, inject, Input } 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'; @@ -12,7 +12,7 @@ interface MenuItem { selector: 'app-nav', imports: [RouterLink, RouterLinkActive], template: ` - @for (menu of menus; track menu.path) { + @for (menu of menus(); track menu.path) { (); } @Component({ - imports: [NavigationComponent, AsyncPipe], + imports: [NavigationComponent], template: ` - @if (info$ | async; as info) { - @if (info !== null) { - - } @else { - - } + @if (info() !== null) { + + } @else { + } `, host: {}, @@ -52,7 +50,9 @@ export class NavigationComponent { 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/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 8dba72d55..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", 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/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 23be9dac6..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,10 +1,9 @@ -import { NgTemplateOutlet } from '@angular/common'; import { ChangeDetectionStrategy, Component } from '@angular/core'; import { ListComponent } from './list.component'; import { PersonComponent } from './person.component'; @Component({ - 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 b9946e428..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,31 +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', - 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 59eb00ab1..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,10 +1,5 @@ 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({ imports: [NgTemplateOutlet], @@ -12,16 +7,15 @@ interface 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/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 bce6fe70e..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", 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 1d00fd246..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", 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 cdad9374a..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", 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 b756e8074..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,10 +1,9 @@ -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({ - imports: [CommonModule], + imports: [], selector: 'app-root', template: ` @for (todo of todos; track todo.id) { @@ -15,9 +14,9 @@ import { randText } from '@ngneat/falso'; 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 d3b28088a..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", 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 f53d65e27..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", 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/57-content-projection-default/project.json b/apps/angular/57-content-projection-default/project.json index a57738bff..0630925f4 100644 --- a/apps/angular/57-content-projection-default/project.json +++ b/apps/angular/57-content-projection-default/project.json @@ -61,7 +61,8 @@ "buildTarget": "angular-content-projection-default:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", diff --git a/apps/angular/57-content-projection-default/tsconfig.app.json b/apps/angular/57-content-projection-default/tsconfig.app.json index 58220429a..8b5631268 100644 --- a/apps/angular/57-content-projection-default/tsconfig.app.json +++ b/apps/angular/57-content-projection-default/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/58-content-projection-condition/project.json b/apps/angular/58-content-projection-condition/project.json index 42869e68b..b268984a2 100644 --- a/apps/angular/58-content-projection-condition/project.json +++ b/apps/angular/58-content-projection-condition/project.json @@ -61,7 +61,8 @@ "buildTarget": "angular-content-projection-condition:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", diff --git a/apps/angular/58-content-projection-condition/tsconfig.app.json b/apps/angular/58-content-projection-condition/tsconfig.app.json index 58220429a..8b5631268 100644 --- a/apps/angular/58-content-projection-condition/tsconfig.app.json +++ b/apps/angular/58-content-projection-condition/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/59-content-projection-defer/project.json b/apps/angular/59-content-projection-defer/project.json index 5572ab573..efd270b03 100644 --- a/apps/angular/59-content-projection-defer/project.json +++ b/apps/angular/59-content-projection-defer/project.json @@ -59,7 +59,8 @@ "buildTarget": "angular-content-projection-defer:build:development" } }, - "defaultConfiguration": "development" + "defaultConfiguration": "development", + "continuous": true }, "extract-i18n": { "executor": "@angular-devkit/build-angular:extract-i18n", diff --git a/apps/angular/59-content-projection-defer/tsconfig.app.json b/apps/angular/59-content-projection-defer/tsconfig.app.json index 58220429a..8b5631268 100644 --- a/apps/angular/59-content-projection-defer/tsconfig.app.json +++ b/apps/angular/59-content-projection-defer/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/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 a3d63b374..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,12 +1,12 @@ -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; @Component({ 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/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 81b339520..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,10 +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', - imports: [CommonModule], template: `

Information Panel

@@ -18,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 b8644ed38..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'; @@ -28,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/angular/60-async-redirect/src/index.html b/apps/angular/60-async-redirect/src/index.html new file mode 100644 index 000000000..e81550c8d --- /dev/null +++ b/apps/angular/60-async-redirect/src/index.html @@ -0,0 +1,13 @@ + + + + + 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/apps/angular/60-async-redirect/tsconfig.app.json b/apps/angular/60-async-redirect/tsconfig.app.json new file mode 100644 index 000000000..5d5af52c7 --- /dev/null +++ b/apps/angular/60-async-redirect/tsconfig.app.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [] + }, + "include": ["src/**/*.ts"], + "exclude": [ + "jest.config.ts", + "src/test-setup.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts" + ] +} diff --git a/apps/angular/60-async-redirect/tsconfig.json b/apps/angular/60-async-redirect/tsconfig.json new file mode 100644 index 000000000..e70623587 --- /dev/null +++ b/apps/angular/60-async-redirect/tsconfig.json @@ -0,0 +1,29 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": 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/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/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 a1ec26bac..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", 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/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: `
} - + }
@@ -50,15 +50,17 @@ import { Ticket, TicketUser, User } from '../../backend.service'; Assign to - - {{ user.name }} - + @for (user of users(); track user.id) { + + {{ user.name }} + + }