` tag:
```html
`` </body>
```
---
#### Standalone Component
.breadcrumbs[Angular > Getting started > Components]
The `standalone` property of the `@Component` decorator indicates that this component does not need to be part of a larger angular module (we'll see later what exactly is a module).
```ts
@Component({
selector: 'app-root',
`standalone: true`,
imports: [CommonModule, RouterOutlet],
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
```
This property should be there with a value of `true` for all your components, wether they've been created manually or with the help of the CLI.
---
#### Component requirements
.breadcrumbs[Angular > Getting started > Components]
The `import` property of the `@Component` decorator tells Angular what other components or modules this component requires in order to do its thing.
```ts
@Component({
selector: 'app-root',
standalone: true,
`imports: [CommonModule, RouterOutlet]`,
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
```
You will need to update this `imports` array while building your components when needed.
---
#### Component template
.breadcrumbs[Angular > Getting started > Components]
The `templateUrl` property of the `@Component` decorator tells Angular which HTML file to use as this component's template:
> This template file defines the HTML structure of the component when displayed on the page
```ts
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, RouterOutlet],
`templateUrl: './app.component.html'`,
styleUrl: './app.component.css'
})
```
For components with very simple template, you can also use the `template` property instead of `templateUrl` to use an **inline template**:
```ts
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, RouterOutlet],
`template: 'Welcome to {{ title }}!
'`,
styleUrl: './app.component.css'
})
```
---
#### Component styles
.breadcrumbs[Angular > Getting started > Components]
Similarly, the `styleUrls` property is a list of CSS files to apply to the component :
```ts
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, RouterOutlet],
templateUrl: './app.component.html',
`styleUrl: './app.component.css'`
})
```
It's also possible to use **inline styles** for very simple component styles:
```ts
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, RouterOutlet],
templateUrl: './app.component.html',
`styles: [ 'h1 { font-weight: normal; }' ]`
})
```
> Styles in Angular are **scoped to the component**. They apply **only to its template**.
Read the [Component Styles][angular-component-styles] documentation to learn more.
---
### Data binding
.breadcrumbs[Angular > Getting started]
You can display data by **binding** parts of an HTML template to properties of the associated component.
There is already a `title` property on `AppComponent` in `src/app/app.component.ts`:
```ts
export class AppComponent {
title = "angular-starter";
}
```
Enclosing a component's property name in double curly braces in the template is called **interpolation**.
You can do so in the `AppComponent`'s template in `src/app/app.component.html` by removing **all** the placeholder (except the `router-outlet` tag) and pasting the following code:
```html
Welcome to `{{ title }}`!
```
This will convert the value of the `title` property to a `string` and concatenate it with the rest of the content.
---
#### Attribute binding
.breadcrumbs[Angular > Getting started > Data binding]
Let's see what else we can interpolate.
Add a new `titleComment` property to the component in `src/app/app.component.ts`:
```ts
export class AppComponent {
title = "angular-starter";
titleComment = "This is awesome!";
}
```
Angular's bracket syntax (`[...]`) allows you to use the **value of a component's property** as the value of a DOM element's **attribute value**.
You can do this in `src/app/app.component.html`:
```html
Welcome to {{ title }}!
```
---
##### To bind, or not to bind
.breadcrumbs[Angular > Getting started > Data binding > Attribute binding]
Note the two syntax when binding to attributes.
Below, as in the previous example, the `[...]` syntax **binds the `titleComment` property value** with the `title` attribute:
```html
Welcome to {{ title }}!
```
Below, the `titleComment` property **value is interpolated** with the `{{ ... }}` syntax and the result is used as the `title` attribute value.
> In this instance, the end result will effectively be the same as the previous example.
```html
Welcome to {{ title }}!
```
> Here, we made a **mistake**.
> We neither bound the attribute, nor used interpolation in its value,
> so the value of the attribute will be the string `"titleComment"`,
> not the value of the corresponding variable:
```html
Welcome to {{ title }}!
```
---
##### When to use which?
.breadcrumbs[Angular > Getting started > Data binding > Attribute binding]
Since the bracket syntax (`[...]`) and the curly braces syntax (`{{...}}`) have apparently **the same result**,
why and when should you use one over the other?
If you want to use a component's property value as **the exact value of an element attribute**, you use brackets around the attribute:
```html
...
```
> The **value** of `title` will always be **exactly** the value of `titleComment` and will have the **same type**.
If you want to use a component's property **as part of the string value** of an element attribute, you use curly braces:
```html
...
```
> The **actual value** of `title` will always be "Custom Title:" plus **the string representation** of `titleComment`'s value.
---
#### Binding to events
.breadcrumbs[Angular > Getting started > Data binding]
Let's imagine that we want to log something when the user clicks on the title.
Add an `onTitleClicked()` method to the component's class in `src/app/app.component.ts`:
```ts
export class AppComponent {
// ...
* onTitleClicked() {
* console.log('The title was clicked');
* }
}
```
Angular's `(...)` syntax allows you to **bind functions to events** occurring on a DOM element.
Let's bind the function we just created to `click` events on the `` tag in `src/app/app.component.html`:
```html
Welcome to {{ title }}!
```
You should now see the message displayed in the console when clicking on the title.
---
##### Getting at the event
.breadcrumbs[Angular > Getting started > Data binding > Binding to events]
You might sometimes need the object reprensenting the actual [event][dom-event] to get some data out of it (for example, the click coordinates).
Let's update `onTitleClicked()` in `src/app/app.component.ts` to accept an event parameter and log it:
```ts
export class AppComponent {
// ...
onTitleClicked(`event: MouseEvent`) {
console.log('The title was clicked'`, event`);
}
}
```
To make this work, pass the special `$event` value as an argument to your function call in `src/app/app.component.html`, and Angular will pass the event object to your function:
```html
Welcome to {{ title }}!
```
> Note that you **must** use exactly `$event` as the argument's value
---
#### Interpolation with functions
.breadcrumbs[Angular > Getting started > Data binding]
Interpolation is not limited to simple properties.
You can also use a component's **method** in the template.
Add the following method to the component in `src/app/app.component.ts`:
```ts
export class AppComponent {
// ...
* hello(name: string): string {
* return \`Hello ${name}`;
* }
}
```
Now use that function in the template in `src/app/app.component.html`:
```html
`{{ hello("World") }}`
```
> Change the value passsed to the `hello()` function to see it change.
---
## User input
.breadcrumbs[Angular]
One of the things you will need to do is **react to user input** (for example, through forms).
Make the following changes to the component in `src/app/app.component.ts`:
```ts
export class AppComponent {
// ...
`greeting = '';`
// ...
}
```
Now use that new property as the `hello` function's argument in the template in `src/app/app.component.html`:
```html
{{ hello(`greeting`) }}
```
---
### `ngModel`
.breadcrumbs[Angular > User input]
Add an input field to the template above the greeting in `src/app/app.component.html`:
```html
*
{{ hello(greeting) }}
```
`[(ngModel)]` is Angular's **two-way data binding** syntax.
It binds the **`greeting` property value** to the HTML **`input` value**, and _vice-versa_.
You will most likely get this error:
```
NG8002: Can't bind to 'ngModel' since it isn't a known property of 'input'.
src/app/app.component.html:3:49
```
This means that Angular does not recognize the `ngModel` property on the `input`.
This is because `[(ngModel)]` is provided by the optional `FormsModule`, which you have to _opt in_ to use.
---
#### `FormsModule`
.breadcrumbs[Angular > User input > `ngModel`]
To use the features of the `FormsModule`, you need to add it the `imports` array of your component.
You can do this in `src/app/app.component.ts`:
```ts
// Other imports...
*import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-root',
standalone: true,
imports: [ CommonModule, RouterOutlet, `FormsModule` ],
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent { /* ... */ }
```
Once you've done that, the error should be gone and everything should display as expected.
Now, as you type inside the input field, the `greeting` variable is **automatically kept up-to-date** in the background,
and Angular **updates the template** to reflect the new value.
---
### Two-way data binding
.breadcrumbs[Angular > User input]
.grid-50[
Traditional templating systems bind data **in only one direction**.
The developer has to write code that constantly syncs the view with the model and vice versa.
]
.grid-50[
With Angular (and most modern frameworks), changes are **immediately reflected** in both view and model.
Also note that our **component** is **isolated from and unaware of the view**:
it does not care about DOM manipulation or rendering concerns.
]
---
## Directives
.breadcrumbs[Angular]
Just as **Components** can be seen as **custom HTML tags** that display and behave as defined by their template and class,
**Directives** can be seen as **custom HTML attributes** that can be added to HTML elements (or Angular components) to apply them various effects.
> Technically, a **Directive** is a class decorated with the `@Directive` decorator.
Directives in Angular can be divided into two categories: **structural** and **attribute** directives.
---
### Structural directives
.breadcrumbs[Angular > Directives]
Structural directives have an impact on the HTML layout.
They shape or reshape the **DOM's structure**, typically by **adding, removing, or manipulating elements**.
Let's add the [`ngIf`][angular-docs-ng-if] directive to our template in `src/app/app.component.html` as an example:
```html
{{ hello(greeting) }}
```
> The `*` before `ngIf` in the template is not a typo ; structural directives must **always** be preceeded by a `*`.
As you can see, the entire paragraph is now removed from the DOM as long as the `greeting` property is _falsy_ (e.g. `undefined` or an empty string).
It is added back to the page as soon as `greeting` has a _truthy_ value.
> Read the [documentation][angular-structural-directives] to learn more about structural directives. Many more such directives are provided by Angular out of the box, like [`ngFor`][angular-docs-ng-for] (which we'll use later) and [`ngSwitch`][angular-docs-ng-switch].
---
### Attribute directives
.breadcrumbs[Angular > Directives]
> Want to create your own attributes directives ? See [advanced-angular][advanced-angular-subject]
An **attribute** directive changes the **appearance or behavior of the DOM element** to which it is attached.
Let's say we want to programmatically toggle some classes to our `` tag in our template: one that italize it, and another that toggle the fact that it can be hovered.
Let's add those CSS classes in our `app.component.css`:
```css
.italic {
font-style: italic;
}
.hoverable {
cursor: pointer;
}
```
Now, we need a way to track which one is active or not. Let's do so with two boolean properties in our component's class un `src/app/app.component.ts`:
```ts
export class AppComponent {
// Previous properties
* italicTitle = false;
* hoverableTitle = false;
// ...
}
```
---
#### Define and bind the classes
.breadcrumbs[Angular > Directives > Attribute directives]
We can now use the `ngClass` directive on our `` tag to tell Angular **when each class should be active or inactive**.
The `ngClass` directive accepts **an object argument**. Each of this object property is named exactly after the CSS class we want to apply _(add quotes if necessary)_ and has a **boolean value**.
When the boolean value is `true`, the class is applied ; when the value is `false`, the class is removed.
```html
Welcome to {{ title }}!
```
> Since our component has two boolean properties, we can reference them in the `ngClass` argument. Those two properties have a value of `false`. Thus the two CSS classes are currently not applied.
---
#### Dynamic changing
.breadcrumbs[Angular > Directives > Attribute directives]
To actually toggle those classes, let's add some interaction in our page in the form of two buttons, each of which will toggle one of the CSS classes:
Add the following snippet right after the `` tag:
```html
```
Notice how each button has a `(click)` callback that reassign one of the boolean properties values each time it is triggerred.
When **a property changes its value**, the `ngClass` directive sees this change, and **reapply the CSS classes** accordingly.
> You can now click on the button to dynamically add or remove each CSS class.
---
### Common directives
.breadcrumbs[Angular > Directives]
These common directives are provided by Angular out of the box:
**Structural directives**
- [`ngFor`][angular-docs-ng-for] - Instantiates a template **once per item** from an iterable.
- [`ngIf`][angular-docs-ng-if] - **Conditionally includes** a template based on the value of an expression.
- [`ngSwitch`][angular-docs-ng-switch] - Adds/removes DOM sub-trees when the nest match expressions matches the **switch** expression.
**Attribute directives**
- [`ngClass`][angular-docs-ng-class] - Adds and removes **CSS classes** on an HTML element.
- [`ngModel`][angular-docs-ng-model] - **Two-way binding** between a form's input field and a component's variable.
- [`ngPlural`][angular-docs-ng-plural] - Adds/removes DOM sub-trees based on a numeric value. (Tailored for **pluralization**.)
- [`ngStyle`][angular-docs-ng-style] - Update an HTML element's **styles**.
---
## Pipes
.breadcrumbs[Angular]
When primitive values or objects are interpolated into a template, they are serialized by Angular using their `toString()` method.
That's fine for strings, but not for everything:
- What about **numbers**? You might not want to display all their decimals.
- What about **currencies**? They are usually displayed in a specific format.
- What about **dates**? You might want to use a simple format like `April 15, 1988` rather than the default `Fri Apr 15 1988 00:00:00 GMT-0700 (Pacific Daylight Time)`.
Clearly, some values benefit from a bit of editing and that's what Pipes are all about.
They're special functions that your value will pass through before being rendered in the template.
> Want to implement your own pipes? See [advances-angular][advanced-angular-subject]
---
### Use case
.breadcrumbs[Angular > Pipes]
Open the `src/app/app.component.ts` file and add a new `progress` property:
```ts
export class AppComponent {
// Previous properties
* progress = 0.45;
// ...
}
```
We want to display this value as a percentage in our template. We _could_ do it like so:
```html
{{ progress `* 100` }}`%`
```
Doing so implies that we repeat this same snippet each time we want to display a percentage in our app. This is error prone as we might forget the `%`, or add an exceeding `0` when multiplying our value, etc.
We'd be better off having this logic in a `Pipe` so we could use it any time we want.
---
### Using a pipe
.breadcrumbs[Angular > Pipes]
In this case, Angular provides us with a `Pipe` named `percent` that does display values as percentages and thus, can help us rendering our progress percentage in our template.
Actually using the `Pipe` is as simple as providing a value to the `Pipe` using the pipe (`|`) character in a template:
```html
We've only done {{ progress` | percent` }} of those damned slides
```
> You can see that this built-in pipe multiply the given value and add the `%` character at the end.
---
### Angular built-in pipes
.breadcrumbs[Angular > Pipes]
Angular provides [a few pipes][angular-docs-pipes] out of the box.
Here's some of them:
- [`CurrencyPipe`][angular-docs-currency-pipe]
- [`DatePipe`][angular-docs-date-pipe]
- [`DecimalPipe`][angular-docs-decimal-pipe]
- [`LowerCasePipe`][angular-docs-lowercase-pipe], [`UpperCasePipe`][angular-docs-uppercase-pipe] & [`TitleCasePipe`][angular-docs-titlecase-pipe]
- [`PercentPipe`][angular-docs-percent-pipe]
Here's few usage examples for [`DatePipe`][angular-docs-date-pipe]:
```html
Today is {{ dateValue | date }}
Or if you prefer, {{ dateValue | date:'fullDate' }}
The time is {{ dateValue | date:'shortTime' }}
```
---
## Modules
.breadcrumbs[Angular]
Modules are a great way to **organize an application** and extend it with capabilities from external libraries.
Many internal or external librairies are `NgModules` that **export** some `Components`, `Pipes` or `Directives` for you to use them in your own code.
In a Standalone Angular application, they are mainly used to create **blocks of functionnality**, grouping together other classes that focus on **a similar business need**.
You could have a module that group together all things related to user management, another grouping all things related to backend API interaction, and so on.
> Using Angular modules in your application is **entirely optional**. They are a tool at your disposal to improve and simplify the architecture of an application.
> You will encounter them when developping so you need to know what they do.
---
### Anatomy of a module
.breadcrumbs[Angular > Modules]
As are many of Angular's structure, a module is a class annotated with the `@NgModule` decorator, which accept an metadata objects, with the following notable properties:
```ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CustomerDashboardComponent } from './customer-dashboard/customer-dashboard.component';
@NgModule({
`imports`: [ CommonModule ],
`declarations`: [ CustomerDashboardComponent ],
`exports`: [ CustomerDashboardComponent ]
})
export class CustomerDashboardModule { }
```
- `imports`: A list of other `NgModules` or standalone `Component`s **required by this module features** ;
- `declarations`: A list of `Component`s, `Directive`s or `Pipe`s that **belong to this module** ;
- `exports`: A list of `Component`s, `Directive`s or `Pipe`s that **this module exports to other** `Component`s or `NgModule`s
---
### Shared Module
.breadcrumbs[Angular > Modules]
If you start building many standalone components, and you notice that, for many of them, you add in their `imports` array the same list of classes, like this:
```ts
// Necessary imports
@Component({
// ...
imports: [`CommonModule`, `FormsModule`, LoaderComponent, `ButtonComponent`],
// ...
})
export class ComponentA {}
```
```ts
// Necessary imports
@Component({
// ...
imports: [
`CommonModule`,
`FormsModule`,
`ButtonComponent`,
GeolocationModule,
],
// ...
})
export class ComponentB {}
```
.grid-100[
..you might want to consider grouping those imports into a **Shared Module**.
]
---
#### Sharing is caring
.breadcrumbs[Angular > Modules > Shared Module]
This Shared Module will import, then re-export, all those shared classes, so that your components can import only the module.
```ts
// Necessary imports
@NgModule({
imports: [`CommonModule, FormsModule, LoaderComponent, ButtonComponent`],
exports: [`CommonModule, FormsModule, LoaderComponent, ButtonComponent`],
})
export class SharedModule {}
```
.grid-50[
```ts
// Necessary imports
@Component({
// ...
imports: [`SharedModule`, LoaderComponent],
// ...
})
export class ComponentA {}
```
]
.grid-50[
```ts
// Necessary imports
@Component({
// ...
imports: [`SharedModule`, GeolocationModule],
// ...
})
export class ComponentB {}
```
]
.grid-100[
> With this technique, your code will be more concise and streamlined.
> [Read more about Shared Module][angular-shared-module].
]
---
## Models
.breadcrumbs[Angular]
Now, let's make our application funny by adding some jokes.
It's a good practice to create **types, interfaces or classes** to define the structure of the objects you will manipulate in your application. This will help you avoid mistakes in your code.
Let's start with a very simple one.
Create a new file at `src/app/jokes/joke.type.ts` with the following content:
```ts
export type Joke = {
`text: string;`
}
```
> **Angular CLI**: Use `ng generate class ` to create the files for a new model class
---
### Using models
.breadcrumbs[Angular > Models]
Let's add some jokes to our component in `src/app/app.component.ts`:
```ts
// Other imports...
`import { Joke } from './jokes/joke.type';`
@Component({ /* ... */})
export class AppComponent {
// ...previous properties
`jokes: Joke[];` // The jokes property is an array of only Joke-like objects
constructor() {
// ...
* this.jokes = [
* { text: 'Knock knock' },
* { text: 'The cake is a lie' }
* ];
}
// ...
}
```
---
### Using `ngFor`
.breadcrumbs[Angular > Models]
Now that we have some jokes, let's display them.
We want a `` list, with a `- ` item for each joke.
That's a job for the [`ngFor`][angular-docs-ng-for] directive.
Add this at the bottom of the component's template in `src/app/app.component.html`:
```html
```
> The directive handles repeating the `
- ` element for us.
> No need to write it multiple times, or to manually build and concatenate DOM elements in the component's TypeScript code.
---
### Using `ngPlural`
.breadcrumbs[Angular > Models]
While we're at it, let's also add a header above the list:
```html
{{ jokes.length }} jokes
```
You might notice that we'll have a minor problem when there is only one joke.
It will say "1 jokes" instead of "1 joke".
The [`ngPlural`][angular-docs-ng-plural] directive comes to the rescue:
```html
{{ jokes.length }} * joke *
jokes
```
---
## Services
.breadcrumbs[Angular]
Let's do something more interesting: fetch those jokes from the internet rather than hard-coding them in our component.
To do it "The Angular Way", we'll encapsulate that functionality into a **Service**.
Why?
- **Components** should not try to do too much;
they should focus on **presenting data** and **delegate data access** to specialized classes.
**Services** are here to fill that role.
> This helps your components remain as simple as possible while services handle your business logic and shared state.
- **Services** can be used by multiples Components, Directives, Pipes, etc, which helps in repeating the same logic or using a shared state.
> **Angular CLI**: Use `ng generate service ` to create all the files for a new service
---
### The joke service
.breadcrumbs[Angular > Services]
Once again, a service is simply a JavaScript class, annotated with the [`@Injectable`][angular-docs-injectable] decorator (more about that later).
Create a new `src/app/jokes/joke.service.ts` file with the following content:
```ts
import { Injectable } from '@angular/core';
`import { Joke } from '../jokes/joke.type';`
@Injectable({ providedIn: 'root' })
export class JokeService {
* getJoke(): Joke {
* return { text: 'Knock knock' };
* }
}
```
> For now, the service only returns a single hard-coded `Joke`. We will actually fetch them from the web later on.
---
### Providing the joke service
.breadcrumbs[Angular > Services]
You may have notice the `providedIn: 'root'` property in the `@Injectable` param object of the generated service.
This particular settings indicates that **the service is provided by the root of your application**, which means that it is accessible to all your `Components`/`Directives`/`Pipes`.
> You might encouter `Services` that do not have this setting in their `@Injectable` decorator (i.e. when using external libraries). If this is the case, you must manually **provide** them in a component or module's `providers` array:
.grid-50[
```ts
// Other imports...
import { SomeService } from "./some/directory";
@Component({
// ...
providers: [`SomeService`],
// ...
})
export class SomeComponent {}
```
]
.grid-50[
```ts
// Other imports...
import { SomeService } from "./some/directory";
@NgModule({
// ...
providers: [`SomeService`],
// ...
})
export class SomeModule {}
```
]
---
### Injecting the joke service
.breadcrumbs[Angular > Services]
Now, you can **reference** the service into your component in `src/app/app.component.ts`.
You just have to add a **constructor parameter property**.
While you're at it, also create a **method to add a joke**:
```ts
// Other imports...
*import { JokeService } from './jokes/joke.service';
export class AppComponent {
// ...
constructor(`private jokeService: JokeService`) {
// ...
}
* addJoke() {
* this.jokes.push(this.jokeService.getJoke());
* }
// ...
}
```
---
#### Calling a method from a button
.breadcrumbs[Angular > Services > Injecting the joke service]
To use our new method, you can add a button in the template in `src/app/app.component.html`:
```html
```
> The `(click)` attribute is Angular's syntax to listen to the `click` event on a DOM element and trigger something when it occurs.
---
### Why does it work?
.breadcrumbs[Angular > Services]
Our component now uses the service.
But why does it work?
All you did was add a parameter property to the constructor:
```ts
constructor(`private jokeService: JokeService`) {
// ...
}
```
> As a reminder, in TypeScript this is equivalent to:
```ts
export class AppComponent {
`jokeService: JokeService;`
constructor(`jokeService: JokeService`) {
`this.jokeService = jokeService;`
}
}
```
You **never instantiated the service with `new`**, so where is the instance coming from?
---
### Dependency injection
.breadcrumbs[Angular > Services]
Angular relies on [dependency injection][di] to plug components, services and other elements together.
- As previously said, your service is **provided** in the **root** of your application.
- This makes it possible for Angular's **injector** to know that your service exists and to create an instance of it.
- By adding the parameter to the component's constructor, you **indicated that Angular should inject** this service instance at runtime.
---
#### Why dependency injection?
.breadcrumbs[Angular > Services > Dependency injection]
.grid-50[
**Strong coupling**
```js
function Car() {
this.engine = new Engine(24);
}
function Engine(gasLead) {
this.gas = new Gas(gasLead);
}
function Gas(lead) {
this.lead = lead;
}
```
]
.grid-50[
**Loose coupling** (with an _injector_)
```js
function Car(engine) {
this.engine = engine;
}
function Engine(gas) {
this.gas = gas;
}
function Gas(lead) {
this.lead = lead;
}
```
]
---
## Observable data
.breadcrumbs[Angular]
Our current `getJoke()` method has an **synchronous** signature; implying that data is returned right away:
```ts
const joke = jokeService.getJoke();
```
This will **not** work when fetching jokes from a **remote server**, which is inherently an **asynchronous** operation.
The `getJoke()` method must be modified to not immediately return a joke, but to have an asynchronous signature instead.
It could take a **callback** or return a [**Promise**][js-promise].
Since Angular uses the [RxJS][rxjs] library internally when handling HTTP requests, our service method should return an `Observable` of a `Joke`.
> To learn more about `Observables` and the `RxJS` library, see [the corresponding subject][rxjs-subject].
---
### Making `getJoke()` observable
.breadcrumbs[Angular > Observable data]
For now, let's modify the signature of our `getJoke()` method in `JokeService` in `src/app/jokes/joke.service.ts` to return an `Observable` of a `Joke`, without actually making an HTTP call yet:
```ts
// Other imports...
*import { Observable, of } from 'rxjs';
@Injectable()
export class JokeService {
// ...
getJoke(): `Observable` {
return `of({ text: 'Knock knock' })`;
}
}
```
> Note the `<>` in `Observable`. This is syntax used by TypeScript to specify a type when using a [generic type](../ts/#37).
> Here, it indicates not only that the method returns an `Observable`, but that it emits specifically `Joke` values.
`of` allows us to create an `Observable` that will simply emit the specified values (here, one `Joke`), then complete.
---
### Subscribing to an Observable
.breadcrumbs[Angular > Observable data]
Of course, the code in our component no longer works now,
since it expects a `Joke` and gets an `Observable` instead:
```
ERROR in src/app/app.component.ts(26,21): error TS2345:
Argument of type 'Observable' is not assignable to parameter of type 'Joke'.
Property 'text' is missing in type 'Observable'.
```
In `AppComponent` in `src/app/app.component.ts`, use the `subscribe()` method of the returned `Observable` to add the `Joke` to the array once it's emitted :
```ts
addJoke() {
* this.jokeService.getJoke().subscribe(joke => {
* this.jokes.push(joke);
* });
}
```
We now have our **asynchronous** implementation:
- We **subscribe** to the Observable when `addJoke` is called.
- But the **callback** adding the new joke into the array will be called **later**,
after the data has been fetched from the remote server.
---
## Making HTTP calls
.breadcrumbs[Angular]
Time to actually fetch some jokes from the internet!
We'll need Angular's [`HttpClient`][angular-docs-http-client] to do so, and we can get it by updating our application's config in `app.config.ts`:
```ts
import { /* Other imports */, provideHttpClient } from '@angular/core';
/* Other imports */
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
* provideHttpClient()
],
};
```
> `importProvidersFrom(...)` will collect all the things provided by its parameters and re-provide them globally.
---
### Injecting `HttpClient`
.breadcrumbs[Angular > Making HTTP calls]
Earlier we annotated `JokeService` with the [`@Injectable`][angular-docs-injectable] decorator.
This not only makes it available to the **injector** for creation,
but also allows it to **have dependencies of its own**.
Now that `HttpClient` is available through `HttpClientModule`, you can inject `HttpClient` into `JokeService` in `src/app/jokes/joke.service.ts`, by adding it to the constructor parameters:
```ts
// Other imports...
*import { HttpClient } from '@angular/common/http';
@Injectable()
export class JokeService {
constructor(`private http: HttpClient`) { }
// ...
}
```
---
### Joke API response
.breadcrumbs[Angular > Making HTTP calls]
The API we are going to call returns JSON data that looks like this:
```json
{
"icon_url": "https://assets.chucknorris.host/img/avatar/chuck-norris.png",
"id": "wDuoHg3CSVSZM1AXzFdDuA",
"url": "",
"value": "Chuck Norris doesnt believe in religion, religion believes in Chuck Norris"
}
```
This does not fit our `Joke` model, which only has a `text` property.
---
#### Joke API response model
.breadcrumbs[Angular > Making HTTP calls > Joke API response]
Let's create a new `JokeResponse` model that we can use with this API.
Create a new file at `src/app/jokes/joke-response.type.ts` and define a `type` that matches the structure of the API response:
```ts
export type JokeResponse = {
icon_url: string;
id: string;
url: string;
value: string;
};
```
---
### Making a GET call
.breadcrumbs[Angular > Making HTTP calls]
We can now update `getJoke()` in `src/app/jokes/joke.service.ts` to make an actual HTTP call:
```ts
// Other imports...
*import { JokeResponse } from '../jokes/joke-response.type';
@Injectable()
export class JokeService {
// ...
getJoke(): Observable {
* return this.http
* .get('https://api.chucknorris.io/jokes/random');
}
}
```
> Using generics, we also specified that the [`HttpClient`][angular-docs-http-client]'s `get` method is supposed to receive data matching the `JokeResponse` model after the call completes. This helps TypeScript understand our code better.
But we're still left with one problem: we need an Observable of `Joke` objects, and have one of `JokeResponse` objects instead:
```
ERROR in src/app/jokes/joke.service.ts(15,5): error TS2322:
Type 'Observable' is not assignable to type 'Observable'.
Type 'JokeResponse' is not assignable to type 'Joke'.
Property 'text' is missing in type 'JokeResponse'.
```
---
### Transforming data
.breadcrumbs[Angular > Making HTTP calls]
We need to be able to transform a `JokeResponse` object into a `Joke`.
Let's add a utility function at the bottom of the file in `src/app/jokes/joke.service.ts`:
```ts
function convertJokeResponseToJoke(response: JokeResponse): Joke {
return {
text: response.value,
};
}
```
Effectively, this function allows mapping a `JokeResponse` to a `Joke`.
---
### Transforming Observable streams
.breadcrumbs[Angular > Making HTTP calls]
Let's use this new mapping function in the method that fetches the joke so that the final Observable it returns is an `Observable`:
```ts
// Other imports...
*import { map } from 'rxjs/operators';
// ...
getJoke(): Observable {
return this.httpClient
.get('https://api.chucknorris.io/jokes/random')
* .pipe(map(convertJokeResponseToJoke));
}
```
> If you forgot or don't know how this `map` operator works, check out the [RxJS subject][rxjs-subject-map]
---
### Reacting to errors in observable streams
.breadcrumbs[Angular > Making HTTP calls]
An observable stream may emit an **error**.
You can be notified of that error when subscribing to an `Observable`. Instead of passing a single callback, **pass an object** with at least one property between `next`, `error` and `complete`.
Let's do so in `AppComponent` in `src/app/app.component.ts`:
```ts
addJoke() {
this.jokeService.getJoke().subscribe({
next: joke => this.jokes.push(joke),
error: err => console.warn('Could not get new joke', err)
// ^^^ In a real application, you should display an error on the screen.
});
}
```
- `next`: the function to execute when an event is emitted. You'll get the event as its parameter ;
- `error`: the function to execute when an error occurs. You'll get the error as its parameter ;
- `complete`: the function to execute when the `Observable` complets. You'll get no parameter.
---
### Make'em fail!
.breadcrumbs[Angular > Making HTTP calls]
You can produce an error by changing the URL in `JokeService`in `src/app/jokes/joke.service.ts`,
so that the call fails.
```ts
getJoke(): Observable {
return this.httpClient
.get('https://`foo.example.com`/jokes/random')
.pipe(map(convertJokeResponseToJoke));
}
```
You can then change it back to the correct URL.
---
## Component interaction
.breadcrumbs[Angular]
As described earlier, components are Angular's fundamental UI building blocks.
We're going to add a few features to our application:
- The ability to **vote** on which are the best jokes.
- The ability to see the **total** number of votes and how many votes the **best** joke has had.
- The ability to **clear** all the collected votes.
.grid-50[
We could implement all of this in `AppComponent`,
but that would not be viable in a real-world scenario with more complex features.
When you have a complex page with multiple areas that each have their specific logic,
it's good practice to **isolate each part into a component**:
]
.grid-50[
]
---
### Adding votes to the model
.breadcrumbs[Angular > Component interaction]
Update the `Joke` model in `src/app/jokes/joke.type.ts` to have a `votes` property:
```ts
export type Joke = {
text: string;
* votes: number;
}
```
You need to update `src/app/app.component.ts` to set the initial votes to `0`:
```ts
this.jokes = [
{ text: "Knock knock"`, votes: 0` },
{ text: "The cake is a lie"`, votes: 0` },
];
```
You also need to update the `convertJokeResponseToJoke` function in `src/app/jokes/joke.service.ts`:
```ts
function convertJokeResponseToJoke(response: JokeResponse): Joke {
return {
text: response.value.joke,
* votes: 0
};
}
```
---
### Creating a component
.breadcrumbs[Angular > Component interaction]
Let's generate a **new component**, `JokeComponent`, whose responsibility will be to properly display a `Joke` object in the page, and provide a button to vote for the joke:
```ts
$> npx ng generate component joke/joke --skip-tests
```
This will create a component in the `src/app/jokes/joke` directory,
with its own TypeScript definition, HTML template and CSS styles.
Noice that the `Component` in `src/app/jokes/joke/joke.component.ts` as an empty `imports` array. We will use the `NgIf` directive in its template, we thus need to add the `CommonModule` there:
```ts
// Other imports
*import { CommonModule } from '@angular/common';
@Component({
selector: 'app-joke',
standalone: true,
imports: [ `CommonModule` ],
templateUrl: './joke.component.html',
styleUrl: './joke.component.css'
})
export class JokeComponent {
}
```
---
#### The `JokeComponent`
.breadcrumbs[Angular > Component interaction > Creating a component]
Let's add an optional `joke` property to the new component in `src/app/jokes/joke/joke.component.ts`:
```ts
// Other imports...
*import { Joke } from 'src/app/jokes/joke.type';
@Component({
selector: 'app-joke',
standalone: true,
imports: [],
templateUrl: './joke.component.html',
styleUrl: './joke.component.css'
})
export class JokeComponent {
* joke?: Joke;
// ...
}
```
> Remember that a property defined with a `?` can be `undefined`.
And update the component's template in `src/app/jokes/joke/joke.component.html` to display the joke's text (if there is a joke to display):
```html
{{ joke.text }}
```
---
### Using our new component
.breadcrumbs[Angular > Component interaction]
As you can see in our component's class, its `selector` has been set to `app-joke`.
This means that we can include our component in another component's template by adding an `` tag.
Let's do so in `src/app/app.component.html`:
```html
```
We can now see on our app page that we have two elements in our list, but that they are empty.
Indeed, we told our `JokeComponent` to display the `joke.text` property in its template, but we didn't actually **gave** it any `joke` to get the `text` from...
---
### Passing data from parent to child with input binding
.breadcrumbs[Angular > Component interaction]
We want the joke to be passed to the `JokeComponent` as an **input**.
Annotating a component's property with the [`@Input`][angular-docs-input] decorator marks it as an **input property**.
You can do this in `src/app/jokes/joke/joke.component.ts`:
```ts
// Other imports...
import { Component, `Input` } from '@angular/core';
// ...
export class JokeComponent {
`@Input()` joke?: Joke;
// ...
}
```
Now, update the main component's template in `src/app/app.component.html` so that it binds each joke object to the `joke` input of the `` tag.
```html
```
---
### Voting on jokes
.breadcrumbs[Angular > Component interaction]
Now that `JokeComponent` is working, we can use it to **handle the logic related to one joke**, like voting.
Add a `vote()` method to `JokeComponent` in `src/app/jokes/joke/joke.component.ts`:
```ts
vote() {
if (this.joke) {
this.joke.votes++;
}
}
```
Add these 2 lines to the component's template in `src/app/jokes/joke/joke.component.html` to display the number of votes and add a button to vote for the joke:
```html
{{ joke.text }} * ({{ joke.votes }} votes) *
```
You can now vote!
---
### Displaying global voting information
.breadcrumbs[Angular > Component interaction]
Let's now display the **total number of votes** and the **best vote** on the page.
That's the job of the **main component**, since a `JokeComponent` only knows about its own joke,
so it can't know the total number of votes or whether its number of votes is the highest.
Add this information to `AppComponent` in `src/app/app.component.ts`:
```ts
export class AppComponent {
// ...
* bestVote: number;
* totalVotes: number;
constructor(private jokeService: JokeService) {
* this.bestVote = 0;
* this.totalVotes = 0;
// ...
}
// ...
}
```
And display it in the template in `src/app/app.component.html`:
```html
Total votes: {{ totalVotes }}, best vote: {{ bestVote }}
```
---
### Output from child components
.breadcrumbs[Angular > Component interaction]
The vote button is in the child component's template, so `AppComponent` can't put an event listener on it directly.
Instead, we need our `JokeComponent` to have an **output** that its parent can listen to.
Annotating a component's property with the [`@Output`][angular-docs-output] decorator marks it as an **output property**.
It must be an [`EventEmitter`][angular-docs-event-emitter] (or an `Observable`).
Let's add one to `JokeComponent` in `src/app/jokes/joke/joke.component.ts` now:
```ts
import { Component, `EventEmitter`, Input, `Output` } from '@angular/core';
// ...
export class JokeComponent {
// ...
* @Output() voted: EventEmitter;
constructor() {
* this.voted = new EventEmitter();
}
vote() {
if (this.joke) {
this.joke.votes++;
* this.voted.emit(this.joke);
}
}
}
```
---
### Listening to child component events from a parent
.breadcrumbs[Angular > Component interaction]
Let's add an `onJokeVoted()` method to `AppComponent` in `src/app/app.component.ts`:
```ts
onJokeVoted(joke: Joke) {
this.totalVotes++;
if (joke.votes > this.bestVote) {
this.bestVote = joke.votes;
}
}
```
We want this method to be called every time a vote button is clicked in a child `JokeComponent`.
From the parent's point of view, an **output property** of a child component is **just like any other DOM event**.
You bind to it using Angular's `(event)='expression'` syntax, exactly like you bind to `(click)` on a `