diff --git a/README.md b/README.md index d5bd3ce1..1c8dbf1f 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ How do we solve this ? Developers love having framework overview by examples. It Angular - + - [x] Reactivity - [x] Declare state @@ -143,12 +143,12 @@ How do we solve this ? Developers love having framework overview by examples. It - [x] Lifecycle - [x] On mount - [x] On unmount -- [ ] Component composition +- [x] Component composition - [x] Props - [x] Emit to parent - [x] Slot - [x] Slot fallback - - [ ] Context + - [x] Context - [x] Form input - [x] Input text - [x] Checkbox @@ -550,7 +550,7 @@ How do we solve this ? Developers love having framework overview by examples. It - [x] Lifecycle - [x] On mount - [x] On unmount -- [ ] Component composition +- [x] Component composition - [x] Props - [x] Emit to parent - [x] Slot @@ -604,6 +604,43 @@ How do we solve this ? Developers love having framework overview by examples. It - [x] Router link - [x] Routing +
+ + + Angular "Renaissance" (v14+) + + +- [x] Reactivity + - [x] Declare state + - [x] Update state + - [x] Computed state +- [x] Templating + - [x] Minimal template + - [x] Styling + - [x] Loop + - [x] Event click + - [x] Dom ref + - [x] Conditional +- [x] Lifecycle + - [x] On mount + - [x] On unmount +- [x] Component composition + - [x] Props + - [x] Emit to parent + - [x] Slot + - [x] Slot fallback + - [x] Context +- [x] Form input + - [x] Input text + - [x] Checkbox + - [x] Radio + - [x] Select +- [x] Webapp features + - [x] Render app + - [x] Fetch data + - [x] Router link + - [x] Routing +
diff --git a/content/1-reactivity/1-declare-state/angular-renaissance/name.component.ts b/content/1-reactivity/1-declare-state/angular-renaissance/name.component.ts new file mode 100644 index 00000000..e756e958 --- /dev/null +++ b/content/1-reactivity/1-declare-state/angular-renaissance/name.component.ts @@ -0,0 +1,10 @@ +import { Component, signal } from "@angular/core"; + +@Component({ + standalone: true, + selector: "app-name", + template: `

Hello {{ name() }}

`, +}) +export class NameComponent { + name = signal("John"); +} diff --git a/content/1-reactivity/1-declare-state/angular/name.component.ts b/content/1-reactivity/1-declare-state/angular/name.component.ts index 8e5eb9b8..7baf4ff7 100644 --- a/content/1-reactivity/1-declare-state/angular/name.component.ts +++ b/content/1-reactivity/1-declare-state/angular/name.component.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { Component, NgModule } from "@angular/core"; @Component({ selector: "app-name", @@ -7,3 +7,9 @@ import { Component } from "@angular/core"; export class NameComponent { name = "John"; } + +@NgModule({ + declarations: [NameComponent], + exports: [NameComponent], +}) +export class NameModule {} diff --git a/content/1-reactivity/2-update-state/angular-renaissance/name.component.ts b/content/1-reactivity/2-update-state/angular-renaissance/name.component.ts new file mode 100644 index 00000000..3d0f2ff9 --- /dev/null +++ b/content/1-reactivity/2-update-state/angular-renaissance/name.component.ts @@ -0,0 +1,14 @@ +import { Component, signal } from "@angular/core"; + +@Component({ + standalone: true, + selector: "app-name", + template: `

Hello {{ name() }}

`, +}) +export class NameComponent { + name = signal("John"); + + constructor() { + this.name.set("Jane"); + } +} diff --git a/content/1-reactivity/2-update-state/angular/name.component.ts b/content/1-reactivity/2-update-state/angular/name.component.ts index 58bcb040..c68cc0eb 100644 --- a/content/1-reactivity/2-update-state/angular/name.component.ts +++ b/content/1-reactivity/2-update-state/angular/name.component.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { Component, NgModule } from "@angular/core"; @Component({ selector: "app-name", @@ -11,3 +11,9 @@ export class NameComponent { this.name = "Jane"; } } + +@NgModule({ + declarations: [NameComponent], + exports: [NameComponent], +}) +export class NameModule {} diff --git a/content/1-reactivity/3-computed-state/angular-renaissance/doublecount.component.ts b/content/1-reactivity/3-computed-state/angular-renaissance/doublecount.component.ts new file mode 100644 index 00000000..f14d7270 --- /dev/null +++ b/content/1-reactivity/3-computed-state/angular-renaissance/doublecount.component.ts @@ -0,0 +1,12 @@ +import { Component, computed, signal } from "@angular/core"; + +@Component({ + standalone: true, + selector: "app-doublecount", + template: `
{{ doubleCount() }}
`, +}) +export class DoublecountComponent { + count = signal(10); + + doubleCount = computed(() => this.count() * 2); +} diff --git a/content/1-reactivity/3-computed-state/angular/doublecount.component.ts b/content/1-reactivity/3-computed-state/angular/doublecount.component.ts index 423413cb..165d1349 100644 --- a/content/1-reactivity/3-computed-state/angular/doublecount.component.ts +++ b/content/1-reactivity/3-computed-state/angular/doublecount.component.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { Component, NgModule } from "@angular/core"; @Component({ selector: "app-doublecount", @@ -11,3 +11,9 @@ export class DoublecountComponent { return this.count * 2; } } + +@NgModule({ + declarations: [DoublecountComponent], + exports: [DoublecountComponent], +}) +export class DoublecountModule {} diff --git a/content/2-templating/1-minimal-template/angular-renaissance/helloworld.component.ts b/content/2-templating/1-minimal-template/angular-renaissance/helloworld.component.ts new file mode 100644 index 00000000..be38ebd4 --- /dev/null +++ b/content/2-templating/1-minimal-template/angular-renaissance/helloworld.component.ts @@ -0,0 +1,8 @@ +import { Component } from "@angular/core"; + +@Component({ + standalone: true, + selector: "app-helloworld", + template: `

Hello world

`, +}) +export class HelloworldComponent {} diff --git a/content/2-templating/1-minimal-template/angular/helloworld.component.ts b/content/2-templating/1-minimal-template/angular/helloworld.component.ts index adf68d5b..f4f6326b 100644 --- a/content/2-templating/1-minimal-template/angular/helloworld.component.ts +++ b/content/2-templating/1-minimal-template/angular/helloworld.component.ts @@ -1,7 +1,13 @@ -import { Component } from "@angular/core"; +import { Component, NgModule } from "@angular/core"; @Component({ selector: "app-helloworld", template: `

Hello world

`, }) export class HelloworldComponent {} + +@NgModule({ + declarations: [HelloworldComponent], + exports: [HelloworldComponent], +}) +export class HelloworldModule {} diff --git a/content/2-templating/2-styling/angular-renaissance/cssstyle.component.ts b/content/2-templating/2-styling/angular-renaissance/cssstyle.component.ts new file mode 100644 index 00000000..bb81145a --- /dev/null +++ b/content/2-templating/2-styling/angular-renaissance/cssstyle.component.ts @@ -0,0 +1,16 @@ +import { Component } from "@angular/core"; + +@Component({ + standalone: true, + selector: "app-cssstyle", + template: ` +

I am red

+ + `, + styles: ` + .title { + color: red; + } + `, +}) +export class CssStyleComponent {} diff --git a/content/2-templating/2-styling/angular/cssstyle.component.ts b/content/2-templating/2-styling/angular/cssstyle.component.ts index 98df5ea6..4bacab3c 100644 --- a/content/2-templating/2-styling/angular/cssstyle.component.ts +++ b/content/2-templating/2-styling/angular/cssstyle.component.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { Component, NgModule } from "@angular/core"; @Component({ selector: "app-cssstyle", @@ -15,3 +15,9 @@ import { Component } from "@angular/core"; ], }) export class CssStyleComponent {} + +@NgModule({ + declarations: [CssStyleComponent], + exports: [CssStyleComponent], +}) +export class CssStyleModule {} diff --git a/content/2-templating/3-loop/angular-renaissance/colors.component.ts b/content/2-templating/3-loop/angular-renaissance/colors.component.ts new file mode 100644 index 00000000..5e210a52 --- /dev/null +++ b/content/2-templating/3-loop/angular-renaissance/colors.component.ts @@ -0,0 +1,16 @@ +import { Component } from "@angular/core"; + +@Component({ + standalone: true, + selector: "app-colors", + template: ` + + `, +}) +export class ColorsComponent { + colors = ["red", "green", "blue"]; +} diff --git a/content/2-templating/3-loop/angular/colors.component.ts b/content/2-templating/3-loop/angular/colors.component.ts index ec438d37..716d9575 100644 --- a/content/2-templating/3-loop/angular/colors.component.ts +++ b/content/2-templating/3-loop/angular/colors.component.ts @@ -1,4 +1,5 @@ -import { Component } from "@angular/core"; +import { Component, NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; @Component({ selector: "app-colors", @@ -11,3 +12,10 @@ import { Component } from "@angular/core"; export class ColorsComponent { colors = ["red", "green", "blue"]; } + +@NgModule({ + declarations: [ColorsComponent], + imports: [CommonModule], + exports: [ColorsComponent], +}) +export class ColorsModule {} diff --git a/content/2-templating/4-event-click/angular-renaissance/counter.component.ts b/content/2-templating/4-event-click/angular-renaissance/counter.component.ts new file mode 100644 index 00000000..d3b77d7e --- /dev/null +++ b/content/2-templating/4-event-click/angular-renaissance/counter.component.ts @@ -0,0 +1,17 @@ +import { Component, signal } from "@angular/core"; + +@Component({ + standalone: true, + selector: "app-counter", + template: ` +

Counter: {{ count() }}

+ + `, +}) +export class CounterComponent { + count = signal(0); + + incrementCount() { + this.count.update((count) => count + 1); + } +} diff --git a/content/2-templating/4-event-click/angular/counter.component.ts b/content/2-templating/4-event-click/angular/counter.component.ts index 298a2d29..3593fd8d 100644 --- a/content/2-templating/4-event-click/angular/counter.component.ts +++ b/content/2-templating/4-event-click/angular/counter.component.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { Component, NgModule } from "@angular/core"; @Component({ selector: "app-counter", @@ -14,3 +14,9 @@ export class CounterComponent { this.count++; } } + +@NgModule({ + declarations: [CounterComponent], + exports: [CounterComponent], +}) +export class CounterModule {} diff --git a/content/2-templating/5-dom-ref/angular-renaissance/inputfocused.component.ts b/content/2-templating/5-dom-ref/angular-renaissance/inputfocused.component.ts new file mode 100644 index 00000000..689bcdb2 --- /dev/null +++ b/content/2-templating/5-dom-ref/angular-renaissance/inputfocused.component.ts @@ -0,0 +1,14 @@ +import { Component, ElementRef, OnInit, viewChild } from "@angular/core"; + +@Component({ + standalone: true, + selector: "app-inputfocused", + template: ``, +}) +export class InputfocusedComponent implements OnInit { + inputRef = viewChild.required>("inputRef"); + + ngOnInit() { + this.inputRef().nativeElement.focus(); + } +} diff --git a/content/2-templating/5-dom-ref/angular/inputfocused.component.ts b/content/2-templating/5-dom-ref/angular/inputfocused.component.ts index b7354db0..4c1843d4 100644 --- a/content/2-templating/5-dom-ref/angular/inputfocused.component.ts +++ b/content/2-templating/5-dom-ref/angular/inputfocused.component.ts @@ -1,4 +1,10 @@ -import { Component, ViewChild, ElementRef, OnInit } from "@angular/core"; +import { + Component, + ViewChild, + ElementRef, + OnInit, + NgModule, +} from "@angular/core"; @Component({ selector: "app-inputfocused", @@ -12,3 +18,9 @@ export class InputfocusedComponent implements OnInit { this.inputRef.nativeElement.focus(); } } + +@NgModule({ + declarations: [InputfocusedComponent], + exports: [InputfocusedComponent], +}) +export class InputfocusedModule {} diff --git a/content/2-templating/6-conditional/angular-renaissance/trafficlight.component.ts b/content/2-templating/6-conditional/angular-renaissance/trafficlight.component.ts new file mode 100644 index 00000000..3f87a870 --- /dev/null +++ b/content/2-templating/6-conditional/angular-renaissance/trafficlight.component.ts @@ -0,0 +1,35 @@ +import { Component, computed, signal } from "@angular/core"; + +const TRAFFIC_LIGHTS = ["red", "orange", "green"]; + +@Component({ + standalone: true, + selector: "app-trafficlight", + template: ` + +

Light is: {{ light() }}

+

+ You must + @switch (light()) { + @case ("red") { + STOP + } + @case ("orange") { + SLOW DOWN + } + @case ("green") { + GO + } + } +

+ `, +}) +export class TrafficlightComponent { + lightIndex = signal(0); + + light = computed(() => TRAFFIC_LIGHTS[this.lightIndex()]); + + nextLight() { + this.lightIndex.update((index) => (index + 1) % TRAFFIC_LIGHTS.length); + } +} diff --git a/content/2-templating/6-conditional/angular/trafficlight.component.ts b/content/2-templating/6-conditional/angular/trafficlight.component.ts index 4235ad45..5611d5df 100644 --- a/content/2-templating/6-conditional/angular/trafficlight.component.ts +++ b/content/2-templating/6-conditional/angular/trafficlight.component.ts @@ -1,4 +1,5 @@ -import { Component } from "@angular/core"; +import { Component, NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; const TRAFFIC_LIGHTS = ["red", "orange", "green"]; @@ -28,3 +29,10 @@ export class TrafficlightComponent { this.lightIndex = (this.lightIndex + 1) % TRAFFIC_LIGHTS.length; } } + +@NgModule({ + declarations: [TrafficlightComponent], + imports: [CommonModule], + exports: [TrafficlightComponent], +}) +export class TrafficlightModule {} diff --git a/content/3-lifecycle/1-on-mount/angular-renaissance/pagetitle.component.ts b/content/3-lifecycle/1-on-mount/angular-renaissance/pagetitle.component.ts new file mode 100644 index 00000000..06ddfa2b --- /dev/null +++ b/content/3-lifecycle/1-on-mount/angular-renaissance/pagetitle.component.ts @@ -0,0 +1,14 @@ +import { Component, OnInit } from "@angular/core"; + +@Component({ + standalone: true, + selector: "app-pagetitle", + template: `

Page title: {{ pageTitle }}

`, +}) +export class PagetitleComponent implements OnInit { + pageTitle = ""; + + ngOnInit() { + this.pageTitle = document.title; + } +} diff --git a/content/3-lifecycle/1-on-mount/angular/pagetitle.component.ts b/content/3-lifecycle/1-on-mount/angular/pagetitle.component.ts index 04c2b1eb..f0c39643 100644 --- a/content/3-lifecycle/1-on-mount/angular/pagetitle.component.ts +++ b/content/3-lifecycle/1-on-mount/angular/pagetitle.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from "@angular/core"; +import { Component, NgModule, OnInit } from "@angular/core"; @Component({ selector: "app-pagetitle", @@ -11,3 +11,9 @@ export class PagetitleComponent implements OnInit { this.pageTitle = document.title; } } + +@NgModule({ + declarations: [PagetitleComponent], + exports: [PagetitleComponent], +}) +export class PagetitleModule {} diff --git a/content/3-lifecycle/2-on-unmount/angular-renaissance/time.component.ts b/content/3-lifecycle/2-on-unmount/angular-renaissance/time.component.ts new file mode 100644 index 00000000..fbc3da5c --- /dev/null +++ b/content/3-lifecycle/2-on-unmount/angular-renaissance/time.component.ts @@ -0,0 +1,33 @@ +import { Component, OnDestroy, signal } from "@angular/core"; + +/* FIXME with rxjs + toSignal (but no need for onDestroy anymore) +```ts +@Component({ + standalone: true, + selector: "app-time", + template: `

Current time: {{ time() }}

`, +}) +export class TimeComponent { + time = toSignal(timer(0, 1000).pipe( + map(() => new Date().toLocaleTimeString()), + )); +} +``` +*/ +@Component({ + standalone: true, + selector: "app-time", + template: `

Current time: {{ time() }}

`, +}) +export class TimeComponent implements OnDestroy { + time = signal(new Date().toLocaleTimeString()); + + timer = setInterval( + () => this.time.set(new Date().toLocaleTimeString()), + 1000 + ); + + ngOnDestroy() { + clearInterval(this.timer); + } +} diff --git a/content/3-lifecycle/2-on-unmount/angular/time.component.ts b/content/3-lifecycle/2-on-unmount/angular/time.component.ts index 8f9b3c02..715c8ece 100644 --- a/content/3-lifecycle/2-on-unmount/angular/time.component.ts +++ b/content/3-lifecycle/2-on-unmount/angular/time.component.ts @@ -1,20 +1,23 @@ -import { Component, OnDestroy } from "@angular/core"; +import { Component, NgModule, OnDestroy } from "@angular/core"; @Component({ selector: "app-time", template: `

Current time: {{ time }}

`, }) export class TimeComponent implements OnDestroy { - time: string = new Date().toLocaleTimeString(); - timer: number; + time = new Date().toLocaleTimeString(); - constructor() { - this.timer = setInterval(() => { - this.time = new Date().toLocaleTimeString(); - }, 1000); - } + timer = setInterval(() => { + this.time = new Date().toLocaleTimeString(); + }, 1000); ngOnDestroy() { clearInterval(this.timer); } } + +@NgModule({ + declarations: [TimeComponent], + exports: [TimeComponent], +}) +export class TimeModule {} diff --git a/content/4-component-composition/1-props/angular-renaissance/app.component.ts b/content/4-component-composition/1-props/angular-renaissance/app.component.ts new file mode 100644 index 00000000..436bafde --- /dev/null +++ b/content/4-component-composition/1-props/angular-renaissance/app.component.ts @@ -0,0 +1,17 @@ +import { Component } from "@angular/core"; +import { UserprofileComponent } from "./userprofile.component"; + +@Component({ + standalone: true, + selector: "app-root", + imports: [UserprofileComponent], + template: ` + + `, +}) +export class AppComponent {} diff --git a/content/4-component-composition/1-props/angular-renaissance/userprofile.component.ts b/content/4-component-composition/1-props/angular-renaissance/userprofile.component.ts new file mode 100644 index 00000000..e775f32e --- /dev/null +++ b/content/4-component-composition/1-props/angular-renaissance/userprofile.component.ts @@ -0,0 +1,18 @@ +import { Component, input } from "@angular/core"; + +@Component({ + standalone: true, + selector: "app-userprofile", + template: ` +

My name is {{ name() }}!

+

My age is {{ age() }}!

+

My favourite colors are {{ favouriteColors().join(", ") }}!

+

I am {{ isAvailable() ? "available" : "not available" }}

+ `, +}) +export class UserprofileComponent { + name = input(""); + age = input(0); + favouriteColors = input([]); + isAvailable = input(false); +} diff --git a/content/4-component-composition/1-props/angular/app.component.ts b/content/4-component-composition/1-props/angular/app.component.ts index f8a2cdf1..5c4d96a4 100644 --- a/content/4-component-composition/1-props/angular/app.component.ts +++ b/content/4-component-composition/1-props/angular/app.component.ts @@ -1,4 +1,5 @@ -import { Component } from "@angular/core"; +import { Component, NgModule } from "@angular/core"; +import { UserprofileModule } from "./userprofile.component"; @Component({ selector: "app-root", @@ -13,3 +14,10 @@ import { Component } from "@angular/core"; `, }) export class AppComponent {} + +@NgModule({ + declarations: [AppComponent], + imports: [UserprofileModule], + bootstrap: [AppComponent], +}) +export class AppModule {} diff --git a/content/4-component-composition/1-props/angular/userprofile.component.ts b/content/4-component-composition/1-props/angular/userprofile.component.ts index 6ad0bb65..05745b04 100644 --- a/content/4-component-composition/1-props/angular/userprofile.component.ts +++ b/content/4-component-composition/1-props/angular/userprofile.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from "@angular/core"; +import { Component, Input, NgModule } from "@angular/core"; @Component({ selector: "app-userprofile", @@ -15,3 +15,9 @@ export class UserprofileComponent { @Input() favouriteColors: string[] = []; @Input() isAvailable: boolean = false; } + +@NgModule({ + declarations: [UserprofileComponent], + exports: [UserprofileComponent], +}) +export class UserprofileModule {} diff --git a/content/4-component-composition/2-emit-to-parent/angular-renaissance/answer-button.component.ts b/content/4-component-composition/2-emit-to-parent/angular-renaissance/answer-button.component.ts new file mode 100644 index 00000000..d097f992 --- /dev/null +++ b/content/4-component-composition/2-emit-to-parent/angular-renaissance/answer-button.component.ts @@ -0,0 +1,14 @@ +import { Component, output } from "@angular/core"; + +@Component({ + standalone: true, + selector: "app-answer-button", + template: ` + + + `, +}) +export class AnswerButtonComponent { + yes = output(); + no = output(); +} diff --git a/content/4-component-composition/2-emit-to-parent/angular-renaissance/app.component.ts b/content/4-component-composition/2-emit-to-parent/angular-renaissance/app.component.ts new file mode 100644 index 00000000..ff2679c5 --- /dev/null +++ b/content/4-component-composition/2-emit-to-parent/angular-renaissance/app.component.ts @@ -0,0 +1,27 @@ +import { Component, signal } from "@angular/core"; +import { AnswerButtonComponent } from "./answer-button.component"; + +@Component({ + standalone: true, + selector: "app-root", + imports: [AnswerButtonComponent], + template: ` +

Are you happy?

+ + + + +

{{ isHappy() ? "😀" : "😥" }}

+ `, +}) +export class AppComponent { + isHappy = signal(true); + + onAnswerYes() { + this.isHappy.set(true); + } + + onAnswerNo() { + this.isHappy.set(false); + } +} diff --git a/content/4-component-composition/2-emit-to-parent/angular/answer-button.component.ts b/content/4-component-composition/2-emit-to-parent/angular/answer-button.component.ts index d7a6129a..51aaafa0 100644 --- a/content/4-component-composition/2-emit-to-parent/angular/answer-button.component.ts +++ b/content/4-component-composition/2-emit-to-parent/angular/answer-button.component.ts @@ -1,4 +1,4 @@ -import { Component, Output, EventEmitter } from "@angular/core"; +import { Component, Output, EventEmitter, NgModule } from "@angular/core"; @Component({ selector: "app-answer-button", @@ -11,3 +11,9 @@ export class AnswerButtonComponent { @Output() yes = new EventEmitter(); @Output() no = new EventEmitter(); } + +@NgModule({ + declarations: [AnswerButtonComponent], + exports: [AnswerButtonComponent], +}) +export class AnswerButtonModule {} diff --git a/content/4-component-composition/2-emit-to-parent/angular/app.component.ts b/content/4-component-composition/2-emit-to-parent/angular/app.component.ts index 17504d64..d15ecfa4 100644 --- a/content/4-component-composition/2-emit-to-parent/angular/app.component.ts +++ b/content/4-component-composition/2-emit-to-parent/angular/app.component.ts @@ -1,4 +1,5 @@ -import { Component } from "@angular/core"; +import { Component, NgModule } from "@angular/core"; +import { AnswerButtonModule } from "./answer-button.component"; @Component({ selector: "app-root", @@ -22,3 +23,10 @@ export class AppComponent { this.isHappy = false; } } + +@NgModule({ + declarations: [AppComponent], + imports: [AnswerButtonModule], + bootstrap: [AppComponent], +}) +export class AppModule {} diff --git a/content/4-component-composition/3-slot/angular-renaissance/app.component.ts b/content/4-component-composition/3-slot/angular-renaissance/app.component.ts new file mode 100644 index 00000000..a3c322e0 --- /dev/null +++ b/content/4-component-composition/3-slot/angular-renaissance/app.component.ts @@ -0,0 +1,10 @@ +import { Component } from "@angular/core"; +import { FunnyButtonComponent } from "./funny-button.component"; + +@Component({ + standalone: true, + selector: "app-root", + imports: [FunnyButtonComponent], + template: `Click me!`, +}) +export class AppComponent {} diff --git a/content/4-component-composition/3-slot/angular-renaissance/funny-button.component.ts b/content/4-component-composition/3-slot/angular-renaissance/funny-button.component.ts new file mode 100644 index 00000000..4b6db278 --- /dev/null +++ b/content/4-component-composition/3-slot/angular-renaissance/funny-button.component.ts @@ -0,0 +1,26 @@ +import { Component } from "@angular/core"; + +@Component({ + standalone: true, + selector: "app-funny-button", + styles: ` + button { + background: rgba(0, 0, 0, 0.4); + color: #fff; + padding: 10px 20px; + font-size: 30px; + border: 2px solid #fff; + margin: 8px; + transform: scale(0.9); + box-shadow: 4px 4px rgba(0, 0, 0, 0.4); + transition: transform 0.2s cubic-bezier(0.34, 1.65, 0.88, 0.925) 0s; + outline: 0; + } + `, + template: ` + + `, +}) +export class FunnyButtonComponent {} diff --git a/content/4-component-composition/3-slot/angular/app.component.ts b/content/4-component-composition/3-slot/angular/app.component.ts index 8fb72406..2acf3424 100644 --- a/content/4-component-composition/3-slot/angular/app.component.ts +++ b/content/4-component-composition/3-slot/angular/app.component.ts @@ -1,7 +1,15 @@ -import { Component } from "@angular/core"; +import { Component, NgModule } from "@angular/core"; +import { FunnyButtonModule } from "./funny-button.component"; @Component({ selector: "app-root", template: `Click me!`, }) export class AppComponent {} + +@NgModule({ + declarations: [AppComponent], + imports: [FunnyButtonModule], + bootstrap: [AppComponent], +}) +export class AppModule {} diff --git a/content/4-component-composition/3-slot/angular/funny-button.component.css b/content/4-component-composition/3-slot/angular/funny-button.component.css deleted file mode 100644 index f138778a..00000000 --- a/content/4-component-composition/3-slot/angular/funny-button.component.css +++ /dev/null @@ -1,12 +0,0 @@ -button { - background: rgba(0, 0, 0, 0.4); - color: #fff; - padding: 10px 20px; - font-size: 30px; - border: 2px solid #fff; - margin: 8px; - transform: scale(0.9); - box-shadow: 4px 4px rgba(0, 0, 0, 0.4); - transition: transform 0.2s cubic-bezier(0.34, 1.65, 0.88, 0.925) 0s; - outline: 0; -} diff --git a/content/4-component-composition/3-slot/angular/funny-button.component.ts b/content/4-component-composition/3-slot/angular/funny-button.component.ts index 5f32a1ef..bb515578 100644 --- a/content/4-component-composition/3-slot/angular/funny-button.component.ts +++ b/content/4-component-composition/3-slot/angular/funny-button.component.ts @@ -1,8 +1,23 @@ -import { Component } from "@angular/core"; +import { Component, NgModule } from "@angular/core"; @Component({ selector: "app-funny-button", - styleUrls: ["./funny-button.component.css"], + styles: [ + ` + button { + background: rgba(0, 0, 0, 0.4); + color: #fff; + padding: 10px 20px; + font-size: 30px; + border: 2px solid #fff; + margin: 8px; + transform: scale(0.9); + box-shadow: 4px 4px rgba(0, 0, 0, 0.4); + transition: transform 0.2s cubic-bezier(0.34, 1.65, 0.88, 0.925) 0s; + outline: 0; + } + `, + ], template: ` + `, +}) +export class FunnyButtonComponent {} diff --git a/content/4-component-composition/4-slot-fallback/angular/app.component.ts b/content/4-component-composition/4-slot-fallback/angular/app.component.ts index 84e8cb2e..83f394e1 100644 --- a/content/4-component-composition/4-slot-fallback/angular/app.component.ts +++ b/content/4-component-composition/4-slot-fallback/angular/app.component.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { Component, NgModule } from "@angular/core"; @Component({ selector: "app-root", @@ -11,3 +11,10 @@ import { Component } from "@angular/core"; `, }) export class AppComponent {} + +@NgModule({ + declarations: [AppComponent], + imports: [FunnyButtonModule], + bootstrap: [AppComponent], +}) +export class AppModule {} diff --git a/content/4-component-composition/4-slot-fallback/angular/funny-button.component.css b/content/4-component-composition/4-slot-fallback/angular/funny-button.component.css deleted file mode 100644 index f138778a..00000000 --- a/content/4-component-composition/4-slot-fallback/angular/funny-button.component.css +++ /dev/null @@ -1,12 +0,0 @@ -button { - background: rgba(0, 0, 0, 0.4); - color: #fff; - padding: 10px 20px; - font-size: 30px; - border: 2px solid #fff; - margin: 8px; - transform: scale(0.9); - box-shadow: 4px 4px rgba(0, 0, 0, 0.4); - transition: transform 0.2s cubic-bezier(0.34, 1.65, 0.88, 0.925) 0s; - outline: 0; -} diff --git a/content/4-component-composition/4-slot-fallback/angular/funny-button.component.ts b/content/4-component-composition/4-slot-fallback/angular/funny-button.component.ts index 62681c2f..bb0e4aef 100644 --- a/content/4-component-composition/4-slot-fallback/angular/funny-button.component.ts +++ b/content/4-component-composition/4-slot-fallback/angular/funny-button.component.ts @@ -1,8 +1,24 @@ -import { Component, ContentChild, TemplateRef } from "@angular/core"; +import { Component, ContentChild, NgModule, TemplateRef } from "@angular/core"; +import { CommonModule } from "@angular/common"; @Component({ selector: "app-funny-button", - styleUrls: ["./funny-button.component.css"], + styles: [ + ` + button { + background: rgba(0, 0, 0, 0.4); + color: #fff; + padding: 10px 20px; + font-size: 30px; + border: 2px solid #fff; + margin: 8px; + transform: scale(0.9); + box-shadow: 4px 4px rgba(0, 0, 0, 0.4); + transition: transform 0.2s cubic-bezier(0.34, 1.65, 0.88, 0.925) 0s; + outline: 0; + } + `, + ], template: ` + + `, +}) +export class UserProfileComponent { + protected userService = inject(UserService); +} diff --git a/content/4-component-composition/5-context/angular-renaissance/user.service.ts b/content/4-component-composition/5-context/angular-renaissance/user.service.ts new file mode 100644 index 00000000..823ac7bd --- /dev/null +++ b/content/4-component-composition/5-context/angular-renaissance/user.service.ts @@ -0,0 +1,14 @@ +import { Injectable, signal } from "@angular/core"; + +@Injectable() +export class UserService { + user = signal({ + id: 1, + username: "unicorn42", + email: "unicorn42@example.com", + }); + + updateUsername(username: string) { + this.user.update((user) => ({ ...user, username })); + } +} diff --git a/content/4-component-composition/5-context/angular/app.component.ts b/content/4-component-composition/5-context/angular/app.component.ts new file mode 100644 index 00000000..831ec6fd --- /dev/null +++ b/content/4-component-composition/5-context/angular/app.component.ts @@ -0,0 +1,22 @@ +import { Component, NgModule } from "@angular/core"; +import { UserService } from "./user.service"; +import { UserProfileModule } from "./user-profile.component"; + +@Component({ + providers: [UserService], + selector: "app-root", + template: ` +

Welcome back, {{ userService.user.username }}

+ + `, +}) +export class AppComponent { + constructor(public userService: UserService) {} +} + +@NgModule({ + declarations: [AppComponent], + imports: [UserProfileModule], + bootstrap: [AppComponent], +}) +export class AppModule {} diff --git a/content/4-component-composition/5-context/angular/user-profile.component.ts b/content/4-component-composition/5-context/angular/user-profile.component.ts new file mode 100644 index 00000000..5c817ccd --- /dev/null +++ b/content/4-component-composition/5-context/angular/user-profile.component.ts @@ -0,0 +1,25 @@ +import { Component, NgModule } from "@angular/core"; +import { UserService } from "./user.service"; + +@Component({ + selector: "app-user-profile", + template: ` +
+

My Profile

+

Username: {{ userService.user.username }}

+

Email: {{ userService.user.email }}

+ +
+ `, +}) +export class UserProfileComponent { + constructor(public userService: UserService) {} +} + +@NgModule({ + declarations: [UserProfileComponent], + exports: [UserProfileComponent], +}) +export class UserProfileModule {} diff --git a/content/4-component-composition/5-context/angular/user.service.ts b/content/4-component-composition/5-context/angular/user.service.ts new file mode 100644 index 00000000..03406aa9 --- /dev/null +++ b/content/4-component-composition/5-context/angular/user.service.ts @@ -0,0 +1,14 @@ +import { Injectable } from "@angular/core"; + +@Injectable() +export class UserService { + user = { + id: 1, + username: "unicorn42", + email: "unicorn42@example.com", + }; + + updateUsername(username: string) { + this.user.username = username; + } +} diff --git a/content/6-form-input/1-input-text/angular-renaissance/input-hello.component.ts b/content/6-form-input/1-input-text/angular-renaissance/input-hello.component.ts new file mode 100644 index 00000000..70cefa59 --- /dev/null +++ b/content/6-form-input/1-input-text/angular-renaissance/input-hello.component.ts @@ -0,0 +1,15 @@ +import { Component, signal } from "@angular/core"; +import { FormsModule } from "@angular/forms"; + +@Component({ + standalone: true, + imports: [FormsModule], + selector: "app-input-hello", + template: ` +

{{ text }}

+ + `, +}) +export class InputHelloComponent { + text = signal(""); +} diff --git a/content/6-form-input/1-input-text/angular/input-hello.component.ts b/content/6-form-input/1-input-text/angular/input-hello.component.ts index 86bec149..f14b66f8 100644 --- a/content/6-form-input/1-input-text/angular/input-hello.component.ts +++ b/content/6-form-input/1-input-text/angular/input-hello.component.ts @@ -1,14 +1,20 @@ -import { Component } from "@angular/core"; -import { CommonModule } from "@angular/common"; +import { Component, NgModule } from "@angular/core"; import { FormsModule } from "@angular/forms"; @Component({ selector: "app-input-hello", - standalone: true, - imports: [CommonModule, FormsModule], - template: `

{{ text }}

- `, + template: ` +

{{ text }}

+ + `, }) export class InputHelloComponent { text = ""; } + +@NgModule({ + declarations: [InputHelloComponent], + imports: [FormsModule], + exports: [InputHelloComponent], +}) +export class InputHelloModule {} diff --git a/content/6-form-input/2-checkbox/angular-renaissance/is-available.component.ts b/content/6-form-input/2-checkbox/angular-renaissance/is-available.component.ts new file mode 100644 index 00000000..3ee813d2 --- /dev/null +++ b/content/6-form-input/2-checkbox/angular-renaissance/is-available.component.ts @@ -0,0 +1,15 @@ +import { Component, signal } from "@angular/core"; +import { FormsModule } from "@angular/forms"; + +@Component({ + standalone: true, + imports: [FormsModule], + selector: "app-is-available", + template: ` + + + `, +}) +export class IsAvailableComponent { + isAvailable = signal(false); +} diff --git a/content/6-form-input/2-checkbox/angular/is-available.component.ts b/content/6-form-input/2-checkbox/angular/is-available.component.ts index 46da1d35..60c2fc49 100644 --- a/content/6-form-input/2-checkbox/angular/is-available.component.ts +++ b/content/6-form-input/2-checkbox/angular/is-available.component.ts @@ -1,11 +1,8 @@ -import { Component } from "@angular/core"; -import { CommonModule } from "@angular/common"; +import { Component, NgModule } from "@angular/core"; import { FormsModule } from "@angular/forms"; @Component({ selector: "app-is-available", - standalone: true, - imports: [CommonModule, FormsModule], template: ` @@ -14,3 +11,10 @@ import { FormsModule } from "@angular/forms"; export class IsAvailableComponent { isAvailable = false; } + +@NgModule({ + declarations: [IsAvailableComponent], + imports: [FormsModule], + exports: [IsAvailableComponent], +}) +export class IsAvailableModule {} diff --git a/content/6-form-input/3-radio/angular-renaissance/pick-pill.component.ts b/content/6-form-input/3-radio/angular-renaissance/pick-pill.component.ts new file mode 100644 index 00000000..c37bc27a --- /dev/null +++ b/content/6-form-input/3-radio/angular-renaissance/pick-pill.component.ts @@ -0,0 +1,20 @@ +import { Component, signal } from "@angular/core"; +import { FormsModule } from "@angular/forms"; + +@Component({ + standalone: true, + imports: [FormsModule], + selector: "app-pick-pill", + template: ` +
Picked: {{ picked }}
+ + + + + + + `, +}) +export class PickPillComponent { + picked = signal("red"); +} diff --git a/content/6-form-input/3-radio/angular/pick-pill.component.ts b/content/6-form-input/3-radio/angular/pick-pill.component.ts index de416f82..87610b17 100644 --- a/content/6-form-input/3-radio/angular/pick-pill.component.ts +++ b/content/6-form-input/3-radio/angular/pick-pill.component.ts @@ -1,11 +1,8 @@ -import { Component } from "@angular/core"; -import { CommonModule } from "@angular/common"; +import { Component, NgModule } from "@angular/core"; import { FormsModule } from "@angular/forms"; @Component({ selector: "app-pick-pill", - standalone: true, - imports: [CommonModule, FormsModule], template: `
Picked: {{ picked }}
@@ -19,3 +16,10 @@ import { FormsModule } from "@angular/forms"; export class PickPillComponent { picked = "red"; } + +@NgModule({ + declarations: [PickPillComponent], + imports: [FormsModule], + exports: [PickPillComponent], +}) +export class PickPillModule {} diff --git a/content/6-form-input/4-select/angular-renaissance/color-select.component.ts b/content/6-form-input/4-select/angular-renaissance/color-select.component.ts new file mode 100644 index 00000000..7f767fcf --- /dev/null +++ b/content/6-form-input/4-select/angular-renaissance/color-select.component.ts @@ -0,0 +1,27 @@ +import { Component, signal } from "@angular/core"; +import { FormsModule } from "@angular/forms"; + +@Component({ + standalone: true, + imports: [FormsModule], + selector: "app-color-select", + template: ` + + `, +}) +export class ColorSelectComponent { + selectedColorId = signal(2); + + colors = [ + { id: 1, text: "red" }, + { id: 2, text: "blue" }, + { id: 3, text: "green" }, + { id: 4, text: "gray", isDisabled: true }, + ]; +} diff --git a/content/6-form-input/4-select/angular/color-select.component.ts b/content/6-form-input/4-select/angular/color-select.component.ts index be465d3a..38988d16 100644 --- a/content/6-form-input/4-select/angular/color-select.component.ts +++ b/content/6-form-input/4-select/angular/color-select.component.ts @@ -1,11 +1,8 @@ -import { Component } from "@angular/core"; -import { CommonModule } from "@angular/common"; +import { Component, NgModule } from "@angular/core"; import { FormsModule } from "@angular/forms"; @Component({ selector: "app-color-select", - standalone: true, - imports: [CommonModule, FormsModule], template: `