Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DOCS Update tutorial to recommend tree-shakeable providers #22934

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 0 additions & 5 deletions aio/content/examples/toh-pt4/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { HeroesComponent } from './heroes/heroes.component';
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
Expand All @@ -19,13 +18,9 @@ import { MessagesComponent } from './messages/messages.component';
FormsModule
],
// #docregion providers
// #docregion providers-heroservice
providers: [
// #enddocregion providers-heroservice
// no need to place any providers due to the `providedIn` flag...
// #docregion providers-heroservice
],
// #enddocregion providers-heroservice
// #enddocregion providers
bootstrap: [ AppComponent ]
})
Expand Down
4 changes: 3 additions & 1 deletion aio/content/examples/toh-pt4/src/app/hero.service.1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import { Hero } from './hero';
import { HEROES } from './mock-heroes';

// #docregion new
@Injectable({providedIn: 'root'})
@Injectable({
providedIn: 'root',
})
export class HeroService {

constructor() { }
Expand Down
4 changes: 3 additions & 1 deletion aio/content/examples/toh-pt4/src/app/hero.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import { HEROES } from './mock-heroes';
import { MessageService } from './message.service';
// #enddocregion import-message-service

@Injectable({ providedIn: 'root' })
@Injectable({
providedIn: 'root',
})
export class HeroService {

// #docregion ctor
Expand Down
4 changes: 3 additions & 1 deletion aio/content/examples/toh-pt4/src/app/message.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
@Injectable({
providedIn: 'root',
})
export class MessageService {
messages: string[] = [];

Expand Down
68 changes: 30 additions & 38 deletions aio/content/tutorial/toh-pt4.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Using the Angular CLI, create a service called `hero`.
</code-example>

The command generates skeleton `HeroService` class in `src/app/hero.service.ts`
The `HeroService` class should look like the below.
The `HeroService` class should look like the following example.

<code-example path="toh-pt4/src/app/hero.service.1.ts" region="new"
title="src/app/hero.service.ts (new service)" linenums="false">
Expand All @@ -40,19 +40,10 @@ The `HeroService` class should look like the below.
### _@Injectable()_ services
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code example should match what the CLI generates

@Injectable({
  providedIn: 'root',
})
export class HeroService {


Notice that the new service imports the Angular `Injectable` symbol and annotates
the class with the `@Injectable()` decorator.
the class with the `@Injectable()` decorator. This marks the class as one that participates in the _dependency injection system_. The `HeroService` class is going to provide an injectable service, and it can also have its own injected dependencies.
It doesn't have any dependencies yet, but [it will soon](#inject-message-service).

The `@Injectable()` decorator tells Angular that this service _might_ itself
have injected dependencies.
It doesn't have dependencies now but [it will soon](#inject-message-service).
Whether it does or it doesn't, it's good practice to keep the decorator.

<div class="l-sub-section">

The Angular [style guidelines](guide/styleguide#style-07-04) strongly recommend keeping it
and the linter enforces this rule.

</div>
The `@Injectable()` decorator accepts a metadata object for the service, the same way the `@Component()` decorator did for your component classes.

### Get hero data

Expand All @@ -76,32 +67,39 @@ Add a `getHeroes` method to return the _mock heroes_.
{@a provide}
## Provide the `HeroService`

You must _provide_ the `HeroService` in the _dependency injection system_
You must make the `HeroService` available to the dependency injection system
before Angular can _inject_ it into the `HeroesComponent`,
as you will do [below](#inject).
as you will do [below](#inject). You do this by registering a _provider_. A provider is something that can create or deliver a service; in this case, it instantiates the `HeroService` class to provide the service.

There are several ways to provide the `HeroService`:
in the `HeroesComponent`, in the `AppComponent`, in the `AppModule`.
Each option has pros and cons.
Now, you need to make sure that the `HeroService` is registered as the provider of this service.
You are registering it with an _injector_, which is the object that is responsible for choosing and injecting the provider where it is required.

This tutorial chooses to provide it in the `AppModule`.
By default, the Angular CLI command `ng generate service` registers a provider with the _root injector_ for your service by including provider metadata in the `@Injectable` decorator.

That's such a popular choice that you could have told the CLI to provide it there automatically
by appending `--module=app`.
If you look at the `@Injectable()` statement right before the `HeroService` class definition, you can see that the `providedIn` metadata value is 'root':

<code-example language="sh" class="code-shell">
ng generate service hero --module=app
</code-example>
```
@Injectable({
providedIn: 'root',
})
```

Since you did not, you'll have to provide it yourself.
When you provide the service at the root level, Angular creates a single, shared instance of `HeroService` and injects into any class that asks for it.
Registering the provider in the `@Injectable` metadata also allows Angular to optimize an app by removing the service if it turns out not to be used after all.

Open the `AppModule` class, import the `HeroService`, and add it to the `@NgModule.providers` array.
<div class="l-sub-section">

<code-example path="toh-pt4/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (providers)" region="providers-heroservice">
If you need to, you can register providers at different levels:
in the `HeroesComponent`, in the `AppComponent`, in the `AppModule`.
For instance, you could have told the CLI to provide the service at the module level automatically by appending `--module=app`.

<code-example language="sh" class="code-shell">
ng generate service hero --module=app
</code-example>

The `providers` array tells Angular to create a single, shared instance of `HeroService`
and inject into any class that asks for it.
To learn more about providers and injectors, see the [Dependency Injection guide](guide/dependency-injection).

</div>

The `HeroService` is now ready to plug into the `HeroesComponent`.

Expand All @@ -111,17 +109,12 @@ This is a interim code sample that will allow you to provide and use the `HeroSe

</div>

<div class="alert is-helpful">

Learn more about _providers_ in the [Providers](guide/providers) guide.

</div>

## Update `HeroesComponent`

Open the `HeroesComponent` class file.

Delete the `HEROES` import as you won't need that anymore.
Delete the `HEROES` import, because you won't need that anymore.
Import the `HeroService` instead.

<code-example path="toh-pt4/src/app/heroes/heroes.component.ts" title="src/app/heroes/heroes.component.ts (import HeroService)" region="hero-service-import">
Expand Down Expand Up @@ -295,10 +288,9 @@ You should see the default paragraph from `MessagesComponent` at the bottom of t
### Create the _MessageService_

Use the CLI to create the `MessageService` in `src/app`.
The `--module=app` option tells the CLI to [_provide_ this service](#provide) in the `AppModule`,

<code-example language="sh" class="code-shell">
ng generate service message --module=app
ng generate service message
</code-example>

Open `MessageService` and replace its contents with the following.
Expand Down Expand Up @@ -442,7 +434,7 @@ Here are the code files discussed on this page and your app should look like thi
## Summary

* You refactored data access to the `HeroService` class.
* You _provided_ the `HeroService` in the root `AppModule` so that it can be injected anywhere.
* You registered the `HeroService` as the _provider_ of its service at the root level so that it can be injected anywhere in the app.
* You used [Angular Dependency Injection](guide/dependency-injection) to inject it into a component.
* You gave the `HeroService` _get data_ method an asynchronous signature.
* You discovered `Observable` and the RxJS _Observable_ library.
Expand Down