docs: clarify hierarchical injectors (#28700)

PR Close #28700
This commit is contained in:
Kapunahele Wong
2019-02-13 12:50:30 -05:00
committed by Andrew Kushnir
parent 3cf2005a93
commit 1c6516199e
55 changed files with 1638 additions and 194 deletions

View File

@ -0,0 +1,21 @@
import { browser, element, by } from 'protractor';
describe('Resolution-modifiers-example', function () {
beforeAll(function () {
browser.get('');
});
it('shows basic flower emoji', function() {
expect(element.all(by.css('p')).get(0).getText()).toContain('🌸');
});
it('shows basic leaf emoji', function() {
expect(element.all(by.css('p')).get(1).getText()).toContain('🌿');
});
it('shows yellow flower in host child', function() {
expect(element.all(by.css('p')).get(9).getText()).toContain('🌼');
});
});

View File

@ -0,0 +1,14 @@
<h1>DI resolution modifiers</h1>
<p>Basic flower service: {{flower.emoji}}</p>
<p>Basic leaf service: {{leaf.emoji}}</p>
<app-optional></app-optional>
<app-self></app-self>
<app-self-no-data></app-self-no-data>
<app-skipself></app-skipself>
<app-host-parent></app-host-parent>

View File

@ -0,0 +1,13 @@
import { Component } from '@angular/core';
import { LeafService } from './leaf.service';
import { FlowerService } from './flower.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular';
constructor(public flower: FlowerService, public leaf: LeafService) {}
}

View File

@ -0,0 +1,33 @@
;
import { OptionalComponent } from './optional/optional.component';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { SelfNoDataComponent } from './self-no-data/self-no-data.component';
import { HostComponent } from './host/host.component';
import { SelfComponent } from './self/self.component';
import { SkipselfComponent } from './skipself/skipself.component';
import { HostParentComponent } from './host-parent/host-parent.component';
import { HostChildComponent } from './host-child/host-child.component';
@NgModule({
imports: [
BrowserModule,
FormsModule
],
declarations: [
AppComponent,
OptionalComponent,
SelfComponent,
SelfNoDataComponent,
HostComponent,
SkipselfComponent,
HostParentComponent,
HostChildComponent
],
bootstrap: [AppComponent],
providers: []
})
export class AppModule { }

View File

@ -0,0 +1,9 @@
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root' // provide this service in the root ModuleInjector
})
export class FlowerService {
emoji = '🌸';
}

View File

@ -0,0 +1,5 @@
.section {
border: 2px solid #369;
padding: 1rem;
margin: 1rem 0;
}

View File

@ -0,0 +1,4 @@
<div class="section">
<h2>Child of @Host() Component</h2>
<p>Flower emoji: {{flower.emoji}}</p>
</div>

View File

@ -0,0 +1,11 @@
import { Component } from '@angular/core';
import { FlowerService } from '../flower.service';
@Component({
selector: 'app-host-child',
templateUrl: './host-child.component.html',
styleUrls: ['./host-child.component.css']
})
export class HostChildComponent {
constructor(public flower: FlowerService) { }
}

View File

@ -0,0 +1,5 @@
.section {
border: 2px solid #369;
padding: 1rem;
margin: 1rem 0;
}

View File

@ -0,0 +1,5 @@
<div class="section">
<h2>Parent of @Host() Component</h2>
<p>Flower emoji: {{flower.emoji}}</p>
<app-host></app-host>
</div>

View File

@ -0,0 +1,16 @@
import { Component } from '@angular/core';
import { FlowerService } from '../flower.service';
@Component({
selector: 'app-host-parent',
templateUrl: './host-parent.component.html',
styleUrls: ['./host-parent.component.css'],
providers: [{ provide: FlowerService, useValue: { emoji: '🌺' } }]
})
export class HostParentComponent {
constructor(public flower: FlowerService) { }
}

View File

@ -0,0 +1,5 @@
.section {
border: 2px solid #369;
padding: 1rem;
margin: 1rem 0;
}

View File

@ -0,0 +1,6 @@
<div class="section">
<h2>@Host() Component</h2>
<p>Flower emoji: {{flower.emoji}}</p>
<p><i>(@Host() stops it here)</i></p>
<app-host-child></app-host-child>
</div>

View File

@ -0,0 +1,21 @@
import { Component, Host, Optional } from '@angular/core';
import { FlowerService } from '../flower.service';
// #docregion host-component
@Component({
selector: 'app-host',
templateUrl: './host.component.html',
styleUrls: ['./host.component.css'],
// provide the service
providers: [{ provide: FlowerService, useValue: { emoji: '🌼' } }]
})
export class HostComponent {
// use @Host() in the constructor when injecting the service
constructor(@Host() @Optional() public flower: FlowerService) { }
}
// #enddocregion host-component
// if you take out @Host() and the providers array, flower will be red hibiscus

