docs(dependency-injection): add Dart version, tweak TS version
closes #972
This commit is contained in:
parent
e17821492e
commit
10877bdbfc
|
@ -0,0 +1,16 @@
|
||||||
|
# Supported lint rules and documentation: http://dart-lang.github.io/linter/lints/
|
||||||
|
linter:
|
||||||
|
rules:
|
||||||
|
- always_declare_return_types
|
||||||
|
- camel_case_types
|
||||||
|
- empty_constructor_bodies
|
||||||
|
- annotate_overrides
|
||||||
|
- avoid_init_to_null
|
||||||
|
- constant_identifier_names
|
||||||
|
- one_member_abstracts
|
||||||
|
- slash_for_doc_comments
|
||||||
|
- sort_constructors_first
|
||||||
|
- unnecessary_brace_in_string_interp
|
||||||
|
|
||||||
|
analyzer:
|
||||||
|
# strong-mode: true
|
|
@ -1,15 +1,14 @@
|
||||||
// #docplaster
|
// #docplaster
|
||||||
|
|
||||||
// #docregion
|
// #docregion
|
||||||
|
|
||||||
// #docregion imports
|
// #docregion imports
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
|
import 'app_config.dart';
|
||||||
import 'car/car_component.dart';
|
import 'car/car_component.dart';
|
||||||
import 'heroes/heroes_component.dart';
|
import 'heroes/heroes_component.dart';
|
||||||
import 'app_config.dart';
|
|
||||||
import 'logger_service.dart';
|
import 'logger_service.dart';
|
||||||
import 'user_service.dart';
|
import 'user_service.dart';
|
||||||
|
//PENDING: check whether we intend to hide injector_component.dart & providers_component.dart; if so, change docregion name?
|
||||||
// #enddocregion imports
|
// #enddocregion imports
|
||||||
import 'injector_component.dart';
|
import 'injector_component.dart';
|
||||||
import 'providers_component.dart';
|
import 'providers_component.dart';
|
||||||
|
@ -17,33 +16,39 @@ import 'providers_component.dart';
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'my-app',
|
selector: 'my-app',
|
||||||
template: '''
|
template: '''
|
||||||
<h1>{{title}}</h1>
|
<h1>{{title}}</h1>
|
||||||
<my-car></my-car>
|
<my-car></my-car>
|
||||||
<my-injectors></my-injectors>
|
<my-injectors></my-injectors>
|
||||||
<my-tests></my-tests>
|
<my-tests></my-tests>
|
||||||
<h2>User</h2>
|
<h2>User</h2>
|
||||||
<p id="user">
|
<p id="user">
|
||||||
{{userInfo}}
|
{{userInfo}}
|
||||||
<button (click)=\'nextUser()\'>Next User</button>
|
<button (click)="nextUser()">Next User</button>
|
||||||
<p>
|
<p>
|
||||||
<my-heroes id="authorized" *ngIf="isAuthorized"></my-heroes>
|
<my-heroes id="authorized" *ngIf="isAuthorized"></my-heroes>
|
||||||
<my-heroes id="unauthorized" *ngIf="!isAuthorized"></my-heroes>
|
<my-heroes id="unauthorized" *ngIf="!isAuthorized"></my-heroes>''',
|
||||||
''',
|
directives: const [
|
||||||
directives: const [CarComponent, HeroesComponent, InjectorComponent, ProvidersComponent],
|
CarComponent,
|
||||||
|
HeroesComponent,
|
||||||
|
InjectorComponent,
|
||||||
|
ProvidersComponent
|
||||||
|
],
|
||||||
// #docregion providers
|
// #docregion providers
|
||||||
providers: const [Logger, UserService, const Provider(Config, useValue: CONFIG)]
|
providers: const [
|
||||||
|
Logger,
|
||||||
|
UserService,
|
||||||
|
const Provider(AppConfig, useValue: config1)]
|
||||||
// #enddocregion providers
|
// #enddocregion providers
|
||||||
)
|
)
|
||||||
class AppComponent {
|
class AppComponent {
|
||||||
UserService _userService;
|
final UserService _userService;
|
||||||
String title;
|
final String title;
|
||||||
|
|
||||||
//#docregion ctor
|
//#docregion ctor
|
||||||
AppComponent(Config config, this._userService) {
|
AppComponent(AppConfig config, this._userService)
|
||||||
title = config.title;
|
: title = config.title;
|
||||||
}
|
|
||||||
|
|
||||||
// #enddocregion ctor
|
// #enddocregion ctor
|
||||||
|
|
||||||
bool get isAuthorized {
|
bool get isAuthorized {
|
||||||
return user.isAuthorized;
|
return user.isAuthorized;
|
||||||
}
|
}
|
||||||
|
@ -56,8 +61,7 @@ class AppComponent {
|
||||||
return _userService.user;
|
return _userService.user;
|
||||||
}
|
}
|
||||||
|
|
||||||
String get userInfo {
|
String get userInfo => 'Current user, ${user.name}, is'
|
||||||
return 'Current user, ${user.name}, is ${isAuthorized ? "" : "not"} authorized. ';
|
'${isAuthorized ? "" : " not"} authorized. ';
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
|
|
||||||
// #docregion
|
// #docregion
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
import 'car/car_component.dart';
|
import 'car/car_component.dart';
|
||||||
import 'heroes/heroes_component_1.dart';
|
import 'heroes/heroes_component_1.dart';
|
||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'my-app',
|
selector: 'my-app',
|
||||||
template: '''
|
template: '''
|
||||||
<h1>{{title}}</h1>
|
<h1>{{title}}</h1>
|
||||||
<my-car></my-car>
|
<my-car></my-car>
|
||||||
<my-heroes></my-heroes>
|
<my-heroes></my-heroes>''',
|
||||||
''',
|
|
||||||
directives: const [CarComponent, HeroesComponent])
|
directives: const [CarComponent, HeroesComponent])
|
||||||
class AppComponent {
|
class AppComponent {
|
||||||
var title = 'Dependency Injection';
|
final String title = 'Dependency Injection';
|
||||||
}
|
}
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
|
||||||
|
|
|
@ -2,34 +2,32 @@
|
||||||
|
|
||||||
// #docregion imports
|
// #docregion imports
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
|
import 'app_config.dart';
|
||||||
import 'car/car_component.dart';
|
import 'car/car_component.dart';
|
||||||
import 'heroes/heroes_component_1.dart';
|
import 'heroes/heroes_component_1.dart';
|
||||||
import 'app_config.dart';
|
|
||||||
import 'logger_service.dart';
|
import 'logger_service.dart';
|
||||||
|
|
||||||
// #enddocregion imports
|
// #enddocregion imports
|
||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'my-app',
|
selector: 'my-app',
|
||||||
template: '''
|
template: '''
|
||||||
<h1>{{title}}</h1>
|
<h1>{{title}}</h1>
|
||||||
<my-car></my-car>
|
<my-car></my-car>
|
||||||
<my-heroes></my-heroes>
|
<my-heroes></my-heroes>''',
|
||||||
''',
|
|
||||||
directives: const [
|
directives: const [
|
||||||
CarComponent,
|
CarComponent,
|
||||||
HeroesComponent
|
HeroesComponent
|
||||||
],
|
],
|
||||||
providers: const [
|
providers: const [
|
||||||
Logger,
|
Logger,
|
||||||
// #docregion provider-config
|
const Provider(AppConfig, useValue: config1)
|
||||||
const Provider('app.config', useValue: CONFIG)
|
|
||||||
])
|
])
|
||||||
class AppComponent {
|
class AppComponent {
|
||||||
String title;
|
final String title;
|
||||||
|
|
||||||
// #docregion ctor
|
// #docregion ctor
|
||||||
AppComponent(@Inject('app.config') Config config) {
|
AppComponent(AppConfig config)
|
||||||
title = config.title;
|
: title = config.title;
|
||||||
}
|
// #enddocregion
|
||||||
}
|
}
|
||||||
// #enddocregion
|
|
||||||
|
|
|
@ -1,29 +1,17 @@
|
||||||
//#docregion
|
// #docregion
|
||||||
|
|
||||||
// #docregion token
|
// #docregion token
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
const APP_CONFIG = const OpaqueToken('app.config');
|
//#docregion const-class
|
||||||
// #enddocregion token
|
@Injectable()
|
||||||
|
class AppConfig {
|
||||||
//#docregion config
|
final apiEndpoint;
|
||||||
abstract class Config {
|
|
||||||
final String apiEndpoint;
|
|
||||||
final String title;
|
final String title;
|
||||||
|
|
||||||
const Config({this.apiEndpoint, this.title});
|
const AppConfig(this.apiEndpoint, this.title);
|
||||||
}
|
}
|
||||||
|
//#enddocregion const-class
|
||||||
|
|
||||||
class ConfigImpl implements Config {
|
//#docregion const-object
|
||||||
final String apiEndpoint;
|
const config1 = const AppConfig('api.heroes.com', 'Dependency Injection');
|
||||||
final String title;
|
//#enddocregion const-object
|
||||||
|
|
||||||
const ConfigImpl({this.apiEndpoint, this.title});
|
|
||||||
}
|
|
||||||
|
|
||||||
const CONFIG = const ConfigImpl(apiEndpoint: 'api.heroes.com', title: 'Dependency Injection');
|
|
||||||
//#enddocregion config
|
|
||||||
|
|
||||||
//#docregion config-hash
|
|
||||||
const CONFIG_HASH = const {'apiEndpoint': 'api.heroes.com', 'title': 'Dependency Injection'};
|
|
||||||
//#enddocregion config-hash
|
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
|
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
// #docregion engine
|
// #docregion engine
|
||||||
class Engine {
|
class Engine {
|
||||||
int cylinders = 4;
|
final int cylinders = 4;
|
||||||
}
|
}
|
||||||
// #enddocregion engine
|
// #enddocregion engine
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
// #docregion tires
|
// #docregion tires
|
||||||
class Tires {
|
class Tires {
|
||||||
String make = 'Flintstone';
|
String make = 'Flintstone';
|
||||||
|
@ -18,8 +19,8 @@ class Tires {
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class Car {
|
class Car {
|
||||||
//#docregion car-ctor
|
//#docregion car-ctor
|
||||||
Engine engine;
|
final Engine engine;
|
||||||
Tires tires;
|
final Tires tires;
|
||||||
String description = 'DI';
|
String description = 'DI';
|
||||||
|
|
||||||
Car(this.engine, this.tires);
|
Car(this.engine, this.tires);
|
||||||
|
@ -27,6 +28,7 @@ class Car {
|
||||||
// #enddocregion car-ctor
|
// #enddocregion car-ctor
|
||||||
|
|
||||||
// Method using the engine and tires
|
// Method using the engine and tires
|
||||||
String drive() => '$description car with ${engine.cylinders} cylinders and ${tires.make} tires.';
|
String drive() => '$description car with ${engine.cylinders} cylinders'
|
||||||
|
' and ${tires.make} tires.';
|
||||||
}
|
}
|
||||||
// #enddocregion car
|
// #enddocregion car
|
||||||
|
|
|
@ -1,29 +1,29 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
|
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
import 'car.dart';
|
import 'car.dart';
|
||||||
import 'car_no_di.dart' as carNoDi;
|
|
||||||
import 'car_factory.dart';
|
|
||||||
import 'car_creations.dart' as carCreations;
|
import 'car_creations.dart' as carCreations;
|
||||||
|
import 'car_factory.dart';
|
||||||
import 'car_injector.dart';
|
import 'car_injector.dart';
|
||||||
|
import 'car_no_di.dart' as carNoDi;
|
||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'my-car',
|
selector: 'my-car',
|
||||||
template: '''
|
template: '''
|
||||||
<h2>Cars</h2>
|
<h2>Cars</h2>
|
||||||
<div id="di">{{car.drive()}}</div>
|
<div id="di">{{car.drive()}}</div>
|
||||||
<div id="nodi">{{noDiCar.drive()}}</div>
|
<div id="nodi">{{noDiCar.drive()}}</div>
|
||||||
<div id="injector">{{injectorCar.drive()}}</div>
|
<div id="injector">{{injectorCar.drive()}}</div>
|
||||||
<div id="factory">{{factoryCar.drive()}}</div>
|
<div id="factory">{{factoryCar.drive()}}</div>
|
||||||
<div id="simple">{{simpleCar.drive()}}</div>
|
<div id="simple">{{simpleCar.drive()}}</div>
|
||||||
<div id="super">{{superCar.drive()}}</div>
|
<div id="super">{{superCar.drive()}}</div>
|
||||||
<div id="test">{{testCar.drive()}}</div>
|
<div id="test">{{testCar.drive()}}</div>''',
|
||||||
''',
|
|
||||||
providers: const [Car, Engine, Tires])
|
providers: const [Car, Engine, Tires])
|
||||||
class CarComponent {
|
class CarComponent {
|
||||||
Car car;
|
final Car car;
|
||||||
|
|
||||||
|
CarComponent(this.car);
|
||||||
|
|
||||||
CarComponent(this.car) {}
|
|
||||||
Car factoryCar = (new CarFactory()).createCar();
|
Car factoryCar = (new CarFactory()).createCar();
|
||||||
Car injectorCar = useInjector();
|
Car injectorCar = useInjector();
|
||||||
carNoDi.Car noDiCar = new carNoDi.Car();
|
carNoDi.Car noDiCar = new carNoDi.Car();
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
// Examples with car and engine variations
|
|
||||||
|
|
||||||
// #docplaster
|
// #docplaster
|
||||||
|
// Examples with car and engine variations
|
||||||
import 'car.dart';
|
import 'car.dart';
|
||||||
|
|
||||||
///////// example 1 ////////////
|
///////// example 1 ////////////
|
||||||
Car simpleCar() {
|
Car simpleCar() {
|
||||||
//#docregion car-ctor-instantiation
|
//#docregion car-ctor-instantiation
|
||||||
|
|
||||||
// Simple car with 4 cylinders and Flintstone tires.
|
// Simple car with 4 cylinders and Flintstone tires.
|
||||||
var car = new Car(new Engine(), new Tires());
|
var car = new Car(new Engine(), new Tires());
|
||||||
//#enddocregion car-ctor-instantiation
|
//#enddocregion car-ctor-instantiation
|
||||||
|
@ -17,19 +15,18 @@ Car simpleCar() {
|
||||||
|
|
||||||
//#docregion car-ctor-instantiation-with-param
|
//#docregion car-ctor-instantiation-with-param
|
||||||
class Engine2 implements Engine {
|
class Engine2 implements Engine {
|
||||||
int cylinders;
|
final int cylinders;
|
||||||
|
|
||||||
Engine2(this.cylinders);
|
Engine2(this.cylinders);
|
||||||
}
|
}
|
||||||
|
|
||||||
//#enddocregion car-ctor-instantiation-with-param
|
//#enddocregion car-ctor-instantiation-with-param
|
||||||
Car superCar() {
|
|
||||||
//#docregion car-ctor-instantiation-with-param
|
|
||||||
|
|
||||||
// Super car with 12 cylinders and Flintstone tires.
|
Car superCar() {
|
||||||
var bigCylinders = 12;
|
//#docregion car-ctor-instantiation-with-param
|
||||||
var car = new Car(new Engine2(bigCylinders), new Tires());
|
// Super car with 12 cylinders and Flintstone tires.
|
||||||
//#enddocregion car-ctor-instantiation-with-param
|
var bigCylinders = 12;
|
||||||
|
var car = new Car(new Engine2(bigCylinders), new Tires());
|
||||||
|
//#enddocregion car-ctor-instantiation-with-param
|
||||||
car.description = 'Super';
|
car.description = 'Super';
|
||||||
return car;
|
return car;
|
||||||
}
|
}
|
||||||
|
@ -37,7 +34,7 @@ Car superCar() {
|
||||||
|
|
||||||
//#docregion car-ctor-instantiation-with-mocks
|
//#docregion car-ctor-instantiation-with-mocks
|
||||||
class MockEngine extends Engine {
|
class MockEngine extends Engine {
|
||||||
int cylinders = 8;
|
final int cylinders = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockTires extends Tires {
|
class MockTires extends Tires {
|
||||||
|
@ -46,11 +43,10 @@ class MockTires extends Tires {
|
||||||
|
|
||||||
//#enddocregion car-ctor-instantiation-with-mocks
|
//#enddocregion car-ctor-instantiation-with-mocks
|
||||||
Car testCar() {
|
Car testCar() {
|
||||||
//#docregion car-ctor-instantiation-with-mocks
|
//#docregion car-ctor-instantiation-with-mocks
|
||||||
|
// Test car with 8 cylinders and YokoGoodStone tires.
|
||||||
// Test car with 8 cylinders and YokoGoodStone tires.
|
var car = new Car(new MockEngine(), new MockTires());
|
||||||
var car = new Car(new MockEngine(), new MockTires());
|
//#enddocregion car-ctor-instantiation-with-mocks
|
||||||
//#enddocregion car-ctor-instantiation-with-mocks
|
|
||||||
car.description = 'Test';
|
car.description = 'Test';
|
||||||
return car;
|
return car;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
|
|
||||||
import 'car.dart';
|
import 'car.dart';
|
||||||
|
|
||||||
|
// BAD pattern!
|
||||||
class CarFactory {
|
class CarFactory {
|
||||||
Car createCar() {
|
Car createCar() {
|
||||||
var car = new Car(createEngine(), createTires());
|
return new Car(createEngine(), createTires())
|
||||||
car.description = 'Factory';
|
..description = 'Factory';
|
||||||
return car;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Engine createEngine() => new Engine();
|
Engine createEngine() => new Engine();
|
||||||
|
|
||||||
Tires createTires() => new Tires();
|
Tires createTires() => new Tires();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
// #docplaster
|
// #docplaster
|
||||||
|
|
||||||
//#docregion
|
//#docregion
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
import 'car.dart';
|
|
||||||
import '../logger_service.dart';
|
import '../logger_service.dart';
|
||||||
|
import 'car.dart';
|
||||||
|
|
||||||
//#docregion injector
|
//#docregion injector
|
||||||
Car useInjector() {
|
Car useInjector() {
|
||||||
|
@ -24,8 +24,8 @@ Car useInjector() {
|
||||||
//#docregion injector-call
|
//#docregion injector-call
|
||||||
var car = injector.get(Car);
|
var car = injector.get(Car);
|
||||||
//#enddocregion injector-call
|
//#enddocregion injector-call
|
||||||
|
|
||||||
//#enddocregion injector-create-and-call
|
//#enddocregion injector-create-and-call
|
||||||
|
|
||||||
car.description = 'Injector';
|
car.description = 'Injector';
|
||||||
var logger = injector.get(Logger);
|
var logger = injector.get(Logger);
|
||||||
logger.log('Injector car.drive() said: ' + car.drive());
|
logger.log('Injector car.drive() said: ' + car.drive());
|
||||||
|
|
|
@ -13,10 +13,10 @@ class Car {
|
||||||
engine = new Engine();
|
engine = new Engine();
|
||||||
tires = new Tires();
|
tires = new Tires();
|
||||||
}
|
}
|
||||||
|
|
||||||
//#enddocregion car-ctor
|
//#enddocregion car-ctor
|
||||||
|
|
||||||
// Method using the engine and tires
|
// Method using the engine and tires
|
||||||
drive() => '$description car with ${engine.cylinders} cylinders and ${tires.make} tires.';
|
String drive() => '$description car with '
|
||||||
|
'${engine.cylinders} cylinders and ${tires.make} tires.';
|
||||||
}
|
}
|
||||||
//#enddocregion car
|
//#enddocregion car
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
|
|
||||||
class Hero {
|
class Hero {
|
||||||
num id;
|
num id;
|
||||||
String name;
|
String name;
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
|
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
import 'hero.dart';
|
import 'hero.dart';
|
||||||
import 'hero_service.dart';
|
import 'hero_service.dart';
|
||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'hero-list',
|
selector: 'hero-list',
|
||||||
template: '''
|
template: '''
|
||||||
<div *ngFor="#hero of heroes">
|
<div *ngFor="#hero of heroes">
|
||||||
{{hero.id}} - {{hero.name}}
|
{{hero.id}} - {{hero.name}}
|
||||||
({{hero.isSecret ? \'secret\' : \'public\'}})
|
({{hero.isSecret ? 'secret' : 'public'}})
|
||||||
</div>
|
</div>''')
|
||||||
''')
|
|
||||||
class HeroListComponent {
|
class HeroListComponent {
|
||||||
List<Hero> heroes;
|
final List<Hero> heroes;
|
||||||
|
|
||||||
//#docregion ctor-signature
|
//#docregion ctor-signature
|
||||||
HeroListComponent(HeroService heroService) : heroes = heroService.getHeroes();
|
HeroListComponent(HeroService heroService) : heroes = heroService.getHeroes();
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
|
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
import 'hero.dart';
|
import 'hero.dart';
|
||||||
import 'mock_heroes.dart';
|
import 'mock_heroes.dart';
|
||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'hero-list',
|
selector: 'hero-list',
|
||||||
template: '''
|
template: '''
|
||||||
<div *ngFor="#hero of heroes">
|
<div *ngFor="#hero of heroes">
|
||||||
{{hero.id}} - {{hero.name}}
|
{{hero.id}} - {{hero.name}}
|
||||||
</div>
|
</div>''')
|
||||||
''')
|
|
||||||
class HeroListComponent {
|
class HeroListComponent {
|
||||||
List<Hero> heroes = HEROES;
|
final List<Hero> heroes = HEROES;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
|
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
import 'hero.dart';
|
import 'hero.dart';
|
||||||
import 'hero_service.dart';
|
import 'hero_service.dart';
|
||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'hero-list',
|
selector: 'hero-list',
|
||||||
template: '''
|
template: '''
|
||||||
<div *ngFor="#hero of heroes">
|
<div *ngFor="#hero of heroes">
|
||||||
{{hero.id}} - {{hero.name}}
|
{{hero.id}} - {{hero.name}}
|
||||||
</div>
|
</div>''')
|
||||||
''')
|
|
||||||
class HeroListComponent {
|
class HeroListComponent {
|
||||||
List<Hero> heroes;
|
final List<Hero> heroes;
|
||||||
|
|
||||||
//#docregion ctor
|
//#docregion ctor
|
||||||
HeroListComponent(HeroService heroService) : heroes = heroService.getHeroes();
|
HeroListComponent(HeroService heroService)
|
||||||
|
: heroes = heroService.getHeroes();
|
||||||
//#enddocregion ctor
|
//#enddocregion ctor
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,24 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
|
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
|
import '../logger_service.dart';
|
||||||
import 'hero.dart';
|
import 'hero.dart';
|
||||||
import 'mock_heroes.dart';
|
import 'mock_heroes.dart';
|
||||||
import '../logger_service.dart';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class HeroService {
|
class HeroService {
|
||||||
// #docregion internals
|
// #docregion internals
|
||||||
Logger _logger;
|
final Logger _logger;
|
||||||
bool _isAuthorized;
|
final bool _isAuthorized;
|
||||||
|
|
||||||
HeroService(this._logger, this._isAuthorized);
|
HeroService(this._logger, this._isAuthorized);
|
||||||
|
|
||||||
List<Hero> getHeroes() {
|
List<Hero> getHeroes() {
|
||||||
var auth = _isAuthorized ? 'authorized' : 'unauthorized';
|
var auth = _isAuthorized ? 'authorized' : 'unauthorized';
|
||||||
_logger.log('Getting heroes for ${auth} user.');
|
_logger.log('Getting heroes for $auth user.');
|
||||||
return HEROES.where((hero) => _isAuthorized || !hero.isSecret).toList();
|
return HEROES
|
||||||
|
.where((hero) => _isAuthorized || !hero.isSecret)
|
||||||
|
.toList();
|
||||||
}
|
}
|
||||||
// #enddocregion internals
|
// #enddocregion internals
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
|
|
||||||
import 'hero.dart';
|
import 'hero.dart';
|
||||||
import 'mock_heroes.dart';
|
import 'mock_heroes.dart';
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
|
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
|
import '../logger_service.dart';
|
||||||
import 'hero.dart';
|
import 'hero.dart';
|
||||||
import 'mock_heroes.dart';
|
import 'mock_heroes.dart';
|
||||||
import '../logger_service.dart';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class HeroService {
|
class HeroService {
|
||||||
Logger _logger;
|
final Logger _logger;
|
||||||
|
|
||||||
//#docregion ctor
|
//#docregion ctor
|
||||||
HeroService(this._logger);
|
HeroService(this._logger);
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
|
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
import 'hero_service.dart';
|
|
||||||
import '../logger_service.dart';
|
import '../logger_service.dart';
|
||||||
import '../user_service.dart';
|
import '../user_service.dart';
|
||||||
|
import 'hero_service.dart';
|
||||||
|
|
||||||
// #docregion factory
|
// #docregion factory
|
||||||
|
@Injectable()
|
||||||
heroServiceFactory(Logger logger, UserService userService) =>
|
heroServiceFactory(Logger logger, UserService userService) =>
|
||||||
new HeroService(logger, userService.user.isAuthorized);
|
new HeroService(logger, userService.user.isAuthorized);
|
||||||
// #enddocregion factory
|
// #enddocregion factory
|
||||||
|
|
||||||
// #docregion provider
|
// #docregion provider
|
||||||
const heroServiceProvider =
|
const heroServiceProvider = const Provider(HeroService,
|
||||||
const Provider(HeroService, useFactory: heroServiceFactory, deps: const [Logger, UserService]);
|
useFactory: heroServiceFactory,
|
||||||
|
deps: const [Logger, UserService]);
|
||||||
// #enddocregion provider
|
// #enddocregion provider
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
|
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
import 'hero_list_component.dart';
|
import 'hero_list_component.dart';
|
||||||
import 'hero_service_provider.dart';
|
import 'hero_service_provider.dart';
|
||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'my-heroes',
|
selector: 'my-heroes',
|
||||||
template: '''
|
template: '''
|
||||||
<h2>Heroes</h2>
|
<h2>Heroes</h2>
|
||||||
<hero-list></hero-list>
|
<hero-list></hero-list>''',
|
||||||
''',
|
|
||||||
providers: const [heroServiceProvider],
|
providers: const [heroServiceProvider],
|
||||||
directives: const [HeroListComponent])
|
directives: const [HeroListComponent])
|
||||||
class HeroesComponent {}
|
class HeroesComponent {}
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
// #docplaster
|
// #docplaster
|
||||||
|
|
||||||
// #docregion
|
// #docregion
|
||||||
// #docregion v1
|
// #docregion v1
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
import 'hero_list_component.dart';
|
|
||||||
|
|
||||||
|
import 'hero_list_component.dart';
|
||||||
// #enddocregion v1
|
// #enddocregion v1
|
||||||
import 'hero_service.dart';
|
import 'hero_service.dart';
|
||||||
|
|
||||||
// #docregion v1
|
// #docregion v1
|
||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'my-heroes',
|
selector: 'my-heroes',
|
||||||
template: '''
|
template: '''
|
||||||
<h2>Heroes</h2>
|
<h2>Heroes</h2>
|
||||||
<hero-list></hero-list>
|
<hero-list></hero-list>''',
|
||||||
''',
|
|
||||||
// #enddocregion v1
|
// #enddocregion v1
|
||||||
// #docregion providers
|
// #docregion providers
|
||||||
providers: const [HeroService],
|
providers: const [HeroService],
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
|
|
||||||
import 'hero.dart';
|
import 'hero.dart';
|
||||||
|
|
||||||
List<Hero> HEROES = [
|
List<Hero> HEROES = [
|
||||||
|
|
|
@ -2,21 +2,21 @@
|
||||||
|
|
||||||
//#docregion
|
//#docregion
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
import 'car/car.dart';
|
import 'car/car.dart';
|
||||||
|
import 'heroes/hero.dart';
|
||||||
import 'heroes/hero_service.dart';
|
import 'heroes/hero_service.dart';
|
||||||
import 'heroes/hero_service_provider.dart';
|
import 'heroes/hero_service_provider.dart';
|
||||||
import 'logger_service.dart';
|
import 'logger_service.dart';
|
||||||
import 'package:dependency_injection/heroes/hero.dart';
|
|
||||||
|
|
||||||
//#docregion injector
|
//#docregion injector
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'my-injectors',
|
selector: 'my-injectors',
|
||||||
template: '''
|
template: '''
|
||||||
<h2>Other Injections</h2>
|
<h2>Other Injections</h2>
|
||||||
<div id="car"> {{car.drive()}}</div>
|
<div id="car">{{car.drive()}}</div>
|
||||||
<div id="hero">{{hero.name}}</div>
|
<div id="hero">{{hero.name}}</div>
|
||||||
<div id="rodent">{{rodent}}</div>
|
<div id="rodent">{{rodent}}</div>''',
|
||||||
''',
|
|
||||||
providers: const [
|
providers: const [
|
||||||
Car,
|
Car,
|
||||||
Engine,
|
Engine,
|
||||||
|
@ -25,20 +25,9 @@ import 'package:dependency_injection/heroes/hero.dart';
|
||||||
Logger
|
Logger
|
||||||
])
|
])
|
||||||
class InjectorComponent {
|
class InjectorComponent {
|
||||||
Injector _injector;
|
final Injector _injector;
|
||||||
|
|
||||||
InjectorComponent(this._injector) {
|
|
||||||
car = _injector.get(Car);
|
|
||||||
heroService = _injector.get(HeroService);
|
|
||||||
hero = heroService.getHeroes()[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
Car car;
|
Car car;
|
||||||
|
|
||||||
//#docregion get-hero-service
|
|
||||||
HeroService heroService;
|
HeroService heroService;
|
||||||
|
|
||||||
//#enddocregion get-hero-service
|
|
||||||
Hero hero;
|
Hero hero;
|
||||||
|
|
||||||
String get rodent {
|
String get rodent {
|
||||||
|
@ -48,6 +37,14 @@ class InjectorComponent {
|
||||||
}
|
}
|
||||||
return "R.O.U.S.'s? I don't think they exist!";
|
return "R.O.U.S.'s? I don't think they exist!";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InjectorComponent(this._injector) {
|
||||||
|
car = _injector.get(Car);
|
||||||
|
//#docregion get-hero-service
|
||||||
|
heroService = _injector.get(HeroService);
|
||||||
|
//#enddocregion get-hero-service
|
||||||
|
hero = heroService.getHeroes()[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//#enddocregion injector
|
//#enddocregion injector
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
|
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
|
@ -2,22 +2,19 @@
|
||||||
|
|
||||||
//#docplaster
|
//#docplaster
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
import 'app_config.dart';
|
import 'app_config.dart';
|
||||||
import 'heroes/hero_service_provider.dart';
|
import 'heroes/hero_service_provider.dart';
|
||||||
import 'heroes/hero_service.dart';
|
import 'heroes/hero_service.dart';
|
||||||
import 'logger_service.dart';
|
import 'logger_service.dart';
|
||||||
import 'user_service.dart';
|
import 'user_service.dart';
|
||||||
|
|
||||||
// #docregion import-optional
|
@Component(
|
||||||
import 'package:angular2/core.dart' show Optional;
|
selector: 'provider-1',
|
||||||
// #enddocregion import-optional
|
template: '{{log}}',
|
||||||
|
providers:
|
||||||
const template = '{{log}}';
|
|
||||||
|
|
||||||
//////////////////////////////////////////
|
|
||||||
@Component(selector: 'provider-1', template: '{{log}}', providers:
|
|
||||||
//#docregion providers-1
|
//#docregion providers-1
|
||||||
const [Logger]
|
const [Logger]
|
||||||
//#enddocregion providers-1
|
//#enddocregion providers-1
|
||||||
)
|
)
|
||||||
class ProviderComponent1 {
|
class ProviderComponent1 {
|
||||||
|
@ -25,14 +22,16 @@ class ProviderComponent1 {
|
||||||
|
|
||||||
ProviderComponent1(Logger logger) {
|
ProviderComponent1(Logger logger) {
|
||||||
logger.log('Hello from logger provided with Logger class');
|
logger.log('Hello from logger provided with Logger class');
|
||||||
log = logger.logs[0];
|
log = logger.logs.last;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////
|
@Component(
|
||||||
@Component(selector: 'provider-2', template: '{{log}}', providers:
|
selector: 'provider-2',
|
||||||
|
template: '{{log}}',
|
||||||
|
providers:
|
||||||
//#docregion providers-2
|
//#docregion providers-2
|
||||||
const [const Provider(Logger, useClass: Logger)]
|
const [const Provider(Logger, useClass: Logger)]
|
||||||
//#enddocregion providers-2
|
//#enddocregion providers-2
|
||||||
)
|
)
|
||||||
class ProviderComponent2 {
|
class ProviderComponent2 {
|
||||||
|
@ -40,36 +39,32 @@ class ProviderComponent2 {
|
||||||
|
|
||||||
ProviderComponent2(Logger logger) {
|
ProviderComponent2(Logger logger) {
|
||||||
logger.log('Hello from logger provided with Provider class and useClass');
|
logger.log('Hello from logger provided with Provider class and useClass');
|
||||||
log = logger.logs[0];
|
log = logger.logs.last;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////
|
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'provider-3',
|
selector: 'provider-3',
|
||||||
template: '{{log}}',
|
template: '{{log}}',
|
||||||
providers: const [const Provider(Logger, useClass: Logger)]
|
providers: const [const Provider(Logger, useClass: Logger)])
|
||||||
/*
|
|
||||||
//#docregion providers-3
|
|
||||||
const [provide(Logger, useClass: Logger)]
|
|
||||||
//#enddocregion providers-3
|
|
||||||
*/
|
|
||||||
)
|
|
||||||
class ProviderComponent3 {
|
class ProviderComponent3 {
|
||||||
String log;
|
String log;
|
||||||
|
|
||||||
ProviderComponent3(Logger logger) {
|
ProviderComponent3(Logger logger) {
|
||||||
logger.log('Hello from logger provided with useClass');
|
logger.log('Hello from logger provided with useClass');
|
||||||
log = logger.logs[0];
|
log = logger.logs.last;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////
|
@Injectable()
|
||||||
class BetterLogger extends Logger {}
|
class BetterLogger extends Logger {}
|
||||||
|
|
||||||
@Component(selector: 'provider-4', template: '{{log}}', providers:
|
@Component(
|
||||||
|
selector: 'provider-4',
|
||||||
|
template: '{{log}}',
|
||||||
|
providers:
|
||||||
//#docregion providers-4
|
//#docregion providers-4
|
||||||
const [const Provider(Logger, useClass: BetterLogger)]
|
const [const Provider(Logger, useClass: BetterLogger)]
|
||||||
//#enddocregion providers-4
|
//#enddocregion providers-4
|
||||||
)
|
)
|
||||||
class ProviderComponent4 {
|
class ProviderComponent4 {
|
||||||
|
@ -77,31 +72,32 @@ class ProviderComponent4 {
|
||||||
|
|
||||||
ProviderComponent4(Logger logger) {
|
ProviderComponent4(Logger logger) {
|
||||||
logger.log('Hello from logger provided with useClass:BetterLogger');
|
logger.log('Hello from logger provided with useClass:BetterLogger');
|
||||||
log = logger.logs[0];
|
log = logger.logs.last;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//////////////////////////////////////////
|
|
||||||
|
|
||||||
// #docregion EvenBetterLogger
|
// #docregion EvenBetterLogger
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class EvenBetterLogger implements Logger {
|
class EvenBetterLogger implements Logger {
|
||||||
UserService _userService;
|
final UserService _userService;
|
||||||
|
@override List<String> logs = [];
|
||||||
List<String> logs = [];
|
|
||||||
|
|
||||||
EvenBetterLogger(this._userService);
|
EvenBetterLogger(this._userService);
|
||||||
|
|
||||||
log(String message) {
|
@override void log(String message) {
|
||||||
message = 'Message to ${ _userService.user.name}: ${ message}.';
|
var msg = 'Message to ${_userService.user.name}: $message.';
|
||||||
print(message);
|
print(msg);
|
||||||
logs.add(message);
|
logs.add(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #enddocregion EvenBetterLogger
|
// #enddocregion EvenBetterLogger
|
||||||
@Component(selector: 'provider-5', template: '{{log}}', providers:
|
@Component(
|
||||||
|
selector: 'provider-5',
|
||||||
|
template: '{{log}}',
|
||||||
|
providers:
|
||||||
//#docregion providers-5
|
//#docregion providers-5
|
||||||
const [UserService, const Provider(Logger, useClass: EvenBetterLogger)]
|
const [UserService, const Provider(Logger, useClass: EvenBetterLogger)]
|
||||||
//#enddocregion providers-5
|
//#enddocregion providers-5
|
||||||
)
|
)
|
||||||
class ProviderComponent5 {
|
class ProviderComponent5 {
|
||||||
|
@ -109,29 +105,30 @@ class ProviderComponent5 {
|
||||||
|
|
||||||
ProviderComponent5(Logger logger) {
|
ProviderComponent5(Logger logger) {
|
||||||
logger.log('Hello from EvenBetterlogger');
|
logger.log('Hello from EvenBetterlogger');
|
||||||
log = logger.logs[0];
|
log = logger.logs.last;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////
|
@Injectable()
|
||||||
class NewLogger extends Logger implements OldLogger {}
|
class NewLogger extends Logger implements OldLogger {}
|
||||||
|
|
||||||
class OldLogger {
|
class OldLogger {
|
||||||
List<String> logs = [];
|
List<String> logs = [];
|
||||||
|
|
||||||
log(String message) {
|
void log(String message) {
|
||||||
throw new Exception('Should not call the old logger!');
|
throw new Exception('Should not call the old logger!');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component(selector: 'provider-6a', template: '{{log}}', providers:
|
@Component(
|
||||||
//#docregion providers-6a
|
selector: 'provider-6a',
|
||||||
const [
|
template: '{{log}}',
|
||||||
NewLogger,
|
providers:
|
||||||
// Not aliased! Creates two instances of `NewLogger`
|
//#docregion providers-6a
|
||||||
const Provider(OldLogger, useClass: NewLogger)
|
const [NewLogger,
|
||||||
]
|
// Not aliased! Creates two instances of `NewLogger`
|
||||||
//#enddocregion providers-6a
|
const Provider(OldLogger, useClass: NewLogger)]
|
||||||
|
//#enddocregion providers-6a
|
||||||
)
|
)
|
||||||
class ProviderComponent6a {
|
class ProviderComponent6a {
|
||||||
String log;
|
String log;
|
||||||
|
@ -144,18 +141,22 @@ class ProviderComponent6a {
|
||||||
// The newLogger wasn't called so no logs[]
|
// The newLogger wasn't called so no logs[]
|
||||||
|
|
||||||
// display the logs of the oldLogger.
|
// display the logs of the oldLogger.
|
||||||
log = newLogger.logs == null || newLogger.logs.isEmpty ? oldLogger.logs[0] : newLogger.logs[0];
|
log = newLogger.logs == null || newLogger.logs.isEmpty
|
||||||
|
? oldLogger.logs[0]
|
||||||
|
: newLogger.logs[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component(selector: 'provider-6b', template: '{{log}}', providers:
|
@Component(
|
||||||
//#docregion providers-6b
|
selector: 'provider-6b',
|
||||||
const [
|
template: '{{log}}',
|
||||||
NewLogger,
|
providers:
|
||||||
// Alias OldLogger w/ reference to NewLogger
|
//#docregion providers-6b
|
||||||
const Provider(OldLogger, useExisting: NewLogger)
|
const [NewLogger,
|
||||||
//#enddocregion providers-6b
|
// Alias OldLogger with reference to NewLogger
|
||||||
])
|
const Provider(OldLogger, useExisting: NewLogger)]
|
||||||
|
//#enddocregion providers-6b
|
||||||
|
)
|
||||||
class ProviderComponent6b {
|
class ProviderComponent6b {
|
||||||
String log;
|
String log;
|
||||||
|
|
||||||
|
@ -167,43 +168,46 @@ class ProviderComponent6b {
|
||||||
log = newLogger.logs[0];
|
log = newLogger.logs[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//////////////////////////////////////////
|
|
||||||
|
|
||||||
// #docregion silent-logger
|
// #docregion opaque-token
|
||||||
|
const loggerPrefix = const OpaqueToken('Logger prefix');
|
||||||
|
// #enddocregion opaque-token
|
||||||
|
|
||||||
// An object in the shape of the logger service
|
// #docregion configurable-logger
|
||||||
class SilentLogger /*implements Logger*/ {
|
@Injectable()
|
||||||
const SilentLogger({this.logs});
|
class ConfigurableLogger extends Logger {
|
||||||
|
final String _prefix;
|
||||||
|
|
||||||
final List<String> logs;
|
// #docregion use-opaque-token
|
||||||
|
ConfigurableLogger(@Inject(loggerPrefix) this._prefix);
|
||||||
|
// #enddocregion use-opaque-token
|
||||||
|
|
||||||
log(String message) {}
|
@override
|
||||||
|
void log(String msg) {
|
||||||
|
super.log('$_prefix: $msg');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// #enddocregion configurable-logger
|
||||||
|
|
||||||
const silentLogger = const SilentLogger(
|
@Component(selector: 'provider-7', template: '{{message}}',
|
||||||
logs: const ['Silent logger says "Shhhhh!". Provided via "useValue"']);
|
|
||||||
// #enddocregion silent-logger
|
|
||||||
|
|
||||||
@Component(selector: 'provider-7', template: '{{log}}', providers:
|
|
||||||
//#docregion providers-7
|
//#docregion providers-7
|
||||||
const [const Provider(SilentLogger, useValue: silentLogger)]
|
providers: const [
|
||||||
|
const Provider(Logger, useClass: ConfigurableLogger),
|
||||||
|
//#docregion providers-usevalue
|
||||||
|
const Provider(loggerPrefix, useValue: 'Testing')
|
||||||
|
//#enddocregion providers-usevalue
|
||||||
|
]
|
||||||
//#enddocregion providers-7
|
//#enddocregion providers-7
|
||||||
/*
|
|
||||||
//#docregion providers-7-unchecked
|
|
||||||
const [const Provider(Logger, useValue: silentLogger)]
|
|
||||||
//#enddocregion providers-7-unchecked
|
|
||||||
*/
|
|
||||||
)
|
)
|
||||||
class ProviderComponent7 {
|
class ProviderComponent7 {
|
||||||
String log;
|
String message;
|
||||||
|
|
||||||
ProviderComponent7(SilentLogger logger) {
|
ProviderComponent7(Logger logger) {
|
||||||
logger.log('Hello from logger provided with useValue');
|
logger.log('Hello from configurable logger.');
|
||||||
log = logger.logs[0];
|
message = logger.logs.last;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////
|
|
||||||
@Component(selector: 'provider-8', template: '{{log}}', providers: const [
|
@Component(selector: 'provider-8', template: '{{log}}', providers: const [
|
||||||
const Provider(HeroService, useFactory: heroServiceFactory),
|
const Provider(HeroService, useFactory: heroServiceFactory),
|
||||||
Logger,
|
Logger,
|
||||||
|
@ -219,129 +223,103 @@ class ProviderComponent8 {
|
||||||
var log = 'Hero service injected successfully';
|
var log = 'Hero service injected successfully';
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////
|
@Component(
|
||||||
@Component(selector: 'provider-9a', template: '{{log}}', providers:
|
selector: 'provider-9',
|
||||||
/*
|
template: '{{log}}',
|
||||||
// #docregion providers-9a-interface
|
providers:
|
||||||
// WOKRKS! Can use abstract class as provider token
|
// #docregion providers-9
|
||||||
[provide(Config, {useValue: CONFIG})]
|
const [const Provider(AppConfig, useValue: config1)]
|
||||||
// #enddocregion providers-9a-interface
|
// #enddocregion providers-9
|
||||||
*/
|
|
||||||
|
|
||||||
// #docregion providers-9a
|
|
||||||
// Use string as provider token
|
|
||||||
const [const Provider('app.config', useValue: CONFIG_HASH)]
|
|
||||||
//#enddocregion providers-9a
|
|
||||||
)
|
)
|
||||||
class ProviderComponent9a implements OnInit {
|
class ProviderComponent9 implements OnInit {
|
||||||
Config _config;
|
AppConfig _config;
|
||||||
|
|
||||||
String log;
|
String log;
|
||||||
|
|
||||||
/*
|
ProviderComponent9(AppConfig this._config);
|
||||||
// #docregion provider-9a-ctor-interface
|
|
||||||
// WORKS! Can inject using the abstract class as the parameter type
|
|
||||||
Config _config;
|
|
||||||
|
|
||||||
ProviderComponent9a(this._config);
|
@override
|
||||||
// #enddocregion provider-9a-ctor-interface
|
void ngOnInit() {
|
||||||
*/
|
log = 'appConfigToken Application title is ${_config.title}';
|
||||||
|
|
||||||
// #docregion provider-9a-ctor
|
|
||||||
|
|
||||||
// @Inject(token) to inject the dependency
|
|
||||||
ProviderComponent9a(@Inject('app.config') Map config) {
|
|
||||||
_config = new ConfigImpl(apiEndpoint: config['apiEndpoint'], title: config['title']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// #enddocregion provider-9a-ctor
|
|
||||||
ngOnInit() {
|
|
||||||
log = '\'app.config\' Application title is ' + _config.title;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component(selector: 'provider-9b', template: '{{log}}', providers:
|
|
||||||
// #docregion providers-9b
|
|
||||||
const [const Provider(APP_CONFIG, useValue: CONFIG_HASH)]) // #enddocregion providers-9b
|
|
||||||
class ProviderComponent9b
|
|
||||||
implements OnInit {
|
|
||||||
Config _config;
|
|
||||||
|
|
||||||
String log;
|
|
||||||
|
|
||||||
// #docregion provider-9b-ctor
|
|
||||||
ProviderComponent9b(@Inject(APP_CONFIG) Map config) {
|
|
||||||
_config = new ConfigImpl(apiEndpoint: config['apiEndpoint'], title: config['title']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// #enddocregion provider-9b-ctor
|
|
||||||
ngOnInit() {
|
|
||||||
log = 'APP_CONFIG Application title is ' + _config.title;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//////////////////////////////////////////
|
|
||||||
|
|
||||||
// Normal required logger
|
// Normal required logger
|
||||||
@Component(selector: 'provider-10a', template: '{{log}}',
|
@Component(selector: 'provider-10a', template: '{{log}}',
|
||||||
//#docregion providers-logger
|
//#docregion providers-logger
|
||||||
providers: const [Logger]
|
providers: const [Logger]
|
||||||
//#enddocregion providers-logger
|
//#enddocregion providers-logger
|
||||||
)
|
)
|
||||||
class ProviderComponent10a {
|
class ProviderComponent10a {
|
||||||
String log;
|
String log;
|
||||||
|
|
||||||
ProviderComponent10a(Logger logger) {
|
ProviderComponent10a(Logger logger) {
|
||||||
logger.log('Hello from the required logger.');
|
logger.log('Hello from the required logger.');
|
||||||
log = logger.logs[0];
|
log = logger.logs.last;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optional logger
|
// Optional logger, can be null
|
||||||
@Component(selector: 'provider-10b', template: '{{log}}')
|
@Component(selector: 'provider-10b', template: '{{log}}')
|
||||||
class ProviderComponent10b implements OnInit {
|
class ProviderComponent10b {
|
||||||
Logger _logger;
|
|
||||||
|
|
||||||
// #docregion provider-10-ctor
|
// #docregion provider-10-ctor
|
||||||
|
final Logger _logger;
|
||||||
String log;
|
String log;
|
||||||
|
|
||||||
ProviderComponent10b(@Optional() this._logger);
|
ProviderComponent10b(@Optional() Logger this._logger) {
|
||||||
|
// . . .
|
||||||
// #enddocregion provider-10-ctor
|
// #enddocregion provider-10-ctor
|
||||||
ngOnInit() {
|
_logger == null ? log = 'No logger exists' : log = _logger.logs.last;
|
||||||
// #docregion provider-10-logger
|
// #docregion provider-10-ctor
|
||||||
|
|
||||||
// No logger? Make one!
|
|
||||||
if (_logger == null) {
|
|
||||||
_logger = new Logger();
|
|
||||||
// #enddocregion provider-10-logger
|
|
||||||
_logger.log('Optional logger was not available.');
|
|
||||||
} else {
|
|
||||||
_logger.log('Hello from the injected logger.');
|
|
||||||
log = _logger.logs[0];
|
|
||||||
}
|
|
||||||
log = _logger.logs[0];
|
|
||||||
}
|
}
|
||||||
|
// #enddocregion provider-10-ctor
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////
|
// Optional logger, non null
|
||||||
|
@Component(selector: 'provider-10c', template: '{{log}}')
|
||||||
|
class ProviderComponent10c {
|
||||||
|
// #docregion provider-10-logger
|
||||||
|
final Logger _logger;
|
||||||
|
String log;
|
||||||
|
|
||||||
|
ProviderComponent10c(@Optional() Logger logger) :
|
||||||
|
_logger = logger ?? new DoNothingLogger() {
|
||||||
|
// . . .
|
||||||
|
// #enddocregion provider-10-logger
|
||||||
|
logger == null
|
||||||
|
? _logger.log('Optional logger was not available.')
|
||||||
|
: _logger.log('Hello from the injected logger.');
|
||||||
|
log = _logger.logs.last;
|
||||||
|
// #docregion provider-10-logger
|
||||||
|
}
|
||||||
|
// #enddocregion provider-10-logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// #docregion provider-10-logger
|
||||||
|
// . . .
|
||||||
|
@Injectable()
|
||||||
|
class DoNothingLogger extends Logger {
|
||||||
|
@override List<String> logs = [];
|
||||||
|
@override void log(String msg) => logs.add(msg);
|
||||||
|
}
|
||||||
|
// #enddocregion provider-10-logger
|
||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'my-providers',
|
selector: 'my-providers',
|
||||||
template: '''
|
template: '''
|
||||||
<h2>Provider variations</h2>
|
<h2>Provider variations</h2>
|
||||||
<div id="p1"><provider-1></provider-1></div>
|
<div id="p1"><provider-1></provider-1></div>
|
||||||
<div id="p2"><provider-2></provider-2></div>
|
<div id="p2"><provider-2></provider-2></div>
|
||||||
<div id="p3"><provider-3></provider-3></div>
|
<div id="p3"><provider-3></provider-3></div>
|
||||||
<div id="p4"><provider-4></provider-4></div>
|
<div id="p4"><provider-4></provider-4></div>
|
||||||
<div id="p5"><provider-5></provider-5></div>
|
<div id="p5"><provider-5></provider-5></div>
|
||||||
<div id="p6a"><provider-6a></provider-6a></div>
|
<div id="p6a"><provider-6a></provider-6a></div>
|
||||||
<div id="p6b"><provider-6b></provider-6b></div>
|
<div id="p6b"><provider-6b></provider-6b></div>
|
||||||
<div id="p7"><provider-7></provider-7></div>
|
<div id="p7"><provider-7></provider-7></div>
|
||||||
<div id="p8"><provider-8></provider-8></div>
|
<div id="p8"><provider-8></provider-8></div>
|
||||||
<div id="p9a"><provider-9a></provider-9a></div>
|
<div id="p8"><provider-9></provider-9></div>
|
||||||
<div id="p9b"><provider-9b></provider-9b></div>
|
<div id="p10a"><provider-10a></provider-10a></div>
|
||||||
<div id="p10a"><provider-10a></provider-10a></div>
|
<div id="p10b"><provider-10b></provider-10b></div>
|
||||||
<div id="p10b"><provider-10b></provider-10b></div>
|
<div id="p10c"><provider-10c></provider-10c></div>''',
|
||||||
''',
|
|
||||||
directives: const [
|
directives: const [
|
||||||
ProviderComponent1,
|
ProviderComponent1,
|
||||||
ProviderComponent2,
|
ProviderComponent2,
|
||||||
|
@ -352,9 +330,9 @@ class ProviderComponent10b implements OnInit {
|
||||||
ProviderComponent6b,
|
ProviderComponent6b,
|
||||||
ProviderComponent7,
|
ProviderComponent7,
|
||||||
ProviderComponent8,
|
ProviderComponent8,
|
||||||
ProviderComponent9a,
|
ProviderComponent9,
|
||||||
ProviderComponent9b,
|
|
||||||
ProviderComponent10a,
|
ProviderComponent10a,
|
||||||
ProviderComponent10b
|
ProviderComponent10b,
|
||||||
|
ProviderComponent10c
|
||||||
])
|
])
|
||||||
class ProvidersComponent {}
|
class ProvidersComponent {}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// #docregion
|
// #docregion
|
||||||
|
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
|
@ -5,7 +5,7 @@ version: 0.0.1
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=1.13.0 <2.0.0'
|
sdk: '>=1.13.0 <2.0.0'
|
||||||
dependencies:
|
dependencies:
|
||||||
angular2: 2.0.0-beta.11
|
angular2: 2.0.0-beta.12
|
||||||
browser: ^0.10.0
|
browser: ^0.10.0
|
||||||
dart_to_js_script_rewriter: ^0.1.0
|
dart_to_js_script_rewriter: ^0.1.0
|
||||||
transformers:
|
transformers:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// A simple test
|
// A simple test
|
||||||
// More details will be in the testing chapter.
|
// More details will be in the testing chapter.
|
||||||
import 'package:angular2/angular2.dart';
|
import 'package:angular2/core.dart';
|
||||||
import 'package:dependency_injection/heroes/hero.dart';
|
import 'package:dependency_injection/heroes/hero.dart';
|
||||||
import 'package:dependency_injection/heroes/hero_list_component.dart';
|
import 'package:dependency_injection/heroes/hero_list_component.dart';
|
||||||
import 'package:dependency_injection/heroes/hero_service.dart';
|
import 'package:dependency_injection/heroes/hero_service.dart';
|
||||||
|
@ -31,4 +31,4 @@ void main() {
|
||||||
expect(hlc.heroes.length, expectedHeroes.length);
|
expect(hlc.heroes.length, expectedHeroes.length);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
//#enddocregion spec
|
//#enddocregion spec
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
<!-- #docregion -->
|
<!--#docregion-->
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Dependency Injection</title>
|
<title>Dependency Injection</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<link rel="stylesheet" href="styles.css">
|
||||||
<link rel="stylesheet" href="styles.css">
|
|
||||||
<script type="application/dart" src="main.dart"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<my-app>Loading my-app ...</my-app>
|
|
||||||
<my-providers>Loading my-providers ...</my-providers>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
|
<script defer src="main.dart" type="application/dart"></script>
|
||||||
|
<script defer src="packages/browser/dart.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<my-app>Loading...</my-app>
|
||||||
|
<my-providers>Loading my-providers ...</my-providers>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import 'package:angular2/platform/browser.dart';
|
import 'package:angular2/platform/browser.dart';
|
||||||
import 'package:dependency_injection/app_component.dart';
|
import 'package:dependency_injection/app_component.dart';
|
||||||
import 'package:dependency_injection/heroes/hero_service.dart';
|
import 'package:dependency_injection/heroes/hero_service.dart';
|
||||||
//#docregion bootstrap
|
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
// Injecting services in bootstrap works but is discouraged
|
//#docregion bootstrap
|
||||||
bootstrap(AppComponent, [HeroService]);
|
bootstrap(AppComponent,
|
||||||
//#enddocregion bootstrap
|
[HeroService]); // DISCOURAGED (but works)
|
||||||
}
|
//#enddocregion bootstrap
|
||||||
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {Logger} from './logger.service';
|
||||||
selector: 'my-injectors',
|
selector: 'my-injectors',
|
||||||
template: `
|
template: `
|
||||||
<h2>Other Injections</h2>
|
<h2>Other Injections</h2>
|
||||||
<div id="car"> {{car.drive()}}</div>
|
<div id="car">{{car.drive()}}</div>
|
||||||
<div id="hero">{{hero.name}}</div>
|
<div id="hero">{{hero.name}}</div>
|
||||||
<div id="rodent">{{rodent}}</div>
|
<div id="rodent">{{rodent}}</div>
|
||||||
`,
|
`,
|
||||||
|
|
|
@ -1,11 +1,279 @@
|
||||||
include ../_util-fns
|
include ../_util-fns
|
||||||
|
|
||||||
|
+includeShared('{ts}', 'intro')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
We're working on the Dart version of this chapter.
|
The complete source code for the example app in this chapter is
|
||||||
In the meantime, please see these resources:
|
[in GitHub](https://github.com/angular/angular.io/tree/master/public/docs/_examples/dependency-injection/dart).
|
||||||
|
|
||||||
* [Dependency Injection](/docs/ts/latest/guide/dependency-injection.html):
|
+includeShared('{ts}', 'why-1')
|
||||||
The TypeScript version of this chapter
|
+makeExample('dependency-injection/dart/lib/car/car_no_di.dart', 'car', 'lib/car/car.dart (without DI)')
|
||||||
|
+includeShared('{ts}', 'why-2')
|
||||||
|
+makeTabs(
|
||||||
|
'dependency-injection/dart/lib/car/car.dart, dependency-injection/dart/lib/car/car_no_di.dart',
|
||||||
|
'car-ctor, car-ctor',
|
||||||
|
'lib/car/car.dart (excerpt with DI), lib/car/car.dart (excerpt without DI)')(format=".")
|
||||||
|
+includeShared('{ts}', 'why-3-1')
|
||||||
|
+includeShared('{ts}', 'why-3-2')
|
||||||
|
- var stylePattern = { otl: /(new Car.*$)/gm };
|
||||||
|
+makeExample('dependency-injection/dart/lib/car/car_creations.dart', 'car-ctor-instantiation', '', stylePattern)(format=".")
|
||||||
|
+includeShared('{ts}', 'why-4')
|
||||||
|
.l-sub-section
|
||||||
|
:marked
|
||||||
|
The _consumer_ of `Car` has the problem. The consumer must update the car creation code to
|
||||||
|
something like this:
|
||||||
|
- var stylePattern = { otl: /(new Car.*$)/gm };
|
||||||
|
+makeExample('dependency-injection/dart/lib/car/car_creations.dart', 'car-ctor-instantiation-with-param', '', stylePattern)(format=".")
|
||||||
|
:marked
|
||||||
|
The critical point is this: `Car` itself did not have to change.
|
||||||
|
We'll take care of the consumer's problem soon enough.
|
||||||
|
+includeShared('{ts}', 'why-6')
|
||||||
|
- var stylePattern = { otl: /(new Car.*$)/gm };
|
||||||
|
+makeExample('dependency-injection/dart/lib/car/car_creations.dart', 'car-ctor-instantiation-with-mocks', '', stylePattern)(format=".")
|
||||||
|
+includeShared('{ts}', 'why-7')
|
||||||
|
+makeExample('dependency-injection/dart/lib/car/car_factory.dart', null, 'lib/car/car_factory.dart')
|
||||||
|
+includeShared('{ts}', 'why-8')
|
||||||
|
+makeExample('dependency-injection/dart/lib/car/car_injector.dart','injector-call')(format=".")
|
||||||
|
+includeShared('{ts}', 'why-9')
|
||||||
|
|
||||||
* [Dart source code](https://github.com/angular/angular.io/tree/master/public/docs/_examples/dependency-injection/dart):
|
+includeShared('{ts}', 'di-1')
|
||||||
A preliminary version of the example code that will appear in this chapter
|
+makeTabs(
|
||||||
|
`dependency-injection/dart/lib/heroes/heroes_component_1.dart,
|
||||||
|
dependency-injection/dart/lib/heroes/hero_list_component_1.dart,
|
||||||
|
dependency-injection/dart/lib/heroes/hero.dart,
|
||||||
|
dependency-injection/dart/lib/heroes/mock_heroes.dart`,
|
||||||
|
'v1,,,',
|
||||||
|
`lib/heroes/heroes_component.dart,
|
||||||
|
lib/heroes/hero_list_component.dart,
|
||||||
|
lib/heroes/hero.dart,
|
||||||
|
lib/heroes/mock_heroes.dart`)
|
||||||
|
+includeShared('{ts}', 'di-2')
|
||||||
|
+includeShared('{ts}', 'di-3')
|
||||||
|
+makeExample('dependency-injection/dart/lib/heroes/hero_service_1.dart',null, 'lib/heroes/hero_service.dart' )
|
||||||
|
+includeShared('{ts}', 'di-4')
|
||||||
|
.l-sub-section
|
||||||
|
:marked
|
||||||
|
We aren't even pretending this is a real service.
|
||||||
|
If we were actually getting data from a remote server, the API would have to be asynchronous,
|
||||||
|
returning a `Future`.
|
||||||
|
We'd also have to rewrite the way components consume our service.
|
||||||
|
This is important in general, but not to our current story.
|
||||||
|
+includeShared('{ts}', 'di-6')
|
||||||
|
+includeShared('{ts}', 'di-configure-injector-1')
|
||||||
|
+makeExample('dependency-injection/dart/web/main.dart', 'bootstrap', 'web/main.dart (excerpt)')(format='.')
|
||||||
|
+includeShared('{ts}', 'di-configure-injector-2')
|
||||||
|
+makeExample('dependency-injection/dart/web/main_1.dart', 'bootstrap')(format='.')
|
||||||
|
+includeShared('{ts}', 'di-configure-injector-3')
|
||||||
|
+includeShared('{ts}', 'di-register-providers-1')
|
||||||
|
+makeExample('dependency-injection/dart/lib/heroes/heroes_component_1.dart',null,'lib/heroes/heroes_component.dart')
|
||||||
|
+includeShared('{ts}', 'di-register-providers-2')
|
||||||
|
+makeExample('dependency-injection/dart/lib/heroes/heroes_component_1.dart','providers')(format='.')
|
||||||
|
+includeShared('{ts}', 'di-register-providers-3')
|
||||||
|
+includeShared('{ts}', 'di-prepare-for-injection-1')
|
||||||
|
+makeTabs(
|
||||||
|
`dependency-injection/dart/lib/heroes/hero_list_component_2.dart,
|
||||||
|
dependency-injection/dart/lib/heroes/hero_list_component_1.dart`,
|
||||||
|
null,
|
||||||
|
`lib/heroes/hero_list_component (with DI),
|
||||||
|
lib/heroes/hero_list_component (without DI)`)
|
||||||
|
.l-sub-section
|
||||||
|
:marked
|
||||||
|
### Focus on the constructor
|
||||||
|
|
||||||
|
Adding a parameter to the constructor isn't all that's happening here.
|
||||||
|
+makeExample('dependency-injection/dart/lib/heroes/hero_list_component_2.dart', 'ctor')(format=".")
|
||||||
|
:marked
|
||||||
|
The constructor parameter has a type: `HeroService`.
|
||||||
|
The `HeroListComponent` class is also annotated with `@Component`
|
||||||
|
(scroll up to confirm that fact).
|
||||||
|
Also recall that the parent component (`HeroesComponent`)
|
||||||
|
has `providers` information for `HeroService`.
|
||||||
|
|
||||||
|
The constructor parameter type, the `@Component` annotation,
|
||||||
|
and the parent's `providers` information combine to tell the
|
||||||
|
Angular injector to inject an instance of
|
||||||
|
`HeroService` whenever it creates a new `HeroListComponent`.
|
||||||
|
+includeShared('{ts}', 'di-create-injector-implicitly-1')
|
||||||
|
+makeExample('dependency-injection/dart/lib/car/car_injector.dart','injector-create-and-call')(format=".")
|
||||||
|
+includeShared('{ts}', 'di-create-injector-implicitly-2')
|
||||||
|
+includeShared('{ts}', 'di-singleton-services')
|
||||||
|
|
||||||
|
// Skip the testing section, for now.
|
||||||
|
// includeShared('{ts}', 'di-testing-component-1')
|
||||||
|
// includeShared('{ts}', 'di-testing-component-2')
|
||||||
|
|
||||||
|
+includeShared('{ts}', 'di-service-service-1')
|
||||||
|
+makeTabs(
|
||||||
|
`dependency-injection/dart/lib/heroes/hero_service_2.dart,
|
||||||
|
dependency-injection/dart/lib/heroes/hero_service_1.dart`,
|
||||||
|
null,
|
||||||
|
`lib/heroes/hero_service (v.2),
|
||||||
|
lib/heroes/hero_service (v.1)`)
|
||||||
|
+includeShared('{ts}', 'di-service-service-2')
|
||||||
|
+includeShared('{ts}', 'di-injectable-1')
|
||||||
|
:marked
|
||||||
|
Forgetting the `@Injectable()` can cause a runtime error:
|
||||||
|
code-example(format, language="html").
|
||||||
|
Cannot find reflection information on <Type>
|
||||||
|
+includeShared('{ts}', 'di-injectable-2')
|
||||||
|
.callout.is-critical
|
||||||
|
header Always include the parentheses
|
||||||
|
:marked
|
||||||
|
Always use `@Injectable()`, not just `@Injectable`.
|
||||||
|
A metadata annotation must be either a reference to a
|
||||||
|
compile-time constant variable or a call to a constant
|
||||||
|
constructor such as `Injectable()`.
|
||||||
|
If we forget the parentheses, the analyzer will complain:
|
||||||
|
"Annotation creation must have arguments". If we try to run the
|
||||||
|
app anyway, it won't work, and the console will say
|
||||||
|
"expression must be a compile-time constant".
|
||||||
|
+includeShared('{ts}', 'logger-service-1')
|
||||||
|
+makeExample(
|
||||||
|
'dependency-injection/dart/lib/logger_service.dart',null, 'lib/logger_service')
|
||||||
|
.l-sub-section
|
||||||
|
:marked
|
||||||
|
### Implementing a logger
|
||||||
|
|
||||||
|
Our examples use a simple logger.
|
||||||
|
A real implementation would probably use the
|
||||||
|
[logging package](https://pub.dartlang.org/packages/logging).
|
||||||
|
:marked
|
||||||
|
We're likely to need the same logger service everywhere in our application,
|
||||||
|
so we put it in the `lib/` folder, and
|
||||||
|
we register it in the `providers` array of the metadata for our application root component, `AppComponent`.
|
||||||
|
+makeExample('dependency-injection/dart/lib/providers_component.dart','providers-logger', 'lib/app_component.dart (excerpt)')
|
||||||
|
+includeShared('{ts}', 'logger-service-3')
|
||||||
|
+includeShared('{ts}', 'logger-service-4')
|
||||||
|
+includeShared('{ts}', 'logger-service-5')
|
||||||
|
+makeExample('dependency-injection/dart/lib/providers_component.dart','provider-10-ctor')(format='.')
|
||||||
|
+includeShared('{ts}', 'logger-service-6')
|
||||||
|
+makeExample('dependency-injection/dart/lib/providers_component.dart','provider-10-logger')(format='.')
|
||||||
|
+includeShared('{ts}', 'logger-service-7')
|
||||||
|
|
||||||
|
+includeShared('{ts}', 'providers-1')
|
||||||
|
+makeExample('dependency-injection/dart/lib/providers_component.dart','providers-logger')
|
||||||
|
+includeShared('{ts}', 'providers-2')
|
||||||
|
+includeShared('{ts}', 'providers-provide-1')
|
||||||
|
:marked
|
||||||
|
### The *Provider* class
|
||||||
|
+includeShared('{ts}', 'providers-provide-1-1')
|
||||||
|
+makeExample('dependency-injection/dart/lib/providers_component.dart','providers-1')
|
||||||
|
+includeShared('{ts}', 'providers-provide-2')
|
||||||
|
+makeExample('dependency-injection/dart/lib/providers_component.dart','providers-2')
|
||||||
|
// includeShared('{ts}', 'providers-provide-3')
|
||||||
|
// includeShared('{ts}', 'providers-provide-4-1')
|
||||||
|
// Don't discuss provide function.
|
||||||
|
:marked
|
||||||
|
We supply two arguments (or more) to the `Provider` constructor.
|
||||||
|
+includeShared('{ts}', 'providers-provide-4-2')
|
||||||
|
:marked
|
||||||
|
The second is a named parameter, such as `useClass`,
|
||||||
|
that we can think of as a *recipe* for creating the dependency value.
|
||||||
|
There are many ways to create dependency values... and many ways to write a recipe.
|
||||||
|
+includeShared('{ts}', 'providers-alternative-1')
|
||||||
|
+makeExample('dependency-injection/dart/lib/providers_component.dart','providers-4')
|
||||||
|
.callout.is-helpful
|
||||||
|
header Dart difference: Constants in metadata
|
||||||
|
:marked
|
||||||
|
In Dart, the value of a metadata annotation must be a compile-time constant.
|
||||||
|
For that reason, we can't call functions to get values
|
||||||
|
to use within an annotation.
|
||||||
|
Instead, we use constant literals or constant constructors.
|
||||||
|
For example, a TypeScript program might use the
|
||||||
|
function call `provide(Logger, {useClass: BetterLogger})`,
|
||||||
|
which is equivalent to the TypeScript code
|
||||||
|
`new Provider(Logger, {useClass: BetterLogger})`.
|
||||||
|
A Dart annotation would instead use the constant value `const Provider(Logger, useClass: BetterLogger)`.
|
||||||
|
+includeShared('{ts}', 'providers-alternative-2')
|
||||||
|
+makeExample('dependency-injection/dart/lib/providers_component.dart','EvenBetterLogger')
|
||||||
|
+includeShared('{ts}', 'providers-alternative-3')
|
||||||
|
+makeExample('dependency-injection/dart/lib/providers_component.dart','providers-5')(format=".")
|
||||||
|
+includeShared('{ts}', 'providers-aliased-1')
|
||||||
|
+makeExample('dependency-injection/dart/lib/providers_component.dart','providers-6a')(format=".")
|
||||||
|
+includeShared('{ts}', 'providers-aliased-2')
|
||||||
|
- var stylePattern = { otl: /(useExisting.*\))/gm };
|
||||||
|
+makeExample('dependency-injection/dart/lib/providers_component.dart','providers-6b','', stylePattern)(format=".")
|
||||||
|
|
||||||
|
+includeShared('{ts}', 'providers-value-1')
|
||||||
|
:marked
|
||||||
|
We can provide objects directly,
|
||||||
|
instead of asking the injector to create an instance of a class,
|
||||||
|
by specifying a `useValue` parameter to `Provider`.
|
||||||
|
|
||||||
|
Because Dart annotations must be compile-time constants,
|
||||||
|
`useValue` is often used with string or list literals.
|
||||||
|
However, `useValue` works with any constant object.
|
||||||
|
|
||||||
|
To create a class that can provide constant objects,
|
||||||
|
make sure all its instance variables are `final`,
|
||||||
|
and give it a `const` constructor:
|
||||||
|
|
||||||
|
+makeExample('dependency-injection/dart/lib/app_config.dart','const-class','lib/app_config.dart (excerpt)')(format='.')
|
||||||
|
|
||||||
|
:marked
|
||||||
|
Create a constant instance of the class by using `const` instead of `new`:
|
||||||
|
|
||||||
|
+makeExample('dependency-injection/dart/lib/app_config.dart','const-object','lib/app_config.dart (excerpt)')(format='.')
|
||||||
|
|
||||||
|
:marked
|
||||||
|
Then specify the object using the `useValue` argument to `Provider`:
|
||||||
|
|
||||||
|
- var stylePattern = { otl: /(useValue.*\))/gm };
|
||||||
|
+makeExample('dependency-injection/dart/lib/providers_component.dart','providers-9','', stylePattern)(format='.')
|
||||||
|
|
||||||
|
:marked
|
||||||
|
See more `useValue` examples in the
|
||||||
|
[Non-class dependencies](#non-class-dependencies) and
|
||||||
|
[OpaqueToken](#opaquetoken) sections.
|
||||||
|
|
||||||
|
+includeShared('{ts}', 'providers-factory-1')
|
||||||
|
+makeExample('dependency-injection/dart/lib/heroes/hero_service.dart','internals', 'lib/heroes/hero_service.dart (excerpt)')(format='.')
|
||||||
|
+includeShared('{ts}', 'providers-factory-2')
|
||||||
|
+makeExample('dependency-injection/dart/lib/heroes/hero_service_provider.dart','factory', 'lib/heroes/hero_service_provider.dart (excerpt)')(format='.')
|
||||||
|
+includeShared('{ts}', 'providers-factory-3')
|
||||||
|
+makeExample('dependency-injection/dart/lib/heroes/hero_service_provider.dart','provider', 'lib/heroes/hero_service_provider.dart (excerpt)')(format='.')
|
||||||
|
+includeShared('{ts}', 'providers-factory-4')
|
||||||
|
+includeShared('{ts}', 'providers-factory-5')
|
||||||
|
- var stylePattern = { otl: /(providers.*),$/gm };
|
||||||
|
+makeTabs(
|
||||||
|
`dependency-injection/dart/lib/heroes/heroes_component.dart,
|
||||||
|
dependency-injection/dart/lib/heroes/heroes_component_1.dart`,
|
||||||
|
null,
|
||||||
|
`lib/heroes/heroes_component (v.3),
|
||||||
|
lib/heroes/heroes_component (v.2)`,
|
||||||
|
stylePattern)
|
||||||
|
+includeShared('{ts}', 'tokens-1')
|
||||||
|
+makeExample('dependency-injection/dart/lib/injector_component.dart','get-hero-service')(format='.')
|
||||||
|
+includeShared('{ts}', 'tokens-2')
|
||||||
|
// [PENDING: How about a better name than ProviderComponent8?]
|
||||||
|
+makeExample('dependency-injection/dart/lib/providers_component.dart','provider-8-ctor')(format=".")
|
||||||
|
+includeShared('{ts}', 'tokens-3')
|
||||||
|
.callout.is-helpful
|
||||||
|
header Dart difference: Interfaces are valid tokens
|
||||||
|
:marked
|
||||||
|
In TypeScript, interfaces don't work as provider tokens.
|
||||||
|
Dart doesn't have this problem;
|
||||||
|
every class implicitly defines an interface,
|
||||||
|
so interface names are just class names.
|
||||||
|
+includeShared('{ts}', 'tokens-non-class-deps-1')
|
||||||
|
:marked
|
||||||
|
We know we can register an object with a [value provider](#value-provider).
|
||||||
|
But what do we use for the token?
|
||||||
|
+includeShared('{ts}', 'tokens-opaque-1')
|
||||||
|
+makeExample('dependency-injection/dart/lib/providers_component.dart','opaque-token')(format='.')
|
||||||
|
+includeShared('{ts}', 'tokens-opaque-2')
|
||||||
|
+makeExample('dependency-injection/dart/lib/providers_component.dart','use-opaque-token')(format=".")
|
||||||
|
:marked
|
||||||
|
Here's an example of providing configuration information
|
||||||
|
for an injected class. First define the class:
|
||||||
|
+makeExample('dependency-injection/dart/lib/providers_component.dart','configurable-logger')(format=".")
|
||||||
|
:marked
|
||||||
|
Then inject that class and its configuration information:
|
||||||
|
+makeExample('dependency-injection/dart/lib/providers_component.dart','providers-7')(format=".")
|
||||||
|
|
||||||
|
|
||||||
|
+includeShared('{ts}', 'summary')
|
||||||
|
|
||||||
|
+includeShared('{ts}', 'appendix-explicit-injector-1')
|
||||||
|
+makeExample('dependency-injection/dart/lib/injector_component.dart', 'injector', 'lib/injector_component.dart')
|
||||||
|
+includeShared('{ts}', 'appendix-explicit-injector-2')
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
include ../_util-fns
|
include ../_util-fns
|
||||||
|
|
||||||
|
// #docregion intro
|
||||||
:marked
|
:marked
|
||||||
**Dependency injection** is an important application design pattern.
|
**Dependency injection** is an important application design pattern.
|
||||||
Angular has its own dependency injection framework, and
|
Angular has its own dependency injection framework, and
|
||||||
|
@ -7,18 +9,21 @@ include ../_util-fns
|
||||||
|
|
||||||
In this chapter we'll learn what DI is and why we want it.
|
In this chapter we'll learn what DI is and why we want it.
|
||||||
Then we'll learn [how to use it](#angular-di) in an Angular app.
|
Then we'll learn [how to use it](#angular-di) in an Angular app.
|
||||||
|
// #enddocregion intro
|
||||||
|
:marked
|
||||||
[Run the live example](/resources/live-examples/dependency-injection/ts/plnkr.html)
|
[Run the live example](/resources/live-examples/dependency-injection/ts/plnkr.html)
|
||||||
|
// #docregion why-1
|
||||||
<a id="why-di"></a>
|
<a id="why-di"></a>
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Why dependency injection?
|
## Why dependency injection?
|
||||||
|
|
||||||
Let's start with the following code.
|
Let's start with the following code.
|
||||||
|
// #enddocregion why-1
|
||||||
+makeExample('dependency-injection/ts/app/car/car-no-di.ts', 'car', 'app/car/car.ts (without DI)')
|
+makeExample('dependency-injection/ts/app/car/car-no-di.ts', 'car', 'app/car/car.ts (without DI)')
|
||||||
|
// #docregion why-2
|
||||||
|
- var lang = current.path[1]
|
||||||
|
- var prefix = lang == 'dart' ? '' : 'this.'
|
||||||
:marked
|
:marked
|
||||||
Our `Car` creates everything it needs inside its constructor.
|
Our `Car` creates everything it needs inside its constructor.
|
||||||
What's the problem?
|
What's the problem?
|
||||||
|
@ -31,7 +36,7 @@ include ../_util-fns
|
||||||
|
|
||||||
What if the `Engine` class evolves and its constructor requires a parameter?
|
What if the `Engine` class evolves and its constructor requires a parameter?
|
||||||
Our `Car` is broken and stays broken until we rewrite it along the lines of
|
Our `Car` is broken and stays broken until we rewrite it along the lines of
|
||||||
`this.engine = new Engine(theNewParameter)`.
|
`#{prefix}engine = new Engine(theNewParameter)`.
|
||||||
We didn't care about `Engine` constructor parameters when we first wrote `Car`.
|
We didn't care about `Engine` constructor parameters when we first wrote `Car`.
|
||||||
We don't really care about them now.
|
We don't really care about them now.
|
||||||
But we'll *have* to start caring because
|
But we'll *have* to start caring because
|
||||||
|
@ -58,40 +63,45 @@ include ../_util-fns
|
||||||
if we can't swap in low-pressure tires during the test?
|
if we can't swap in low-pressure tires during the test?
|
||||||
|
|
||||||
We have no control over the car's hidden dependencies.
|
We have no control over the car's hidden dependencies.
|
||||||
When we can't control the dependencies, a class become difficult to test.
|
When we can't control the dependencies, a class becomes difficult to test.
|
||||||
|
|
||||||
How can we make `Car` more robust, flexible, and testable?
|
How can we make `Car` more robust, flexible, and testable?
|
||||||
|
|
||||||
That's super easy. We probably already know what to do. We change our `Car` constructor to a version with DI:
|
That's super easy. We change our `Car` constructor to a version with DI:
|
||||||
|
|
||||||
<a id="ctor-injection"></a>
|
<a id="ctor-injection"></a>
|
||||||
|
// #enddocregion why-2
|
||||||
+makeTabs(
|
+makeTabs(
|
||||||
'dependency-injection/ts/app/car/car.ts, dependency-injection/ts/app/car/car-no-di.ts',
|
'dependency-injection/ts/app/car/car.ts, dependency-injection/ts/app/car/car-no-di.ts',
|
||||||
'car-ctor, car-ctor',
|
'car-ctor, car-ctor',
|
||||||
'app/car/car.ts (excerpt with DI), app/car/car.ts (excerpt without DI)')(format=".")
|
'app/car/car.ts (excerpt with DI), app/car/car.ts (excerpt without DI)')(format=".")
|
||||||
|
// #docregion why-3-1
|
||||||
:marked
|
:marked
|
||||||
See what happened? We moved the definition of the dependencies to the constructor.
|
See what happened? We moved the definition of the dependencies to the constructor.
|
||||||
Our `Car` class no longer creates an engine or tires.
|
Our `Car` class no longer creates an engine or tires.
|
||||||
It just consumes them.
|
It just consumes them.
|
||||||
|
// #enddocregion why-3-1
|
||||||
|
// TypeScript only
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
We also leverage TypeScript's constructor syntax for declaring parameters and properties simultaneously.
|
We also leverage TypeScript's constructor syntax for declaring parameters and properties simultaneously.
|
||||||
|
// #docregion why-3-2
|
||||||
:marked
|
:marked
|
||||||
Now we create a car by passing the engine and tires to the constructor.
|
Now we create a car by passing the engine and tires to the constructor.
|
||||||
|
// #enddocregion why-3-2
|
||||||
- var stylePattern = { otl: /(new Car.*$)/gm };
|
- var stylePattern = { otl: /(new Car.*$)/gm };
|
||||||
+makeExample('dependency-injection/ts/app/car/car-creations.ts', 'car-ctor-instantiation', '', stylePattern)(format=".")
|
+makeExample('dependency-injection/ts/app/car/car-creations.ts', 'car-ctor-instantiation', '', stylePattern)(format=".")
|
||||||
|
// #docregion why-4
|
||||||
:marked
|
:marked
|
||||||
How cool is that?
|
How cool is that?
|
||||||
The definition of the engine and tire dependencies are decoupled from the `Car` class itself.
|
The definition of the engine and tire dependencies are
|
||||||
|
decoupled from the `Car` class itself.
|
||||||
We can pass in any kind of engine or tires we like, as long as they
|
We can pass in any kind of engine or tires we like, as long as they
|
||||||
conform to the general API requirements of an engine or tires.
|
conform to the general API requirements of an engine or tires.
|
||||||
|
|
||||||
If someone extends the `Engine` class, that is not `Car`'s problem.
|
If someone extends the `Engine` class, that is not `Car`'s problem.
|
||||||
|
// #enddocregion why-4
|
||||||
|
// Must copy the following, due to indented +make.
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
The _consumer_ of `Car` has the problem. The consumer must update the car creation code to
|
The _consumer_ of `Car` has the problem. The consumer must update the car creation code to
|
||||||
|
@ -103,16 +113,16 @@ include ../_util-fns
|
||||||
:marked
|
:marked
|
||||||
The critical point is this: `Car` itself did not have to change.
|
The critical point is this: `Car` itself did not have to change.
|
||||||
We'll take care of the consumer's problem soon enough.
|
We'll take care of the consumer's problem soon enough.
|
||||||
|
// #docregion why-6
|
||||||
:marked
|
:marked
|
||||||
The `Car` class is much easier to test because we are in complete control
|
The `Car` class is much easier to test because we are in complete control
|
||||||
of its dependencies.
|
of its dependencies.
|
||||||
We can pass mocks to the constructor that do exactly what we want them to do
|
We can pass mocks to the constructor that do exactly what we want them to do
|
||||||
during each test:
|
during each test:
|
||||||
|
// #enddocregion why-6
|
||||||
- var stylePattern = { otl: /(new Car.*$)/gm };
|
- var stylePattern = { otl: /(new Car.*$)/gm };
|
||||||
+makeExample('dependency-injection/ts/app/car/car-creations.ts', 'car-ctor-instantiation-with-mocks', '', stylePattern)(format=".")
|
+makeExample('dependency-injection/ts/app/car/car-creations.ts', 'car-ctor-instantiation-with-mocks', '', stylePattern)(format=".")
|
||||||
|
// #docregion why-7
|
||||||
:marked
|
:marked
|
||||||
**We just learned what dependency injection is**.
|
**We just learned what dependency injection is**.
|
||||||
|
|
||||||
|
@ -126,9 +136,9 @@ include ../_util-fns
|
||||||
We need something that takes care of assembling these parts for us.
|
We need something that takes care of assembling these parts for us.
|
||||||
|
|
||||||
We could write a giant class to do that:
|
We could write a giant class to do that:
|
||||||
|
// #enddocregion why-7
|
||||||
+makeExample('dependency-injection/ts/app/car/car-factory.ts', null, 'app/car/car-factory.ts')
|
+makeExample('dependency-injection/ts/app/car/car-factory.ts', null, 'app/car/car-factory.ts')
|
||||||
|
// #docregion why-8
|
||||||
:marked
|
:marked
|
||||||
It's not so bad now with only three creation methods.
|
It's not so bad now with only three creation methods.
|
||||||
But maintaining it will be hairy as the application grows.
|
But maintaining it will be hairy as the application grows.
|
||||||
|
@ -143,9 +153,9 @@ include ../_util-fns
|
||||||
We register some classes with this injector, and it figures out how to create them.
|
We register some classes with this injector, and it figures out how to create them.
|
||||||
|
|
||||||
When we need a `Car`, we simply ask the injector to get it for us and we're good to go.
|
When we need a `Car`, we simply ask the injector to get it for us and we're good to go.
|
||||||
|
// #enddocregion why-8
|
||||||
+makeExample('dependency-injection/ts/app/car/car-injector.ts','injector-call')(format=".")
|
+makeExample('dependency-injection/ts/app/car/car-injector.ts','injector-call')(format=".")
|
||||||
|
// #docregion why-9
|
||||||
:marked
|
:marked
|
||||||
Everyone wins. The `Car` knows nothing about creating an `Engine` or `Tires`.
|
Everyone wins. The `Car` knows nothing about creating an `Engine` or `Tires`.
|
||||||
The consumer knows nothing about creating a `Car`.
|
The consumer knows nothing about creating a `Car`.
|
||||||
|
@ -156,7 +166,8 @@ include ../_util-fns
|
||||||
|
|
||||||
Now that we know what dependency injection is and appreciate its benefits,
|
Now that we know what dependency injection is and appreciate its benefits,
|
||||||
let's see how it is implemented in Angular.
|
let's see how it is implemented in Angular.
|
||||||
|
// #enddocregion why-9
|
||||||
|
// #docregion di-1
|
||||||
<a id="angular-di"></a>
|
<a id="angular-di"></a>
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
|
@ -170,7 +181,7 @@ include ../_util-fns
|
||||||
|
|
||||||
We'll begin with a simplified version of the `HeroesComponent`
|
We'll begin with a simplified version of the `HeroesComponent`
|
||||||
that we built in the [The Tour of Heroes](../tutorial/).
|
that we built in the [The Tour of Heroes](../tutorial/).
|
||||||
|
// #enddocregion di-1
|
||||||
+makeTabs(
|
+makeTabs(
|
||||||
`dependency-injection/ts/app/heroes/heroes.component.1.ts,
|
`dependency-injection/ts/app/heroes/heroes.component.1.ts,
|
||||||
dependency-injection/ts/app/heroes/hero-list.component.1.ts,
|
dependency-injection/ts/app/heroes/hero-list.component.1.ts,
|
||||||
|
@ -181,34 +192,36 @@ include ../_util-fns
|
||||||
app/heroes/hero-list.component.ts,
|
app/heroes/hero-list.component.ts,
|
||||||
app/heroes/hero.ts,
|
app/heroes/hero.ts,
|
||||||
app/heroes/mock-heroes.ts`)
|
app/heroes/mock-heroes.ts`)
|
||||||
|
// #docregion di-2
|
||||||
:marked
|
:marked
|
||||||
The `HeroesComponent` is the root component of the *Heroes* feature area.
|
The `HeroesComponent` is the root component of the *Heroes* feature area.
|
||||||
It governs all the child components of this area.
|
It governs all the child components of this area.
|
||||||
Our stripped down version has only one child, `HeroListComponent`,
|
Our stripped down version has only one child, `HeroListComponent`,
|
||||||
which displays a list of heroes.
|
which displays a list of heroes.
|
||||||
.l-sub-section
|
// #enddocregion di-2
|
||||||
:marked
|
// #docregion di-3
|
||||||
Do we really need so many files? Of course not!
|
|
||||||
We're going *beyond* the strictly necessary
|
|
||||||
in order to illustrate patterns that work well in real applications.
|
|
||||||
:marked
|
:marked
|
||||||
Right now `HeroListComponent` gets heroes from `HEROES`, an in-memory collection
|
Right now `HeroListComponent` gets heroes from `HEROES`, an in-memory collection
|
||||||
defined in another file and imported by this component.
|
defined in another file.
|
||||||
That may suffice in the early stages of development, but it's far from ideal.
|
That may suffice in the early stages of development, but it's far from ideal.
|
||||||
As soon as we try to test this component or want to get our heroes data from a remote server,
|
As soon as we try to test this component or want to get our heroes data from a remote server,
|
||||||
we'll have to change the implementation of `heroes` and
|
we'll have to change the implementation of `heroes` and
|
||||||
fix every other use of the `HEROES` mock data.
|
fix every other use of the `HEROES` mock data.
|
||||||
|
|
||||||
Let's make a service that hides how we get hero data.
|
Let's make a service that hides how we get hero data.
|
||||||
|
// #enddocregion di-3
|
||||||
|
|
||||||
|
// Unnecessary for Dart
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
Write this service in its own file. See [this note](#forward-ref) to understand why.
|
Write this service in its own file. See [this note](#forward-ref) to understand why.
|
||||||
|
|
||||||
+makeExample('dependency-injection/ts/app/heroes/hero.service.1.ts',null, 'app/heroes/hero.service.ts' )
|
+makeExample('dependency-injection/ts/app/heroes/hero.service.1.ts',null, 'app/heroes/hero.service.ts' )
|
||||||
|
// #docregion di-4
|
||||||
:marked
|
:marked
|
||||||
Our `HeroService` exposes a `getHeroes` method that returns
|
Our `HeroService` exposes a `getHeroes` method that returns
|
||||||
the same mock data as before, but none of its consumers need to know that.
|
the same mock data as before, but none of its consumers need to know that.
|
||||||
|
// #enddocregion di-4
|
||||||
|
// #docregion di-5
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
We aren't even pretending this is a real service.
|
We aren't even pretending this is a real service.
|
||||||
|
@ -217,24 +230,30 @@ include ../_util-fns
|
||||||
[ES2015 promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
|
[ES2015 promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
|
||||||
We'd also have to rewrite the way components consume our service.
|
We'd also have to rewrite the way components consume our service.
|
||||||
This is important in general, but not to our current story.
|
This is important in general, but not to our current story.
|
||||||
|
// #enddocregion di-5
|
||||||
|
// #docregion di-6
|
||||||
:marked
|
:marked
|
||||||
A service is nothing more than a class in Angular 2.
|
A service is nothing more than a class in Angular 2.
|
||||||
It remains nothing more than a class until we register it with an Angular injector.
|
It remains nothing more than a class until we register it with an Angular injector.
|
||||||
|
// #enddocregion di-6
|
||||||
|
// #docregion di-configure-injector-1
|
||||||
|
:marked
|
||||||
### Configuring the injector
|
### Configuring the injector
|
||||||
|
|
||||||
<a id="bootstrap"></a>
|
<a id="bootstrap"></a>
|
||||||
We don't have to create an Angular injector.
|
We don't have to create an Angular injector.
|
||||||
Angular creates an application-wide injector for us during the bootstrap process.
|
Angular creates an application-wide injector for us during the bootstrap process.
|
||||||
|
// #enddocregion di-configure-injector-1
|
||||||
+makeExample('dependency-injection/ts/app/main.ts', 'bootstrap', 'app/main.ts (excerpt)')(format='.')
|
+makeExample('dependency-injection/ts/app/main.ts', 'bootstrap', 'app/main.ts (excerpt)')(format='.')
|
||||||
|
// #docregion di-configure-injector-2
|
||||||
:marked
|
:marked
|
||||||
We do have to configure the injector by registering the **providers**
|
We do have to configure the injector by registering the **providers**
|
||||||
that create the services our application requires.
|
that create the services our application requires.
|
||||||
We'll explain what [providers](#providers) are later in this chapter.
|
We'll explain what [providers](#providers) are later in this chapter.
|
||||||
Before we do, let's see an example of provider registration during bootstrapping:
|
Before we do, let's see an example of provider registration during bootstrapping:
|
||||||
|
// #enddocregion di-configure-injector-2
|
||||||
+makeExample('dependency-injection/ts/app/main.1.ts', 'bootstrap')(format='.')
|
+makeExample('dependency-injection/ts/app/main.1.ts', 'bootstrap')(format='.')
|
||||||
|
// #docregion di-configure-injector-3
|
||||||
:marked
|
:marked
|
||||||
The injector now knows about our `HeroService`.
|
The injector now knows about our `HeroService`.
|
||||||
An instance of our `HeroService` will be available for injection across our entire application.
|
An instance of our `HeroService` will be available for injection across our entire application.
|
||||||
|
@ -247,36 +266,42 @@ include ../_util-fns
|
||||||
The preferred approach is to register application providers in application components.
|
The preferred approach is to register application providers in application components.
|
||||||
Because the `HeroService` is used within the *Heroes* feature area —
|
Because the `HeroService` is used within the *Heroes* feature area —
|
||||||
and nowhere else — the ideal place to register it is in the top-level `HeroesComponent`.
|
and nowhere else — the ideal place to register it is in the top-level `HeroesComponent`.
|
||||||
|
// #enddocregion di-configure-injector-3
|
||||||
|
// #docregion di-register-providers-1
|
||||||
|
:marked
|
||||||
### Registering providers in a component
|
### Registering providers in a component
|
||||||
Here's a revised `HeroesComponent` that registers the `HeroService`.
|
Here's a revised `HeroesComponent` that registers the `HeroService`.
|
||||||
|
// #enddocregion di-register-providers-1
|
||||||
+makeExample('dependency-injection/ts/app/heroes/heroes.component.1.ts',null,'app/heroes/heroes.component.ts')
|
+makeExample('dependency-injection/ts/app/heroes/heroes.component.1.ts',null,'app/heroes/heroes.component.ts')
|
||||||
|
// #docregion di-register-providers-2
|
||||||
:marked
|
:marked
|
||||||
Look closely at the `providers` part of the `@Component` metadata:
|
Look closely at the `providers` part of the `@Component` metadata:
|
||||||
|
// #enddocregion di-register-providers-2
|
||||||
+makeExample('dependency-injection/ts/app/heroes/heroes.component.1.ts','providers')(format='.')
|
+makeExample('dependency-injection/ts/app/heroes/heroes.component.1.ts','providers')(format='.')
|
||||||
|
// #docregion di-register-providers-3
|
||||||
:marked
|
:marked
|
||||||
An instance of the `HeroService` is now available for injection in this `HeroesComponent`
|
An instance of the `HeroService` is now available for injection in this `HeroesComponent`
|
||||||
and all of its child components.
|
and all of its child components.
|
||||||
|
|
||||||
The `HeroesComponent` itself doesn't happen to need the `HeroService`.
|
The `HeroesComponent` itself doesn't happen to need the `HeroService`.
|
||||||
But its child `HeroListComponent` does, so we head there next.
|
But its child `HeroListComponent` does, so we head there next.
|
||||||
|
// #enddocregion di-register-providers-3
|
||||||
|
// #docregion di-prepare-for-injection-1
|
||||||
|
:marked
|
||||||
### Preparing the HeroListComponent for injection
|
### Preparing the HeroListComponent for injection
|
||||||
|
|
||||||
The `HeroListComponent` should get heroes from the injected `HeroService`.
|
The `HeroListComponent` should get heroes from the injected `HeroService`.
|
||||||
Per the dependency injection pattern, the component must ask for the service in its constructor, [as we explained
|
Per the dependency injection pattern, the component must ask for the service in its constructor, [as we explained
|
||||||
earlier](#ctor-injection).
|
earlier](#ctor-injection).
|
||||||
It's a small change:
|
It's a small change:
|
||||||
|
// #enddocregion di-prepare-for-injection-1
|
||||||
+makeTabs(
|
+makeTabs(
|
||||||
`dependency-injection/ts/app/heroes/hero-list.component.2.ts,
|
`dependency-injection/ts/app/heroes/hero-list.component.2.ts,
|
||||||
dependency-injection/ts/app/heroes/hero-list.component.1.ts`,
|
dependency-injection/ts/app/heroes/hero-list.component.1.ts`,
|
||||||
null,
|
null,
|
||||||
`app/heroes/hero-list.component (with DI),
|
`app/heroes/hero-list.component (with DI),
|
||||||
app/heroes/hero-list.component (without DI)`)
|
app/heroes/hero-list.component (without DI)`)
|
||||||
:marked
|
// Must copy the following, due to indented +make.
|
||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
### Focus on the constructor
|
### Focus on the constructor
|
||||||
|
@ -285,6 +310,7 @@ include ../_util-fns
|
||||||
|
|
||||||
+makeExample('dependency-injection/ts/app/heroes/hero-list.component.2.ts', 'ctor')(format=".")
|
+makeExample('dependency-injection/ts/app/heroes/hero-list.component.2.ts', 'ctor')(format=".")
|
||||||
|
|
||||||
|
// TypeScript only
|
||||||
:marked
|
:marked
|
||||||
We're writing in TypeScript and have followed the parameter name with a type annotation, `:HeroService`.
|
We're writing in TypeScript and have followed the parameter name with a type annotation, `:HeroService`.
|
||||||
The class is also decorated with the `@Component` decorator (scroll up to confirm that fact).
|
The class is also decorated with the `@Component` decorator (scroll up to confirm that fact).
|
||||||
|
@ -295,14 +321,15 @@ include ../_util-fns
|
||||||
|
|
||||||
That's how the Angular injector knows to inject an instance of the `HeroService` when it
|
That's how the Angular injector knows to inject an instance of the `HeroService` when it
|
||||||
creates a new `HeroListComponent`.
|
creates a new `HeroListComponent`.
|
||||||
<a id="di-metadata"></a>
|
// #docregion di-create-injector-implicitly-1
|
||||||
:marked
|
:marked
|
||||||
|
<a id="di-metadata"></a>
|
||||||
### Creating the injector (implicitly)
|
### Creating the injector (implicitly)
|
||||||
When we introduced the idea of an injector above, we showed how to create
|
When we introduced the idea of an injector above, we showed how to create
|
||||||
an injector and use it to create a new `Car`.
|
an injector and use it to create a new `Car`.
|
||||||
|
// #enddocregion di-create-injector-implicitly-1
|
||||||
+makeExample('dependency-injection/ts/app/car/car-injector.ts','injector-create-and-call')(format=".")
|
+makeExample('dependency-injection/ts/app/car/car-injector.ts','injector-create-and-call')(format=".")
|
||||||
|
// #docregion di-create-injector-implicitly-2
|
||||||
:marked
|
:marked
|
||||||
We won't find code like that in the Tour of Heroes or any of our other samples.
|
We won't find code like that in the Tour of Heroes or any of our other samples.
|
||||||
We *could* write [code with an explicit injector](#explicit-injector) if we *had* to, but we rarely do.
|
We *could* write [code with an explicit injector](#explicit-injector) if we *had* to, but we rarely do.
|
||||||
|
@ -310,7 +337,9 @@ include ../_util-fns
|
||||||
when it creates components for us — whether through HTML markup, as in `<hero-list></hero-list>`,
|
when it creates components for us — whether through HTML markup, as in `<hero-list></hero-list>`,
|
||||||
or after navigating to a component with the [router](./router.html).
|
or after navigating to a component with the [router](./router.html).
|
||||||
If we let Angular do its job, we'll enjoy the benefits of automated dependency injection.
|
If we let Angular do its job, we'll enjoy the benefits of automated dependency injection.
|
||||||
|
// #enddocregion di-create-injector-implicitly-2
|
||||||
|
// #docregion di-singleton-services
|
||||||
|
:marked
|
||||||
### Singleton services
|
### Singleton services
|
||||||
Dependencies are singletons within the scope of an injector.
|
Dependencies are singletons within the scope of an injector.
|
||||||
In our example, a single `HeroService` instance is shared among the
|
In our example, a single `HeroService` instance is shared among the
|
||||||
|
@ -319,19 +348,25 @@ include ../_util-fns
|
||||||
However, Angular DI is an hierarchical injection
|
However, Angular DI is an hierarchical injection
|
||||||
system, which means that nested injectors can create their own service instances.
|
system, which means that nested injectors can create their own service instances.
|
||||||
Learn more about that in the [Hierarchical Injectors](./hierarchical-dependency-injection.html) chapter.
|
Learn more about that in the [Hierarchical Injectors](./hierarchical-dependency-injection.html) chapter.
|
||||||
|
// #enddocregion di-singleton-services
|
||||||
|
|
||||||
|
// Skip this for Dart, for now
|
||||||
|
// #docregion di-testing-component-1
|
||||||
|
:marked
|
||||||
### Testing the component
|
### Testing the component
|
||||||
We emphasized earlier that designing a class for dependency injection makes the class easier to test.
|
We emphasized earlier that designing a class for dependency injection makes the class easier to test.
|
||||||
Listing dependencies as constructor parameters may be all we need to test application parts effectively.
|
Listing dependencies as constructor parameters may be all we need to test application parts effectively.
|
||||||
|
|
||||||
For example, we can create a new `HeroListComponent` with a mock service that we can manipulate
|
For example, we can create a new `HeroListComponent` with a mock service that we can manipulate
|
||||||
under test:
|
under test:
|
||||||
|
// #enddocregion di-testing-component-1
|
||||||
+makeExample('dependency-injection/ts/app/test.component.ts', 'spec')(format='.')
|
+makeExample('dependency-injection/ts/app/test.component.ts', 'spec')(format='.')
|
||||||
|
// #docregion di-testing-component-2
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
Learn more in [Testing](../testing/index.html).
|
Learn more in [Testing](../testing/index.html).
|
||||||
|
// #enddocregion di-testing-component-2
|
||||||
|
// #docregion di-service-service-1
|
||||||
:marked
|
:marked
|
||||||
### When the service needs a service
|
### When the service needs a service
|
||||||
Our `HeroService` is very simple. It doesn't have any dependencies of its own.
|
Our `HeroService` is very simple. It doesn't have any dependencies of its own.
|
||||||
|
@ -342,49 +377,61 @@ include ../_util-fns
|
||||||
adding a constructor that takes a `Logger` parameter.
|
adding a constructor that takes a `Logger` parameter.
|
||||||
|
|
||||||
Here is the revision compared to the original.
|
Here is the revision compared to the original.
|
||||||
|
// #enddocregion di-service-service-1
|
||||||
+makeTabs(
|
+makeTabs(
|
||||||
`dependency-injection/ts/app/heroes/hero.service.2.ts,
|
`dependency-injection/ts/app/heroes/hero.service.2.ts,
|
||||||
dependency-injection/ts/app/heroes/hero.service.1.ts`,
|
dependency-injection/ts/app/heroes/hero.service.1.ts`,
|
||||||
null,
|
null,
|
||||||
`app/heroes/hero.service (v.2),
|
`app/heroes/hero.service (v.2),
|
||||||
app/heroes/hero.service (v.1)`)
|
app/heroes/hero.service (v.1)`)
|
||||||
|
// #docregion di-service-service-2
|
||||||
:marked
|
:marked
|
||||||
The constructor now asks for an injected instance of a `Logger` and stores it in a private property called `_logger`.
|
The constructor now asks for an injected instance of a `Logger` and stores it in a private property called `_logger`.
|
||||||
We call that property within our `getHeroes` method when anyone asks for heroes.
|
We call that property within our `getHeroes` method when anyone asks for heroes.
|
||||||
|
// #enddocregion di-service-service-2
|
||||||
|
// #docregion di-injectable-1
|
||||||
|
- var lang = current.path[1]
|
||||||
|
- var decoration = lang == 'dart' ? 'annotation' : 'decoration'
|
||||||
|
- var tsmetadata = lang == 'ts' ? 'As <a href="#di-metadata">we mentioned earlier</a>, <b>TypeScript only generates metadata for classes that have a decorator.</b>' : ''
|
||||||
|
:marked
|
||||||
<a id="injectable"></a>
|
<a id="injectable"></a>
|
||||||
### Why @Injectable?
|
### Why @Injectable?
|
||||||
Notice the `@Injectable()` decoration above the service class.
|
Notice the `@Injectable()` #{decoration} above the service class.
|
||||||
We haven't seen `@Injectable()` before.
|
We haven't seen `@Injectable()` before.
|
||||||
As it happens, we could have added it to our first version of `HeroService`.
|
As it happens, we could have added it to our first version of `HeroService`.
|
||||||
We didn't bother because we didn't need it then.
|
We didn't bother because we didn't need it then.
|
||||||
|
|
||||||
We need it now... now that our service has an injected dependency.
|
We need it now... now that our service has an injected dependency.
|
||||||
We need it because Angular requires constructor parameter metadata in order to inject a `Logger`.
|
We need it because Angular requires constructor parameter metadata in order to inject a `Logger`. !{tsmetadata}
|
||||||
As [we mentioned earlier](#di-metadata), **TypeScript only generates metadata for classes that have a decorator.**
|
// #enddocregion di-injectable-1
|
||||||
|
|
||||||
|
// #docregion di-injectable-2
|
||||||
|
- var lang = current.path[1]
|
||||||
|
- var a_decorator = lang == 'dart' ? 'an annotation' : 'a decorator'
|
||||||
|
- var decorated = lang == 'dart' ? 'annotated' : 'decorated'
|
||||||
|
- var any_decorator = lang == 'dart' ? '' : 'TypeScript generates metadata for any class with a decorator, and any decorator will do.'
|
||||||
.callout.is-helpful
|
.callout.is-helpful
|
||||||
header Always add @Injectable()
|
header Always add @Injectable()
|
||||||
:marked
|
:marked
|
||||||
We recommend adding `@Injectable()` to every service class, even those that don't have dependencies
|
We recommend adding `@Injectable()` to every service class, even those that don't have dependencies
|
||||||
and, therefore, do not technically require it. Here's why:
|
and, therefore, do not technically require it. Here's why:
|
||||||
ul(style="font-size:inherit")
|
ul(style="font-size:inherit")
|
||||||
li <b>Future proofing:</b> No need to remember <code>@Injectable</code> when we add a dependency later.
|
li <b>Future proofing:</b> No need to remember <code>@Injectable()</code> when we add a dependency later.
|
||||||
li <b>Consistency:</b> All services follow the same rules, and we don't have to wonder why a decorator is missing.
|
li <b>Consistency:</b> All services follow the same rules, and we don't have to wonder why #{a_decorator} is missing.
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
The `HeroesComponent` has an injected dependency too. Why don't we add `@Injectable()` to the `HeroesComponent`?
|
The `HeroesComponent` has an injected dependency too. Why don't we add `@Injectable()` to the `HeroesComponent`?
|
||||||
|
|
||||||
We *can* add it if we really want to. It isn't necessary because the `HeroesComponent` is already decorated with `@Component`.
|
We *can* add it if we really want to. It isn't necessary because
|
||||||
TypeScript generates metadata for *any* class with a decorator, and *any* decorator will do.
|
the `HeroesComponent` is already #{decorated} with `@Component`. #{any_decorator}
|
||||||
|
// #enddocregion di-injectable-2
|
||||||
.alert.is-critical
|
.callout.is-critical
|
||||||
|
header Always include the parentheses
|
||||||
:marked
|
:marked
|
||||||
**Always include the parentheses!** Always call `@Injectable()`.
|
Always use `@Injectable()`, not just `@Injectable`.
|
||||||
Our application will fail mysteriously if we forget the parentheses.
|
Our application will fail mysteriously if we forget the parentheses.
|
||||||
|
|
||||||
|
// #docregion logger-service-1
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Creating and registering a logger service
|
## Creating and registering a logger service
|
||||||
|
@ -393,18 +440,23 @@ include ../_util-fns
|
||||||
1. Register it with the application.
|
1. Register it with the application.
|
||||||
|
|
||||||
The logger service implementation is no big deal.
|
The logger service implementation is no big deal.
|
||||||
|
// #enddocregion logger-service-1
|
||||||
+makeExample(
|
+makeExample(
|
||||||
'dependency-injection/ts/app/logger.service.ts',null, 'app/logger.service')
|
'dependency-injection/ts/app/logger.service.ts',null, 'app/logger.service')
|
||||||
|
|
||||||
|
// Copied into Dart, due to different directory structure
|
||||||
:marked
|
:marked
|
||||||
We're likely to need the same logger service everywhere in our application,
|
We're likely to need the same logger service everywhere in our application,
|
||||||
so we put it at the root level of the application in the `app/` folder, and
|
so we put it at the root level of the application in the `app/` folder, and
|
||||||
we register it in the `providers` array of the metadata for our application root component, `AppComponent`.
|
we register it in the `providers` array of the metadata for our application root component, `AppComponent`.
|
||||||
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-logger', 'app/app.component.ts (excerpt)')
|
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-logger', 'app/app.component.ts (excerpt)')
|
||||||
|
// #docregion logger-service-3
|
||||||
:marked
|
:marked
|
||||||
If we forget to register the logger, Angular throws an exception when it first looks for the logger:
|
If we forget to register the logger, Angular throws an exception when it first looks for the logger:
|
||||||
code-example(format, language="html").
|
code-example(format, language="html").
|
||||||
EXCEPTION: No provider for Logger! (HeroListComponent -> HeroService -> Logger)
|
EXCEPTION: No provider for Logger! (HeroListComponent -> HeroService -> Logger)
|
||||||
|
// #enddocregion logger-service-3
|
||||||
|
// #docregion logger-service-4
|
||||||
:marked
|
:marked
|
||||||
That's Angular telling us that the dependency injector couldn't find the *provider* for the logger.
|
That's Angular telling us that the dependency injector couldn't find the *provider* for the logger.
|
||||||
It needed that provider to create a `Logger` to inject into a new
|
It needed that provider to create a `Logger` to inject into a new
|
||||||
|
@ -419,20 +471,32 @@ code-example(format, language="html").
|
||||||
|
|
||||||
Our `HeroService` currently requires a `Logger`. What if we could get by without a logger?
|
Our `HeroService` currently requires a `Logger`. What if we could get by without a logger?
|
||||||
We'd use it if we had it, ignore it if we didn't. We can do that.
|
We'd use it if we had it, ignore it if we didn't. We can do that.
|
||||||
|
// #enddocregion logger-service-4
|
||||||
|
|
||||||
|
// TypeScript only?
|
||||||
|
:marked
|
||||||
First import the `@Optional()` decorator.
|
First import the `@Optional()` decorator.
|
||||||
+makeExample('dependency-injection/ts/app/providers.component.ts','import-optional')(format='.')
|
+makeExample('dependency-injection/ts/app/providers.component.ts','import-optional')(format='.')
|
||||||
|
|
||||||
|
// #docregion logger-service-5
|
||||||
|
- var lang = current.path[1]
|
||||||
|
- var rewrite = lang == 'dart' ? 'Just rewrite' : 'Then rewrite'
|
||||||
|
- var decorator = lang == 'dart' ? 'annotation' : 'decorator'
|
||||||
:marked
|
:marked
|
||||||
Then rewrite the constructor with `@Optional()` decorator preceding the private `_logger` parameter.
|
#{rewrite} the constructor with the `@Optional()` #{decorator} preceding the private `_logger` parameter.
|
||||||
That tells the injector that `_logger` is optional.
|
That tells the injector that `_logger` is optional.
|
||||||
|
// #enddocregion logger-service-5
|
||||||
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-10-ctor')(format='.')
|
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-10-ctor')(format='.')
|
||||||
|
// #docregion logger-service-6
|
||||||
:marked
|
:marked
|
||||||
Be prepared for a null logger. If we don't register one somewhere up the line,
|
Be prepared for a null logger. If we don't register one somewhere up the line,
|
||||||
the injector will inject `null`. We have a method that logs.
|
the injector will inject `null`. We have a method that logs.
|
||||||
What can we do to avoid a null reference exception?
|
What can we do to avoid a null reference exception?
|
||||||
|
|
||||||
We could substitute a *do-nothing* logger stub so that calling methods continue to work:
|
We could substitute a *do-nothing* logger stub so that calling methods continue to work:
|
||||||
|
// #enddocregion logger-service-6
|
||||||
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-10-logger')(format='.')
|
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-10-logger')(format='.')
|
||||||
|
// #docregion logger-service-7
|
||||||
:marked
|
:marked
|
||||||
Obviously we'd take a more sophisticated approach if the logger were optional
|
Obviously we'd take a more sophisticated approach if the logger were optional
|
||||||
in multiple locations.
|
in multiple locations.
|
||||||
|
@ -440,7 +504,10 @@ code-example(format, language="html").
|
||||||
But enough about optional loggers. In our sample application, the `Logger` is required.
|
But enough about optional loggers. In our sample application, the `Logger` is required.
|
||||||
We must register a `Logger` with the application injector using *providers*,
|
We must register a `Logger` with the application injector using *providers*,
|
||||||
as we learn in the next section.
|
as we learn in the next section.
|
||||||
|
// #enddocregion logger-service-7
|
||||||
|
|
||||||
|
// #docregion providers-1
|
||||||
|
:marked
|
||||||
<a id="providers"></a>
|
<a id="providers"></a>
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
|
@ -453,62 +520,94 @@ code-example(format, language="html").
|
||||||
We must register a service *provider* with the injector, or it won't know how to create the service.
|
We must register a service *provider* with the injector, or it won't know how to create the service.
|
||||||
|
|
||||||
Earlier we registered the `Logger` service in the `providers` array of the metadata for the `AppComponent` like this:
|
Earlier we registered the `Logger` service in the `providers` array of the metadata for the `AppComponent` like this:
|
||||||
|
// #enddocregion providers-1
|
||||||
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-logger')
|
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-logger')
|
||||||
|
// #docregion providers-2
|
||||||
|
- var lang = current.path[1]
|
||||||
|
- var implements = lang == 'dart' ? 'implements' : 'looks and behaves like a '
|
||||||
|
- var objectlike = lang == 'dart' ? '' : 'an object that behaves like '
|
||||||
|
- var loggerlike = lang == 'dart' ? '' : 'We could provide a logger-like object. '
|
||||||
:marked
|
:marked
|
||||||
The `providers` array appears to hold a service class.
|
The `providers` array appears to hold a service class.
|
||||||
In reality it holds an instance of the [Provider](/docs/ts/latest/api/core/Provider-class.html) class that can create that service.
|
In reality it holds an instance of the [Provider](/docs/ts/latest/api/core/Provider-class.html) class that can create that service.
|
||||||
|
|
||||||
There are many ways to *provide* something that looks and behaves like a `Logger`.
|
There are many ways to *provide* something that #{implements} `Logger`.
|
||||||
The `Logger` class itself is an obvious and natural provider — it has the right shape and it's designed to be created.
|
The `Logger` class itself is an obvious and natural provider — it has the right shape and it's designed to be created.
|
||||||
But it's not the only way.
|
But it's not the only way.
|
||||||
|
|
||||||
We can configure the injector with alternative providers that can deliver an object that behaves like a `Logger`.
|
We can configure the injector with alternative providers that can deliver #{objectlike} a `Logger`.
|
||||||
We could provide a substitute class. We could provide a logger-like object.
|
We could provide a substitute class. #{loggerlike}
|
||||||
We could give it a provider that calls a logger factory function.
|
We could give it a provider that calls a logger factory function.
|
||||||
Any of these approaches might be a good choice under the right circumstances.
|
Any of these approaches might be a good choice under the right circumstances.
|
||||||
|
|
||||||
What matters is that the injector has a provider to go to when it needs a `Logger`.
|
What matters is that the injector has a provider to go to when it needs a `Logger`.
|
||||||
|
// #enddocregion providers-2
|
||||||
|
// #docregion providers-provide-1
|
||||||
|
:marked
|
||||||
<a id="provide"></a>
|
<a id="provide"></a>
|
||||||
### The *provide* function
|
// #enddocregion providers-provide-1
|
||||||
|
|
||||||
|
// Don't mention provide function in Dart
|
||||||
|
:marked
|
||||||
|
### The *Provider* class and *provide* function
|
||||||
|
// #docregion providers-provide-1-1
|
||||||
|
:marked
|
||||||
We wrote the `providers` array like this:
|
We wrote the `providers` array like this:
|
||||||
|
// #enddocregion providers-provide-1-1
|
||||||
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-1')
|
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-1')
|
||||||
|
// #docregion providers-provide-2
|
||||||
:marked
|
:marked
|
||||||
This is actually a short-hand expression for a provider registration that creates a new instance of the
|
This is actually a short-hand expression for a provider registration that creates a new instance of the
|
||||||
[Provider](/docs/ts/latest/api/core/Provider-class.html) class.
|
[Provider](/docs/ts/latest/api/core/Provider-class.html) class.
|
||||||
|
// #enddocregion providers-provide-2
|
||||||
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-2')
|
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-2')
|
||||||
|
// #docregion providers-provide-3
|
||||||
|
// Skip for Dart, where the provide() function won't pass type checking.
|
||||||
:marked
|
:marked
|
||||||
The [provide](/docs/ts/latest/api/core/provide-function.html) function is the more common, friendlier way to create a `Provider`:
|
The [provide](/docs/ts/latest/api/core/provide-function.html) function is the more common, friendlier way to create a `Provider`:
|
||||||
|
// #enddocregion providers-provide-3
|
||||||
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-3')
|
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-3')
|
||||||
|
// #docregion providers-provide-4-1
|
||||||
|
// Modified for Dart.
|
||||||
:marked
|
:marked
|
||||||
In both approaches — `Provider` class and `provide` function —
|
In both approaches — `Provider` class and `provide` function —
|
||||||
we supply two arguments.
|
we supply two arguments.
|
||||||
|
// #enddocregion providers-provide-4-1
|
||||||
|
// #docregion providers-provide-4-2
|
||||||
|
:marked
|
||||||
The first is the [token](#token) that serves as the key for both locating a dependency value
|
The first is the [token](#token) that serves as the key for both locating a dependency value
|
||||||
and registering the provider.
|
and registering the provider.
|
||||||
|
// #enddocregion providers-provide-4-2
|
||||||
|
|
||||||
|
// Dart is different here (uses an optional parameter)
|
||||||
|
:marked
|
||||||
The second is a provider definition object,
|
The second is a provider definition object,
|
||||||
which we can think of as a *recipe* for creating the dependency value.
|
which we can think of as a *recipe* for creating the dependency value.
|
||||||
There are many ways to create dependency values... and many ways to write a recipe.
|
There are many ways to create dependency values... and many ways to write a recipe.
|
||||||
|
// #docregion providers-alternative-1
|
||||||
|
:marked
|
||||||
<a id="class-provider"></a>
|
<a id="class-provider"></a>
|
||||||
### Alternative class providers
|
### Alternative class providers
|
||||||
|
|
||||||
Occasionally we'll ask a different class to provide the service.
|
Occasionally we'll ask a different class to provide the service.
|
||||||
The following code tells the injector
|
The following code tells the injector
|
||||||
to return a `BetterLogger` when something asks for the `Logger`.
|
to return a `BetterLogger` when something asks for the `Logger`.
|
||||||
|
// #enddocregion providers-alternative-1
|
||||||
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-4')
|
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-4')
|
||||||
|
// #docregion providers-alternative-2
|
||||||
:marked
|
:marked
|
||||||
### Class provider with dependencies
|
### Class provider with dependencies
|
||||||
Maybe an `EvenBetterLogger` could display the user name in the log message.
|
Maybe an `EvenBetterLogger` could display the user name in the log message.
|
||||||
This logger gets the user from the injected `UserService`,
|
This logger gets the user from the injected `UserService`,
|
||||||
which happens also to be injected at the application level.
|
which happens also to be injected at the application level.
|
||||||
|
// #enddocregion providers-alternative-2
|
||||||
+makeExample('dependency-injection/ts/app/providers.component.ts','EvenBetterLogger')
|
+makeExample('dependency-injection/ts/app/providers.component.ts','EvenBetterLogger')
|
||||||
|
// #docregion providers-alternative-3
|
||||||
:marked
|
:marked
|
||||||
Configure it like we did `BetterLogger`.
|
Configure it like we did `BetterLogger`.
|
||||||
|
// #enddocregion providers-alternative-3
|
||||||
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-5')(format=".")
|
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-5')(format=".")
|
||||||
|
// #docregion providers-aliased-1
|
||||||
:marked
|
:marked
|
||||||
### Aliased class providers
|
### Aliased class providers
|
||||||
|
|
||||||
|
@ -525,16 +624,21 @@ code-example(format, language="html").
|
||||||
|
|
||||||
We certainly do not want two different `NewLogger` instances in our app.
|
We certainly do not want two different `NewLogger` instances in our app.
|
||||||
Unfortunately, that's what we get if we try to alias `OldLogger` to `NewLogger` with `useClass`.
|
Unfortunately, that's what we get if we try to alias `OldLogger` to `NewLogger` with `useClass`.
|
||||||
|
// #enddocregion providers-aliased-1
|
||||||
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-6a')(format=".")
|
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-6a')(format=".")
|
||||||
|
// #docregion providers-aliased-2
|
||||||
:marked
|
:marked
|
||||||
The solution: Alias with the `useExisting` option.
|
The solution: Alias with the `useExisting` option.
|
||||||
|
// #enddocregion providers-aliased-2
|
||||||
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-6b')(format=".")
|
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-6b')(format=".")
|
||||||
|
// #docregion providers-value-1
|
||||||
<a id="value-provider"></a>
|
<a id="value-provider"></a>
|
||||||
:marked
|
:marked
|
||||||
### Value providers
|
### Value providers
|
||||||
|
// #enddocregion providers-value-1
|
||||||
|
|
||||||
|
// Typescript only
|
||||||
|
:marked
|
||||||
Sometimes it's easier to provide a ready-made object rather than ask the injector to create it from a class.
|
Sometimes it's easier to provide a ready-made object rather than ask the injector to create it from a class.
|
||||||
+makeExample('dependency-injection/ts/app/providers.component.ts','silent-logger')(format=".")
|
+makeExample('dependency-injection/ts/app/providers.component.ts','silent-logger')(format=".")
|
||||||
:marked
|
:marked
|
||||||
|
@ -542,6 +646,7 @@ code-example(format, language="html").
|
||||||
which makes this object play the logger role.
|
which makes this object play the logger role.
|
||||||
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-7')(format=".")
|
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-7')(format=".")
|
||||||
|
|
||||||
|
// #docregion providers-factory-1
|
||||||
<a id="factory-provider"></a>
|
<a id="factory-provider"></a>
|
||||||
:marked
|
:marked
|
||||||
### Factory providers
|
### Factory providers
|
||||||
|
@ -571,20 +676,24 @@ code-example(format, language="html").
|
||||||
Why? We don't know either. Stuff like this happens.
|
Why? We don't know either. Stuff like this happens.
|
||||||
:marked
|
:marked
|
||||||
Instead the `HeroService` constructor takes a boolean flag to control display of secret heroes.
|
Instead the `HeroService` constructor takes a boolean flag to control display of secret heroes.
|
||||||
|
// #enddocregion providers-factory-1
|
||||||
+makeExample('dependency-injection/ts/app/heroes/hero.service.ts','internals', 'app/heroes/hero.service.ts (excerpt)')(format='.')
|
+makeExample('dependency-injection/ts/app/heroes/hero.service.ts','internals', 'app/heroes/hero.service.ts (excerpt)')(format='.')
|
||||||
|
// #docregion providers-factory-2
|
||||||
:marked
|
:marked
|
||||||
We can inject the `Logger`, but we can't inject the boolean `isAuthorized`.
|
We can inject the `Logger`, but we can't inject the boolean `isAuthorized`.
|
||||||
We'll have to take over the creation of new instances of this `HeroService` with a factory provider.
|
We'll have to take over the creation of new instances of this `HeroService` with a factory provider.
|
||||||
|
|
||||||
A factory provider needs a factory function:
|
A factory provider needs a factory function:
|
||||||
|
// #enddocregion providers-factory-2
|
||||||
+makeExample('dependency-injection/ts/app/heroes/hero.service.provider.ts','factory', 'app/heroes/hero.service.provider.ts (excerpt)')(format='.')
|
+makeExample('dependency-injection/ts/app/heroes/hero.service.provider.ts','factory', 'app/heroes/hero.service.provider.ts (excerpt)')(format='.')
|
||||||
|
// #docregion providers-factory-3
|
||||||
:marked
|
:marked
|
||||||
Although the `HeroService` has no access to the `UserService`, our factory function does.
|
Although the `HeroService` has no access to the `UserService`, our factory function does.
|
||||||
|
|
||||||
We inject both the `Logger` and the `UserService` into the factory provider and let the injector pass them along to the factory function:
|
We inject both the `Logger` and the `UserService` into the factory provider and let the injector pass them along to the factory function:
|
||||||
|
// #enddocregion providers-factory-3
|
||||||
+makeExample('dependency-injection/ts/app/heroes/hero.service.provider.ts','provider', 'app/heroes/hero.service.provider.ts (excerpt)')(format='.')
|
+makeExample('dependency-injection/ts/app/heroes/hero.service.provider.ts','provider', 'app/heroes/hero.service.provider.ts (excerpt)')(format='.')
|
||||||
:marked
|
// #docregion providers-factory-4
|
||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
The `useFactory` field tells Angular that the provider is a factory function
|
The `useFactory` field tells Angular that the provider is a factory function
|
||||||
|
@ -593,6 +702,8 @@ code-example(format, language="html").
|
||||||
The `deps` property is an array of [provider tokens](#token).
|
The `deps` property is an array of [provider tokens](#token).
|
||||||
The `Logger` and `UserService` classes serve as tokens for their own class providers.
|
The `Logger` and `UserService` classes serve as tokens for their own class providers.
|
||||||
The injector resolves these tokens and injects the corresponding services into the matching factory function parameters.
|
The injector resolves these tokens and injects the corresponding services into the matching factory function parameters.
|
||||||
|
// #enddocregion providers-factory-4
|
||||||
|
// #docregion providers-factory-5
|
||||||
:marked
|
:marked
|
||||||
Notice that we captured the factory provider in an exported variable, `heroServiceProvider`.
|
Notice that we captured the factory provider in an exported variable, `heroServiceProvider`.
|
||||||
This extra step makes the factory provider reusable.
|
This extra step makes the factory provider reusable.
|
||||||
|
@ -601,6 +712,7 @@ code-example(format, language="html").
|
||||||
In our sample, we need it only in the `HeroesComponent`,
|
In our sample, we need it only in the `HeroesComponent`,
|
||||||
where it replaces the previous `HeroService` registration in the metadata `providers` array.
|
where it replaces the previous `HeroService` registration in the metadata `providers` array.
|
||||||
Here we see the new and the old implementation side-by-side:
|
Here we see the new and the old implementation side-by-side:
|
||||||
|
// #enddocregion providers-factory-5
|
||||||
+makeTabs(
|
+makeTabs(
|
||||||
`dependency-injection/ts/app/heroes/heroes.component.ts,
|
`dependency-injection/ts/app/heroes/heroes.component.ts,
|
||||||
dependency-injection/ts/app/heroes/heroes.component.1.ts`,
|
dependency-injection/ts/app/heroes/heroes.component.1.ts`,
|
||||||
|
@ -608,6 +720,7 @@ code-example(format, language="html").
|
||||||
`app/heroes/heroes.component (v.3),
|
`app/heroes/heroes.component (v.3),
|
||||||
app/heroes/heroes.component (v.2)`)
|
app/heroes/heroes.component (v.2)`)
|
||||||
|
|
||||||
|
// #docregion tokens-1
|
||||||
<a id="token"></a>
|
<a id="token"></a>
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
|
@ -620,58 +733,86 @@ code-example(format, language="html").
|
||||||
In all previous examples, the dependency value has been a class *instance*, and
|
In all previous examples, the dependency value has been a class *instance*, and
|
||||||
the class *type* served as its own lookup key.
|
the class *type* served as its own lookup key.
|
||||||
Here we get a `HeroService` directly from the injector by supplying the `HeroService` type as the token:
|
Here we get a `HeroService` directly from the injector by supplying the `HeroService` type as the token:
|
||||||
|
// #enddocregion tokens-1
|
||||||
+makeExample('dependency-injection/ts/app/injector.component.ts','get-hero-service')(format='.')
|
+makeExample('dependency-injection/ts/app/injector.component.ts','get-hero-service')(format='.')
|
||||||
|
// #docregion tokens-2
|
||||||
:marked
|
:marked
|
||||||
We have similar good fortune when we write a constructor that requires an injected class-based dependency.
|
We have similar good fortune when we write a constructor that requires an injected class-based dependency.
|
||||||
We define a constructor parameter with the `HeroService` class type,
|
We define a constructor parameter with the `HeroService` class type,
|
||||||
and Angular knows to inject the
|
and Angular knows to inject the
|
||||||
service associated with that `HeroService` class token:
|
service associated with that `HeroService` class token:
|
||||||
|
// #enddocregion tokens-2
|
||||||
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-8-ctor')(format=".")
|
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-8-ctor')(format=".")
|
||||||
|
// #docregion tokens-3
|
||||||
:marked
|
:marked
|
||||||
This is especially convenient when we consider that most dependency values are provided by classes.
|
This is especially convenient when we consider that most dependency values are provided by classes.
|
||||||
|
// #enddocregion tokens-3
|
||||||
|
|
||||||
|
// #docregion tokens-non-class-deps-1
|
||||||
|
- var lang = current.path[1]
|
||||||
|
- var objectexamples = lang == 'dart' ? 'a string or list literal, or maybe a function' : 'a string, a function, or an object'
|
||||||
|
// Is function injection useful? Should we show it?
|
||||||
|
:marked
|
||||||
### Non-class dependencies
|
### Non-class dependencies
|
||||||
|
|
||||||
What if the dependency value isn't a class?
|
What if the dependency value isn't a class?
|
||||||
Sometimes the thing we want to inject is a string, a function, or an object.
|
Sometimes the thing we want to inject is #{objectexamples}.
|
||||||
|
// #enddocregion tokens-non-class-deps-1
|
||||||
|
|
||||||
|
// TS/JS only
|
||||||
|
:marked
|
||||||
Applications often define configuration objects with lots of small facts like the title of the application or the address of a web API endpoint.
|
Applications often define configuration objects with lots of small facts like the title of the application or the address of a web API endpoint.
|
||||||
These configuration objects aren't always instances of a class. They tend to be object hashes like this one:
|
These configuration objects aren't always instances of a class. They tend to be object hashes like this one:
|
||||||
|
|
||||||
+makeExample('dependency-injection/ts/app/app.config.ts','config','app/app-config.ts (excerpt)')(format='.')
|
+makeExample('dependency-injection/ts/app/app.config.ts','config','app/app-config.ts (excerpt)')(format='.')
|
||||||
|
|
||||||
|
// TypeScript only?
|
||||||
:marked
|
:marked
|
||||||
We'd like to make this `config` object available for injection.
|
We'd like to make this `config` object available for injection.
|
||||||
We know we can register an object with a [value provider](#value-provider).
|
We know we can register an object with a [value provider](#value-provider).
|
||||||
But what do we use for the token?
|
But what do we use for the token?
|
||||||
We don't have a class to serve as a token. There is no `Config` class.
|
We don't have a class to serve as a token. There is no `Config` class.
|
||||||
|
|
||||||
// begin Typescript only
|
// Typescript only
|
||||||
<a id="interface"></a>
|
<a id="interface"></a>
|
||||||
:marked
|
.l-sub-section
|
||||||
### Interfaces aren't valid tokens
|
:marked
|
||||||
|
### TypeScript interfaces aren't valid tokens
|
||||||
|
|
||||||
The `CONFIG` constant has an interface, `Config`. Unfortunately, we
|
The `CONFIG` constant has an interface, `Config`. Unfortunately, we
|
||||||
cannot use an interface as a token:
|
cannot use a TypeScript interface as a token:
|
||||||
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-9a-interface')(format=".")
|
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-9a-interface')(format=".")
|
||||||
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-9a-ctor-interface')(format=".")
|
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-9a-ctor-interface')(format=".")
|
||||||
:marked
|
:marked
|
||||||
That seems strange if we're used to dependency injection in strongly typed languages, where
|
That seems strange if we're used to dependency injection in strongly typed languages, where
|
||||||
an interface is the preferred dependency lookup key.
|
an interface is the preferred dependency lookup key.
|
||||||
|
|
||||||
It's not Angular's fault. An interface is a TypeScript design-time artifact. JavaScript doesn't have interfaces.
|
It's not Angular's fault. An interface is a TypeScript design-time artifact. JavaScript doesn't have interfaces.
|
||||||
The TypeScript interface disappears from the generated JavaScript.
|
The TypeScript interface disappears from the generated JavaScript.
|
||||||
There is no interface type information left for Angular to find at runtime.
|
There is no interface type information left for Angular to find at runtime.
|
||||||
// end Typescript only
|
// end Typescript only
|
||||||
<a id="string-token"></a>
|
|
||||||
|
// #docregion tokens-opaque-1
|
||||||
|
<a id="opaque-token"></a>
|
||||||
|
- var lang = current.path[1]
|
||||||
|
- var opaquetoken = lang == 'dart' ? '<code>OpaqueToken</code>' : '<a href="/docs/ts/latest/api/core/OpaqueToken-class.html"><code>OpaqueToken</code></a>'
|
||||||
|
h3 OpaqueToken
|
||||||
|
p.
|
||||||
|
The solution is to define and use an !{opaquetoken}.
|
||||||
|
The definition looks like this:
|
||||||
|
// #enddocregion tokens-opaque-1
|
||||||
|
+makeExample('dependency-injection/ts/app/app.config.ts','token')(format='.')
|
||||||
:marked
|
:marked
|
||||||
### String tokens
|
We register the dependency provider using the `OpaqueToken` object:
|
||||||
Fortunately, we can register any dependency provider with a string token.
|
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-9b')(format=".")
|
||||||
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-9a')(format=".")
|
// #docregion tokens-opaque-2
|
||||||
:marked
|
- var lang = current.path[1]
|
||||||
Now we inject the configuration object into any constructor that needs it, with
|
- var decorated = lang == 'dart' ? 'annotated' : 'decorated'
|
||||||
the help of an `@Inject` decorator that tells Angular how to find the configuration dependency value.
|
- var configuration = lang == 'dart' ? '' : 'configuration'
|
||||||
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-9a-ctor')(format=".")
|
|
||||||
:marked
|
:marked
|
||||||
|
Now we can inject the #{configuration} object into any constructor that needs it, with
|
||||||
|
the help of an `@Inject` #{decorator} that tells Angular how to find the #{configuration} dependency value.
|
||||||
|
// #enddocregion tokens-opaque-2
|
||||||
|
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-9b-ctor')(format=".")
|
||||||
|
|
||||||
// begin Typescript only
|
// begin Typescript only
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
|
@ -681,32 +822,13 @@ code-example(format, language="html").
|
||||||
:marked
|
:marked
|
||||||
// end typescript only
|
// end typescript only
|
||||||
|
|
||||||
<a id="opaque-token"></a>
|
// Skip for Dart (we have another example)
|
||||||
:marked
|
:marked
|
||||||
### OpaqueToken
|
Or we can provide and inject the configuration object in our top-level `AppComponent`.
|
||||||
Alternatively, we could define and use an [OpaqueToken](/docs/ts/latest/api/core/OpaqueToken-class.html)
|
|
||||||
rather than rely on magic strings that may collide with other developers' string choices.
|
|
||||||
|
|
||||||
The definition looks like this:
|
|
||||||
+makeExample('dependency-injection/ts/app/app.config.ts','token')(format='.')
|
|
||||||
:marked
|
|
||||||
Substitute `APP_CONFIG` for the magic string when registering the provider and defining the constructor parameter:
|
|
||||||
+makeExample('dependency-injection/ts/app/providers.component.ts','providers-9b')(format=".")
|
|
||||||
+makeExample('dependency-injection/ts/app/providers.component.ts','provider-9b-ctor')(format=".")
|
|
||||||
:marked
|
|
||||||
Here's how we provide and inject the configuration object in our top-level `AppComponent`.
|
|
||||||
+makeExample('dependency-injection/ts/app/app.component.ts','providers', 'app/app.component.ts (providers)')(format=".")
|
+makeExample('dependency-injection/ts/app/app.component.ts','providers', 'app/app.component.ts (providers)')(format=".")
|
||||||
+makeExample('dependency-injection/ts/app/app.component.ts','ctor', 'app/app.component.ts (constructor)')(format=".")
|
+makeExample('dependency-injection/ts/app/app.component.ts','ctor', 'app/app.component.ts (constructor)')(format=".")
|
||||||
|
|
||||||
.l-sub-section
|
// #docregion summary
|
||||||
:marked
|
|
||||||
Angular itself uses `OpaqueToken` objects to register all of its own non-class dependencies. For example,
|
|
||||||
[HTTP_PROVIDERS](/docs/ts/latest/api/http/HTTP_PROVIDERS-let.html)
|
|
||||||
is the `OpaqueToken` associated with an array of providers for persisting data with the Angular `Http` client.
|
|
||||||
|
|
||||||
Internally, the `Provider` turns both the string and the class type into an `OpaqueToken`
|
|
||||||
and keys its *token-provider* map with that.
|
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Summary
|
## Summary
|
||||||
|
@ -719,15 +841,18 @@ code-example(format, language="html").
|
||||||
We can learn more about its advanced features, beginning with its support for
|
We can learn more about its advanced features, beginning with its support for
|
||||||
nested injectors, in the
|
nested injectors, in the
|
||||||
[Hierarchical Dependency Injection](hierarchical-dependency-injection.html) chapter.
|
[Hierarchical Dependency Injection](hierarchical-dependency-injection.html) chapter.
|
||||||
|
// #enddocregion summary
|
||||||
|
|
||||||
|
// #docregion appendix-explicit-injector-1
|
||||||
.l-main-section
|
.l-main-section
|
||||||
<a id="explicit-injector"></a>
|
<a id="explicit-injector"></a>
|
||||||
:marked
|
:marked
|
||||||
### Appendix: Working with injectors directly
|
### Appendix: Working with injectors directly
|
||||||
We rarely work directly with an injector.
|
We rarely work directly with an injector.
|
||||||
Here's an `InjectorComponent` that does.
|
Here's an `InjectorComponent` that does.
|
||||||
|
// #enddocregion appendix-explicit-injector-1
|
||||||
+makeExample('dependency-injection/ts/app/injector.component.ts', 'injector', 'app/injector.component.ts')
|
+makeExample('dependency-injection/ts/app/injector.component.ts', 'injector', 'app/injector.component.ts')
|
||||||
|
// #docregion appendix-explicit-injector-2
|
||||||
:marked
|
:marked
|
||||||
The `Injector` is itself an injectable service.
|
The `Injector` is itself an injectable service.
|
||||||
|
|
||||||
|
@ -755,7 +880,9 @@ code-example(format, language="html").
|
||||||
|
|
||||||
Framework developers may take this approach when they
|
Framework developers may take this approach when they
|
||||||
must acquire services generically and dynamically.
|
must acquire services generically and dynamically.
|
||||||
|
// #enddocregion appendix-explicit-injector-2
|
||||||
|
|
||||||
|
// TypeScript only? Unnecessary for Dart
|
||||||
.l-main-section
|
.l-main-section
|
||||||
<a id="forward-ref"></a>
|
<a id="forward-ref"></a>
|
||||||
:marked
|
:marked
|
||||||
|
|
Loading…
Reference in New Issue