Skip to content

Commit 4bf008e

Browse files
committed
Update tooling and first steps chapters to Angular 20
1 parent c2ce819 commit 4bf008e

File tree

5 files changed

+199
-103
lines changed

5 files changed

+199
-103
lines changed

docs/src/.vuepress/public/eslint.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ module.exports = tseslint.config(
1414
],
1515
processor: angular.processInlineTemplates,
1616
languageOptions: {
17-
ecmaVersion: 2020,
17+
ecmaVersion: 2022,
1818
sourceType: "module",
1919
parserOptions: {
20-
project: ["tsconfig.json", "e2e/tsconfig.json"],
20+
project: ["tsconfig.app.json", "tsconfig.spec.json"],
2121
createDefaultProgram: true,
2222
ecmaFeatures: {
2323
modules: true,

docs/src/first-steps/README.md

Lines changed: 89 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -50,75 +50,116 @@ The `src` folder contains:
5050
The `app` folder contains:
5151
- `app.config.ts`: Defines the application configuration that tells Angular how to assemble the application.
5252
- `app.routes.ts`: Defines the application's routing configuration
53-
- `app.component.html`: Defines the HTML template associated with the root `AppComponent`
54-
- `app.component.scss`: Defines the base stylesheet for the root `AppComponent`
55-
- `app.component.ts`: Defines the logic for the app's root component, named `AppComponent`. The view associated with this root component is the root of the view hierarchy.
56-
- `app.component.spec.ts`: Defines a unit test for the root `AppComponent`
53+
- `app.html`: Defines the HTML template associated with the root `App` component.
54+
- `app.scss`: Defines the base stylesheet for the root `App` component.
55+
- `app.ts`: Defines the logic for the app's root component, named `App`. The view associated with this root component is the root of the view hierarchy.
56+
- `app.spec.ts`: Defines a unit test for the root `App` component.
5757

5858

5959
## Text interpolation in templates
6060

61-
Like any other component, the shell AppComponent is distributed over three files.
62-
Open the component class file (`app.component.ts`) and change the value of the title property to 'Search films'
61+
Like any other component, the shell `App` component is distributed over three files.
62+
Open the component typescript file (`app.ts`) and change the value of the title property to 'Search films'
6363

6464
```typescript
6565
// app.component.ts
6666
import { Component } from '@angular/core'
6767

6868
@Component({
6969
selector: 'app-root',
70-
templateUrl: './app.component.html',
71-
styleUrls: ['./app.component.scss']
70+
templateUrl: './app.html',
71+
styleUrls: ['./app.scss']
7272
})
73-
export class AppComponent {
74-
title = 'Search Films'
73+
export class App {
74+
protected readonly title = signal('Search Films')
7575
}
7676
```
7777

78-
Open the component template file (`app.component.html`) and delete the default template generated by the Angular CLI. Replace it with the following line of HTML.
78+
Open the component template file (`app.html`) and delete the default template generated by the Angular CLI. Replace it with the following line of HTML.
7979
```html
80-
<!-- app.component.html -->
81-
<h1>{{title}}</h1>
80+
<!-- app.html -->
81+
<h1>{{title()}}</h1>
8282
```
8383
The double curly braces are Angular's *interpolation binding syntax*. This interpolation binding presents the component's title property value inside the HTML header tag.
8484

8585
The browser refreshes and displays the new application title.
8686

87-
The simplest way to insert data dynamically into your components is through text interpolation, using the `{{myVariable}}` syntax. Inside double curly braces, you can specify any valid JavaScript expression that don't have or promote side-effects.
87+
The simplest way to insert data dynamically into your components is through text interpolation, using the `{{myVariable}}` syntax. Inside double curly braces, you can specify any valid JavaScript expression except for:
8888

89-
JavaScript expressions that have or promote side effects include:
90-
- assignements (`=`, `+=`, ...)
91-
- operators such as `new`, `typeof` or `instanceof`
92-
- increment and decrement operators `++` and `--`
89+
- bitwise operators: `&`, `&=`, `~`, `|=`, `^=`, etc.
90+
- object destructuring: `const { name } = person`
91+
- array destructuring: `const [firstItem] = items`
92+
- comma operator: `x = (x++, x)`
93+
- instanceof: `movie instanceof Film`
94+
- new: `new Film()`
95+
- regex
96+
- declarations
9397

94-
Add two fields after the title variable in the `app.component.ts` file:
98+
Add two fields after the title variable in the `app.ts` file:
9599
```typescript
96-
// app.component.ts
97-
title = 'Search Films'
98-
orderReference = 'ABCXYZ'
99-
price = 17.3
100+
// app.ts
101+
protected readonly title = signal('Search Films')
102+
protected readonly orderReference = signal('ABCXYZ')
103+
protected readonly price = signal(17.3)
100104
```
101105

102106
In the template:
103107
```html
104-
<!--app.component.html-->
105-
<h1>{{title}}</h1>
106-
<p>Order ref. {{ orderReference }} - Total: {{ price.toFixed(2) + "€" }}</p>
108+
<!--app.html-->
109+
<h1>{{title()}}</h1>
110+
<p>Order ref. {{ orderReference() }} - Total: {{ price().toFixed(2) + "€" }}</p>
107111
```
108112

109-
The template has access to all the non-private members of the component class. Changing the visibility of `price` to `private` will render this error: *Property 'price' is private and only accessible within class 'AppComponent'.*
113+
The template has access to all the non-private members of the component class. Changing the visibility of `price` to `private` will render this error: *Property 'price' is private and only accessible within class 'App'.*
110114

111-
Interpolation only works on textual content of elements. You can not use it to change the value of HTML attributes or to insert HTML code. For this, you will need to resort to directives, which we will see later in the training.
115+
Interpolation only works on textual content of elements. You should not use it to change the value of HTML attributes and can't use it to insert HTML code. For this, we will see strategies later in the training.
112116

113117
In this example, we formatted the price manually. We will later see that Angular provides a way to declare reusable formatters: **pipes**.
114118

115119

116120
## Working with components
117121

118-
The `AppComponent` is only the root component of an Angular application. A web application is made of small reusable components, embedded in higher level components to form the layout, the arrangement of your elements on the page. This structure can be described as a component tree. Angular creates, updates, and destroys components as the user moves through the application. The app can take action at each moment in this lifecycle through optional lifecycle hooks, like `ngOnInit()`.
122+
The `App` component is only the root component of an Angular application. A web application is made of small reusable components, embedded in higher level components to form the layout, the arrangement of your elements on the page. This structure can be described as a component tree. Angular creates, updates, and destroys components as the user moves through the application. The app can take action at each moment in this lifecycle through optional lifecycle hooks, like `ngOnInit()`.
119123

120124
![Component tree](../assets/tree.png)
121125

126+
Before generating a second component, let's configure the naming convention of components and other key building blocks of an Angular app. Since Angular 20, the type of building block (component, service, directive) generated is not automatically appended to the file name and class name. That gives more flexibility to teams in their naming choices, however it can easily lead to naming clashs where the component, the service and the DTO have exactly the same class name for instance. This is why we will add the following configuration to the `angular.json` file to restore the appending of the building block name to the class name and file name.
127+
128+
```json{7-32}
129+
// angular.json
130+
"cli": {
131+
"schematicCollections": [
132+
"angular-eslint"
133+
]
134+
},
135+
"schematics": {
136+
"@schematics/angular:component": {
137+
"type": "component"
138+
},
139+
"@schematics/angular:directive": {
140+
"type": "directive"
141+
},
142+
"@schematics/angular:service": {
143+
"type": "service"
144+
},
145+
"@schematics/angular:guard": {
146+
"typeSeparator": "."
147+
},
148+
"@schematics/angular:interceptor": {
149+
"typeSeparator": "."
150+
},
151+
"@schematics/angular:module": {
152+
"typeSeparator": "."
153+
},
154+
"@schematics/angular:pipe": {
155+
"typeSeparator": "."
156+
},
157+
"@schematics/angular:resolver": {
158+
"typeSeparator": "."
159+
}
160+
}
161+
```
162+
122163
Let's create a second component. It is advised to generate components using the [Angular CLI](https://angular.dev/cli/generate#component).
123164

124165
```bash
@@ -128,7 +169,7 @@ ng g c child #shorthand for ng generate component child
128169
The `ng g c` command added a `child` folder containing the `ChildComponent` files in the `app` folder.
129170

130171
```typescript
131-
// child.component.ts
172+
// child.ts
132173
import { Component } from '@angular/core'
133174

134175
@Component({
@@ -142,30 +183,30 @@ export class ChildComponent {
142183
}
143184
```
144185

145-
To link the components together, the child components are declared in their parent's component template, using their selector as a tag. The child component also needs to be added to the `imports` array of the parent component decorator. A component can be reused as many times as desired. The `ChildComponent`'s selector is `app-child`. Including this component as a child to the `AppComponent` is done as follows:
186+
To link the components together, the child components are declared in their parent's component template, using their selector as a tag. The child component also needs to be added to the `imports` array of the parent component decorator. A component can be reused as many times as desired. The `ChildComponent`'s selector is `app-child`. Including this component as a child to the `App` component is done as follows:
146187

147188
<CodeGroup>
148-
<CodeGroupItem title="app.component.html">
189+
<CodeGroupItem title="app.html">
149190

150191
```html
151-
<h1>{{title}}</h1>
192+
<h1>{{title()}}</h1>
152193
<app-child></app-child>
153194
```
154195
</CodeGroupItem>
155-
<CodeGroupItem title="app.component.ts">
196+
<CodeGroupItem title="app.ts">
156197

157198
```ts{2,6}
158-
import { Component } from '@angular/core'
199+
import { Component, signal } from '@angular/core'
159200
import { ChildComponent } from './child/child.component'
160201
161202
@Component({
162203
selector: 'app-root',
163204
imports: [ChildComponent],
164-
templateUrl: './app.component.html',
165-
styleUrl: './app.component.scss'
205+
templateUrl: './app.html',
206+
styleUrl: './app.scss'
166207
})
167208
export class AppComponent {
168-
title = 'Search Films'
209+
protected readonly title = signal('Search Films')
169210
}
170211
```
171212
</CodeGroupItem>
@@ -221,15 +262,18 @@ Here is the folder structure we will strive to achieve in the Search Films appli
221262
<!-- TODO replace image-->
222263
![Simple folder structure](../assets/folder-structure.png)
223264

224-
This folder structure is best suited to simple projects with only one main feature that has its routes defined in one place, the `app-route.ts` file. As a project grows, feature folders and routes will be introduced and the structure can evolve to this:
265+
This folder structure is best suited to simple projects with only one main feature that has its routes defined in one place, the `app.route.ts` file. As a project grows, feature folders and routes will be introduced and the structure can evolve to this:
225266

267+
<!-- TODO replace image -->
226268
![Multi feature folder structure](../assets/folder-structure-multi-module.png)
227269

270+
The Angular team provides a [style guide](https://angular.dev/style-guide) where they advise to follow such a structure instead of the first one. When in doubt, refer to it.
271+
228272
:::tip
229273
By default, the CLI will always generate in the `app` folder. You can tell it to generate in another folder by passing the path before the name of the element you want it to generate. For instance `ng generate component components/dashboard` will generate the `DashboardComponent` 4 files in `app/components/dashboard`. The `components` folder is created by the CLI if it doesn't already exist, as well as the `dashboard` folder.
230274
:::
231275

232-
As the complexity of the folder structure of the application increases, it is a good practice to add aliases in the `tsconfig.json` file. Let's do it now to avoid a tedious refactoring later:
276+
<!-- As the complexity of the folder structure of the application increases, it is a good practice to add aliases in the `tsconfig.json` file. Let's do it now to avoid a tedious refactoring later:
233277
```json
234278
"compilerOptions": {
235279
//...
@@ -243,16 +287,16 @@ As the complexity of the folder structure of the application increases, it is a
243287
}
244288
}
245289
```
246-
VsCode will automatically use those paths for the imports instead of relative ones that can be tough to read or maintain.
290+
VsCode will automatically use those paths for the imports instead of relative ones that can be tough to read or maintain. -->
247291

248292

249293
## Practical work: Your first component
250294

251-
1. If you created the `child` component, delete it as we won't need it moving forward and remove any reference to it in the `AppComponent`. You may get an error in the console where you launched the project once you delete the child component, in that case restart the project.
295+
1. If you created the `app-child` component, delete it as we won't need it moving forward and remove any reference to it in the `App` component. You may get an error in the console where you launched the project once you delete the child component, in that case restart the project.
252296

253297
2. Most apps strive for a consistent look across the application. The CLI generated an empty `styles.scss` file for this purpose. Copy paste the content of the SCSS stylesheet that will serve as basis for all the practical work, downloadable here: [styles.scss](https://worldline.github.io/angular-training/styles.scss) in it.
254298

255-
3. Create a new component login-form containing the following authentication form (don't forget to generate it in the *components* folder):
299+
3. Create a new component `login-form` containing the following authentication form (don't forget to generate it in the *components* folder):
256300
```html
257301
<form id="login-form">
258302
<h1>Authentication</h1>
@@ -269,9 +313,9 @@ VsCode will automatically use those paths for the imports instead of relative on
269313

270314
```
271315

272-
4. Delete the existing content of the `AppComponent` template (html file of the component) if it is not already done and the variables declared in the `AppComponent` class. Display the `LoginFormComponent` in the `AppComponent` by addding `<app-login-form></app-login-form>` in its template. Don't forget to add the `LoginFormComponent` class to the `imports` array of the @Component definition of the `AppComponent`.
316+
4. Delete the existing content of the `App` component template (html file of the component) if it is not already done and the variables declared in the `App` class. Display the `LoginFormComponent` in the `App` component by addding `<app-login-form></app-login-form>` in its template. Don't forget to add the `LoginFormComponent` class to the `imports` array of the @Component definition of the `App` class (if you used VSCode aucompletion to add the new component to the App component template, then it should be added automatically).
273317

274-
5. Complete the `login-form.component.ts` file: add a field `title = 'Authentication'`. Then, use text interpolation in the template to pass the title from the component ts file to the h1 tag.
318+
5. Complete the `login-form.ts` file: add a field `protected readonly title = signal('Authentication')`. Then, use text interpolation in the template to pass the title from the component ts file to the h1 tag. Don't forget that you need to invoke a signal to read its value.
275319

276320
6. Don't forget to commit
277321

0 commit comments

Comments
 (0)