View File

@ -0,0 +1,11 @@
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
// #docregion leafservice
export class LeafService {
emoji = '🌿';
}
// #enddocregion leafservice

View File

@ -0,0 +1,7 @@
import { Injectable } from '@angular/core';
@Injectable()
export class OptionalService {
}
// This service isn't provided anywhere.

View File

@ -0,0 +1,5 @@
.section {
border: 2px solid #369;
padding: 1rem;
margin: 1rem 0;
}

View File

@ -0,0 +1,4 @@
<div class="section">
<h2>@Optional() Component</h2>
<p>This component still works even though the OptionalService (notice @Optional() in the consturctor isn't provided or configured anywhere. Angular goes through tree and visibilty rules, and if it doesn't find the requested service, returns null.</p>
</div>

View File

@ -0,0 +1,21 @@
import { Component, Optional } from '@angular/core';
import { OptionalService } from '../optional.service';
@Component({
selector: 'app-optional',
templateUrl: './optional.component.html',
styleUrls: ['./optional.component.css']
})
// #docregion optional-component
export class OptionalComponent {
constructor(@Optional() public optional: OptionalService) {}
}
// #enddocregion optional-component
// The OptionalService isn't provided here, in the @Injectable()
// providers array, or in the NgModule. If you remove @Optional()
// from the constructor, you'll get an error.

View File

@ -0,0 +1,5 @@
.section {
border: 2px solid #369;
padding: 1rem;
margin: 1rem 0;
}

View File

@ -0,0 +1,4 @@
<div class="section">
<h2>@Self() Component (without a provider)</h2>
<p>Leaf emoji: {{leaf?.emoji}}</p>
</div>

View File

@ -0,0 +1,18 @@
import { Component, Self, Optional } from '@angular/core';
import { LeafService } from '../leaf.service';
// #docregion self-no-data-component
@Component({
selector: 'app-self-no-data',
templateUrl: './self-no-data.component.html',
styleUrls: ['./self-no-data.component.css']
})
export class SelfNoDataComponent {
constructor(@Self() @Optional() public leaf: LeafService) { }
}
// #enddocregion self-no-data-component
// The app doesn't break because the value being available at self is optional.
// If you remove @Optional(), the app breaks.

View File

@ -0,0 +1,5 @@
.section {
border: 2px solid #369;
padding: 1rem;
margin: 1rem 0;
}

View File

@ -0,0 +1,4 @@
<div class="section">
<h2>@Self() Component</h2>
<p>Flower emoji: {{flower?.emoji}}</p>
</div>

View File

@ -0,0 +1,19 @@
import { Component, Self } from '@angular/core';
import { FlowerService } from '../flower.service';
// #docregion self-component
@Component({
selector: 'app-self',
templateUrl: './self.component.html',
styleUrls: ['./self.component.css'],
providers: [{ provide: FlowerService, useValue: { emoji: '🌼' } }]
})
export class SelfComponent {
constructor(@Self() public flower: FlowerService) {}
}
// #enddocregion self-component
// This component provides the FlowerService so the injector
// doesn't have to look further up the injector tree

View File

@ -0,0 +1,5 @@
.section {
border: 2px solid #369;
padding: 1rem;
margin: 1rem 0;
}

View File

@ -0,0 +1,4 @@
<div class="section">
<h2>@SkipSelf() Component</h2>
<p>Leaf emoji: {{leaf.emoji}}</p>
</div>

View File

@ -0,0 +1,18 @@
import { Component, SkipSelf } from '@angular/core';
import { LeafService } from '../leaf.service';
// #docregion skipself-component
@Component({
selector: 'app-skipself',
templateUrl: './skipself.component.html',
styleUrls: ['./skipself.component.css'],
// Angular would ignore this LeafService instance
providers: [{ provide: LeafService, useValue: { emoji: '🍁' } }]
})
export class SkipselfComponent {
// Use @SkipSelf() in the constructor
constructor(@SkipSelf() public leaf: LeafService) { }
}
// #enddocregion skipself-component
// @SkipSelf(): Specifies that the dependency resolution should start from the parent injector, not here.

View File

@ -0,0 +1,14 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>DI Resolution Modifiers Example</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root>Loading...</app-root>
</body>
</html>

View File

@ -0,0 +1,11 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@ -0,0 +1,10 @@
{
"description": "NgModules",
"files": [
"!**/*.d.ts",
"!**/*.js",
"!**/*.[1,2].*"
],
"file": "src/app/app.component.ts",
"tags": ["NgModules"]
}