docs(dependency-injection): revised Dart and TS code and prose (#1573)
docs(dependency-injection): revise Dart and TS code and prose
This commit is contained in:
parent
1324085c0c
commit
05864c2584
@ -1,6 +1,3 @@
|
|||||||
// #docplaster
|
|
||||||
// #docregion
|
|
||||||
// #docregion imports
|
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
import 'app_config.dart';
|
import 'app_config.dart';
|
||||||
@ -8,9 +5,8 @@ import 'car/car_component.dart';
|
|||||||
import 'heroes/heroes_component.dart';
|
import 'heroes/heroes_component.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
|
|
||||||
import 'injector_component.dart';
|
import 'injector_component.dart';
|
||||||
|
import 'test_component.dart';
|
||||||
import 'providers_component.dart';
|
import 'providers_component.dart';
|
||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
@ -31,21 +27,21 @@ import 'providers_component.dart';
|
|||||||
CarComponent,
|
CarComponent,
|
||||||
HeroesComponent,
|
HeroesComponent,
|
||||||
InjectorComponent,
|
InjectorComponent,
|
||||||
|
TestComponent,
|
||||||
ProvidersComponent
|
ProvidersComponent
|
||||||
],
|
],
|
||||||
// #docregion providers
|
// #docregion providers
|
||||||
providers: const [
|
providers: const [
|
||||||
Logger,
|
Logger, UserService,
|
||||||
UserService,
|
const Provider(APP_CONFIG, useFactory: heroDiConfigFactory)]
|
||||||
const Provider(AppConfig, useValue: config1)]
|
// #enddocregion providers
|
||||||
// #enddocregion providers
|
|
||||||
)
|
)
|
||||||
class AppComponent {
|
class AppComponent {
|
||||||
final UserService _userService;
|
final UserService _userService;
|
||||||
final String title;
|
final String title;
|
||||||
|
|
||||||
//#docregion ctor
|
// #docregion ctor
|
||||||
AppComponent(AppConfig config, this._userService)
|
AppComponent(@Inject(APP_CONFIG) AppConfig config, this._userService)
|
||||||
: title = config.title;
|
: title = config.title;
|
||||||
// #enddocregion ctor
|
// #enddocregion ctor
|
||||||
|
|
||||||
@ -61,7 +57,6 @@ class AppComponent {
|
|||||||
return _userService.user;
|
return _userService.user;
|
||||||
}
|
}
|
||||||
|
|
||||||
String get userInfo => 'Current user, ${user.name}, is'
|
String get userInfo => 'Current user, ${user.name}, is' +
|
||||||
'${isAuthorized ? "" : " not"} authorized. ';
|
(isAuthorized ? '' : ' not') + ' authorized. ';
|
||||||
}
|
}
|
||||||
// #enddocregion
|
|
||||||
|
@ -16,13 +16,3 @@ import 'heroes/heroes_component_1.dart';
|
|||||||
class AppComponent {
|
class AppComponent {
|
||||||
final String title = 'Dependency Injection';
|
final String title = 'Dependency Injection';
|
||||||
}
|
}
|
||||||
// #enddocregion
|
|
||||||
|
|
||||||
/*
|
|
||||||
//#docregion ctor-di-fail
|
|
||||||
// FAIL! Injectable `config` is not a class!
|
|
||||||
AppComponent(HeroService heroService, Map config) {
|
|
||||||
title = config['title'];
|
|
||||||
}
|
|
||||||
//#enddocregion ctor-di-fail
|
|
||||||
*/
|
|
||||||
|
@ -21,13 +21,16 @@ import 'logger_service.dart';
|
|||||||
],
|
],
|
||||||
providers: const [
|
providers: const [
|
||||||
Logger,
|
Logger,
|
||||||
const Provider(AppConfig, useValue: config1)
|
// #docregion providers
|
||||||
])
|
const Provider(APP_CONFIG, useValue: heroDiConfig)
|
||||||
|
// #enddocregion providers
|
||||||
|
]
|
||||||
|
)
|
||||||
class AppComponent {
|
class AppComponent {
|
||||||
final String title;
|
final String title;
|
||||||
|
|
||||||
// #docregion ctor
|
// #docregion ctor
|
||||||
AppComponent(AppConfig config)
|
AppComponent(@Inject(APP_CONFIG) Map config)
|
||||||
: title = config.title;
|
: title = config['title'];
|
||||||
// #enddocregion
|
// #enddocregion ctor
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,27 @@
|
|||||||
// #docregion
|
|
||||||
// #docregion token
|
// #docregion token
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
//#docregion const-class
|
const APP_CONFIG = const OpaqueToken('app.config');
|
||||||
@Injectable()
|
// #enddocregion token
|
||||||
|
|
||||||
|
// #docregion config
|
||||||
|
const Map heroDiConfig = const <String,String>{
|
||||||
|
'apiEndpoint' : 'api.heroes.com',
|
||||||
|
'title' : 'Dependency Injection'
|
||||||
|
};
|
||||||
|
// #enddocregion config
|
||||||
|
|
||||||
|
// #docregion config-alt
|
||||||
class AppConfig {
|
class AppConfig {
|
||||||
final apiEndpoint;
|
String apiEndpoint;
|
||||||
final String title;
|
String title;
|
||||||
|
|
||||||
const AppConfig(this.apiEndpoint, this.title);
|
|
||||||
}
|
}
|
||||||
//#enddocregion const-class
|
|
||||||
|
|
||||||
//#docregion const-object
|
AppConfig heroDiConfigFactory() => new AppConfig()
|
||||||
const config1 = const AppConfig('api.heroes.com', 'Dependency Injection');
|
..apiEndpoint = 'api.heroes.com'
|
||||||
//#enddocregion const-object
|
..title = 'Dependency Injection';
|
||||||
|
// #enddocregion config-alt
|
||||||
|
|
||||||
|
const appConfigProvider = const Provider(APP_CONFIG,
|
||||||
|
useFactory: heroDiConfigFactory,
|
||||||
|
deps: const []);
|
||||||
|
@ -1,21 +1,19 @@
|
|||||||
// #docregion
|
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
// #docregion engine
|
|
||||||
class Engine {
|
class Engine {
|
||||||
final int cylinders = 4;
|
final int cylinders;
|
||||||
|
|
||||||
|
Engine() : cylinders = 4;
|
||||||
|
Engine.withCylinders(this.cylinders);
|
||||||
}
|
}
|
||||||
// #enddocregion engine
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
// #docregion tires
|
|
||||||
class Tires {
|
class Tires {
|
||||||
String make = 'Flintstone';
|
String make = 'Flintstone';
|
||||||
String model = 'Square';
|
String model = 'Square';
|
||||||
}
|
}
|
||||||
|
|
||||||
// #enddocregion tires
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class Car {
|
class Car {
|
||||||
//#docregion car-ctor
|
//#docregion car-ctor
|
||||||
@ -24,11 +22,10 @@ class Car {
|
|||||||
String description = 'DI';
|
String description = 'DI';
|
||||||
|
|
||||||
Car(this.engine, this.tires);
|
Car(this.engine, this.tires);
|
||||||
|
|
||||||
// #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'
|
String drive() => '$description car with '
|
||||||
' and ${tires.make} tires.';
|
'${engine.cylinders} cylinders and '
|
||||||
|
'${tires.make} tires.';
|
||||||
}
|
}
|
||||||
// #enddocregion car
|
|
||||||
|
@ -3,50 +3,39 @@
|
|||||||
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());
|
new Car(new Engine(), new Tires())
|
||||||
//#enddocregion car-ctor-instantiation
|
// #enddocregion car-ctor-instantiation
|
||||||
car.description = 'Simple';
|
..description = 'Simple';
|
||||||
return car;
|
|
||||||
}
|
|
||||||
///////// example 2 ////////////
|
///////// example 2 ////////////
|
||||||
|
|
||||||
//#docregion car-ctor-instantiation-with-param
|
// #docregion car-ctor-instantiation-with-param
|
||||||
class Engine2 implements Engine {
|
class Engine2 extends Engine {
|
||||||
final int cylinders;
|
Engine2(cylinders) : super.withCylinders(cylinders);
|
||||||
|
|
||||||
Engine2(this.cylinders);
|
|
||||||
}
|
}
|
||||||
//#enddocregion car-ctor-instantiation-with-param
|
|
||||||
|
|
||||||
Car superCar() {
|
Car superCar() =>
|
||||||
//#docregion car-ctor-instantiation-with-param
|
// Super car with 12 cylinders and Flintstone tires.
|
||||||
// Super car with 12 cylinders and Flintstone tires.
|
new Car(new Engine2(12), new Tires())
|
||||||
var bigCylinders = 12;
|
..description = 'Super';
|
||||||
var car = new Car(new Engine2(bigCylinders), new Tires());
|
// #enddocregion car-ctor-instantiation-with-param
|
||||||
//#enddocregion car-ctor-instantiation-with-param
|
|
||||||
car.description = 'Super';
|
|
||||||
return car;
|
|
||||||
}
|
|
||||||
/////////// example 3 //////////
|
/////////// example 3 //////////
|
||||||
|
|
||||||
//#docregion car-ctor-instantiation-with-mocks
|
// #docregion car-ctor-instantiation-with-mocks
|
||||||
class MockEngine extends Engine {
|
class MockEngine extends Engine {
|
||||||
final int cylinders = 8;
|
MockEngine() : super.withCylinders(8);
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockTires extends Tires {
|
class MockTires extends Tires {
|
||||||
String make = 'YokoGoodStone';
|
MockTires() { make = 'YokoGoodStone'; }
|
||||||
}
|
}
|
||||||
|
|
||||||
//#enddocregion car-ctor-instantiation-with-mocks
|
Car testCar() =>
|
||||||
Car testCar() {
|
// Test car with 8 cylinders and YokoGoodStone tires.
|
||||||
//#docregion car-ctor-instantiation-with-mocks
|
new Car(new MockEngine(), new MockTires())
|
||||||
// Test car with 8 cylinders and YokoGoodStone tires.
|
..description = 'Test';
|
||||||
var car = new Car(new MockEngine(), new MockTires());
|
// #enddocregion car-ctor-instantiation-with-mocks
|
||||||
//#enddocregion car-ctor-instantiation-with-mocks
|
|
||||||
car.description = 'Test';
|
|
||||||
return car;
|
|
||||||
}
|
|
||||||
|
@ -3,10 +3,9 @@ import 'car.dart';
|
|||||||
|
|
||||||
// BAD pattern!
|
// BAD pattern!
|
||||||
class CarFactory {
|
class CarFactory {
|
||||||
Car createCar() {
|
Car createCar() =>
|
||||||
return new Car(createEngine(), createTires())
|
new Car(createEngine(), createTires())
|
||||||
..description = 'Factory';
|
..description = 'Factory';
|
||||||
}
|
|
||||||
|
|
||||||
Engine createEngine() => new Engine();
|
Engine createEngine() => new Engine();
|
||||||
Tires createTires() => new Tires();
|
Tires createTires() => new Tires();
|
||||||
|
@ -1,36 +1,27 @@
|
|||||||
// #docplaster
|
|
||||||
//#docregion
|
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
import '../logger_service.dart';
|
import '../logger_service.dart';
|
||||||
import 'car.dart';
|
import 'car.dart';
|
||||||
|
|
||||||
//#docregion injector
|
// #docregion injector
|
||||||
Car useInjector() {
|
Car useInjector() {
|
||||||
ReflectiveInjector injector;
|
ReflectiveInjector injector;
|
||||||
//#enddocregion injector
|
// #enddocregion injector
|
||||||
|
|
||||||
/*
|
/*
|
||||||
//#docregion injector-no-new
|
// #docregion injector-no-new
|
||||||
// Cannot 'new' an ReflectiveInjector like this!
|
// Cannot instantiate an ReflectiveInjector like this!
|
||||||
var injector = new ReflectiveInjector([Car, Engine, Tires, Logger]);
|
var injector = new ReflectiveInjector([Car, Engine, Tires]);
|
||||||
//#enddocregion injector-no-new
|
// #enddocregion injector-no-new
|
||||||
*/
|
*/
|
||||||
|
// #docregion injector, injector-create-and-call
|
||||||
//#docregion injector
|
injector = ReflectiveInjector.resolveAndCreate([Car, Engine, Tires]);
|
||||||
|
// #docregion injector-call
|
||||||
//#docregion injector-create-and-call
|
|
||||||
injector = ReflectiveInjector.resolveAndCreate([Car, Engine, Tires, Logger]);
|
|
||||||
//#docregion injector-call
|
|
||||||
var car = injector.get(Car);
|
var car = injector.get(Car);
|
||||||
//#enddocregion injector-call
|
// #enddocregion injector-call, injector-create-and-call
|
||||||
//#enddocregion injector-create-and-call
|
|
||||||
|
|
||||||
car.description = 'Injector';
|
car.description = 'Injector';
|
||||||
|
|
||||||
|
injector = ReflectiveInjector.resolveAndCreate([Logger]);
|
||||||
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());
|
||||||
return car;
|
return car;
|
||||||
}
|
}
|
||||||
//#enddocregion injector
|
|
||||||
|
|
||||||
//#enddocregion
|
|
||||||
|
@ -17,6 +17,7 @@ class Car {
|
|||||||
|
|
||||||
// Method using the engine and tires
|
// Method using the engine and tires
|
||||||
String drive() => '$description car with '
|
String drive() => '$description car with '
|
||||||
'${engine.cylinders} cylinders and ${tires.make} tires.';
|
'${engine.cylinders} cylinders and '
|
||||||
|
'${tires.make} tires.';
|
||||||
}
|
}
|
||||||
//#enddocregion car
|
//#enddocregion car
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
class Hero {
|
class Hero {
|
||||||
num id;
|
final int id;
|
||||||
String name;
|
final String name;
|
||||||
bool isSecret = false;
|
final bool isSecret;
|
||||||
|
|
||||||
|
Hero(this.id, this.name, [this.isSecret = false]);
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,8 @@ import 'hero_service.dart';
|
|||||||
class HeroListComponent {
|
class HeroListComponent {
|
||||||
final List<Hero> heroes;
|
final List<Hero> heroes;
|
||||||
|
|
||||||
//#docregion ctor-signature
|
// #docregion ctor-signature
|
||||||
HeroListComponent(HeroService heroService) : heroes = heroService.getHeroes();
|
HeroListComponent(HeroService heroService)
|
||||||
//#enddocregion ctor-signature
|
// #enddocregion ctor-signature
|
||||||
|
: heroes = heroService.getHeroes();
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,16 @@
|
|||||||
|
// #docplaster
|
||||||
// #docregion
|
// #docregion
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
import 'hero.dart';
|
import 'hero.dart';
|
||||||
|
// #enddocregion
|
||||||
|
import 'hero_service_1.dart';
|
||||||
|
/*
|
||||||
|
// #docregion
|
||||||
import 'hero_service.dart';
|
import 'hero_service.dart';
|
||||||
|
// #enddocregion
|
||||||
|
*/
|
||||||
|
// #docregion
|
||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'hero-list',
|
selector: 'hero-list',
|
||||||
@ -13,8 +21,8 @@ import 'hero_service.dart';
|
|||||||
class HeroListComponent {
|
class HeroListComponent {
|
||||||
final List<Hero> heroes;
|
final List<Hero> heroes;
|
||||||
|
|
||||||
//#docregion ctor
|
// #docregion ctor
|
||||||
HeroListComponent(HeroService heroService)
|
HeroListComponent(HeroService heroService)
|
||||||
: heroes = heroService.getHeroes();
|
: heroes = heroService.getHeroes();
|
||||||
//#enddocregion ctor
|
// #enddocregion ctor
|
||||||
}
|
}
|
||||||
|
@ -20,5 +20,5 @@ class HeroService {
|
|||||||
.where((hero) => _isAuthorized || !hero.isSecret)
|
.where((hero) => _isAuthorized || !hero.isSecret)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
// #enddocregion internals
|
// #enddocregion internals
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
import 'hero.dart';
|
import 'hero.dart';
|
||||||
import 'mock_heroes.dart';
|
import 'mock_heroes.dart';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
class HeroService {
|
class HeroService {
|
||||||
List<Hero> getHeroes() => HEROES;
|
List<Hero> getHeroes() => HEROES;
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ class HeroService {
|
|||||||
|
|
||||||
//#docregion ctor
|
//#docregion ctor
|
||||||
HeroService(this._logger);
|
HeroService(this._logger);
|
||||||
|
|
||||||
//#enddocregion ctor
|
//#enddocregion ctor
|
||||||
List<Hero> getHeroes() {
|
List<Hero> getHeroes() {
|
||||||
_logger.log('Getting heroes ...');
|
_logger.log('Getting heroes ...');
|
||||||
|
@ -6,8 +6,7 @@ import '../user_service.dart';
|
|||||||
import 'hero_service.dart';
|
import 'hero_service.dart';
|
||||||
|
|
||||||
// #docregion factory
|
// #docregion factory
|
||||||
@Injectable()
|
HeroService 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
|
||||||
|
|
||||||
|
@ -1,23 +1,27 @@
|
|||||||
// #docplaster
|
// #docplaster
|
||||||
// #docregion
|
// #docregion
|
||||||
// #docregion v1
|
// #docregion full, v1
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
|
// #enddocregion full, v1
|
||||||
|
import 'hero_list_component_2.dart';
|
||||||
|
import 'hero_service_1.dart';
|
||||||
|
/*
|
||||||
|
// #docregion full
|
||||||
import 'hero_list_component.dart';
|
import 'hero_list_component.dart';
|
||||||
// #enddocregion v1
|
|
||||||
import 'hero_service.dart';
|
|
||||||
// #docregion v1
|
// #docregion v1
|
||||||
|
import 'hero_service.dart';
|
||||||
|
// #enddocregion full, v1
|
||||||
|
*/
|
||||||
|
// #docregion full, 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
|
|
||||||
providers: const [HeroService],
|
providers: const [HeroService],
|
||||||
// #enddocregion providers
|
// #docregion v1
|
||||||
// #docregion v1
|
|
||||||
directives: const [HeroListComponent])
|
directives: const [HeroListComponent])
|
||||||
class HeroesComponent {}
|
class HeroesComponent {}
|
||||||
// #enddocregion v1
|
|
||||||
|
@ -1,45 +1,18 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import 'hero.dart';
|
import 'hero.dart';
|
||||||
|
|
||||||
List<Hero> HEROES = [
|
List<Hero> HEROES = <Map>[
|
||||||
new Hero()
|
{'id': 11, 'isSecret': false, 'name': 'Mr. Nice'},
|
||||||
..id = 11
|
{'id': 12, 'isSecret': false, 'name': 'Narco'},
|
||||||
..isSecret = false
|
{'id': 13, 'isSecret': false, 'name': 'Bombasto'},
|
||||||
..name = 'Mr. Nice',
|
{'id': 14, 'isSecret': false, 'name': 'Celeritas'},
|
||||||
new Hero()
|
{'id': 15, 'isSecret': false, 'name': 'Magneta'},
|
||||||
..id = 12
|
{'id': 16, 'isSecret': false, 'name': 'RubberMan'},
|
||||||
..isSecret = false
|
{'id': 17, 'isSecret': false, 'name': 'Dynama'},
|
||||||
..name = 'Narco',
|
{'id': 18, 'isSecret': true, 'name': 'Dr IQ'},
|
||||||
new Hero()
|
{'id': 19, 'isSecret': true, 'name': 'Magma'},
|
||||||
..id = 13
|
{'id': 20, 'isSecret': true, 'name': 'Tornado'}
|
||||||
..isSecret = false
|
].map(_initHero).toList();
|
||||||
..name = 'Bombasto',
|
|
||||||
new Hero()
|
Hero _initHero(Map heroProperties) => new Hero(
|
||||||
..id = 14
|
heroProperties['id'], heroProperties['name'], heroProperties['isSecret']);
|
||||||
..isSecret = false
|
|
||||||
..name = 'Celeritas',
|
|
||||||
new Hero()
|
|
||||||
..id = 15
|
|
||||||
..isSecret = false
|
|
||||||
..name = 'Magneta',
|
|
||||||
new Hero()
|
|
||||||
..id = 16
|
|
||||||
..isSecret = false
|
|
||||||
..name = 'RubberMan',
|
|
||||||
new Hero()
|
|
||||||
..id = 17
|
|
||||||
..isSecret = false
|
|
||||||
..name = 'Dynama',
|
|
||||||
new Hero()
|
|
||||||
..id = 18
|
|
||||||
..isSecret = true
|
|
||||||
..name = 'Dr IQ',
|
|
||||||
new Hero()
|
|
||||||
..id = 19
|
|
||||||
..isSecret = true
|
|
||||||
..name = 'Magma',
|
|
||||||
new Hero()
|
|
||||||
..id = 20
|
|
||||||
..isSecret = true
|
|
||||||
..name = 'Tornado'
|
|
||||||
];
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// #docplaster
|
// #docplaster
|
||||||
|
|
||||||
//#docregion
|
// #docregion
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
import 'car/car.dart';
|
import 'car/car.dart';
|
||||||
@ -9,7 +9,7 @@ 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';
|
||||||
|
|
||||||
//#docregion injector
|
// #docregion injector
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'my-injectors',
|
selector: 'my-injectors',
|
||||||
template: '''
|
template: '''
|
||||||
@ -18,39 +18,26 @@ import 'logger_service.dart';
|
|||||||
<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, Tires, heroServiceProvider, Logger])
|
||||||
Engine,
|
|
||||||
Tires,
|
|
||||||
const Provider(HeroService, useFactory: heroServiceFactory),
|
|
||||||
Logger
|
|
||||||
])
|
|
||||||
class InjectorComponent {
|
class InjectorComponent {
|
||||||
final Injector _injector;
|
final Injector _injector;
|
||||||
Car car;
|
Car car;
|
||||||
HeroService heroService;
|
HeroService heroService;
|
||||||
Hero hero;
|
Hero hero;
|
||||||
|
|
||||||
String get rodent {
|
|
||||||
try {
|
|
||||||
_injector.get(ROUS);
|
|
||||||
} on NoProviderError {
|
|
||||||
return "R.O.U.S.'s? I don't think they exist!";
|
|
||||||
}
|
|
||||||
throw new Exception('Aaaargh!');
|
|
||||||
}
|
|
||||||
|
|
||||||
InjectorComponent(this._injector) {
|
InjectorComponent(this._injector) {
|
||||||
car = _injector.get(Car);
|
car = _injector.get(Car);
|
||||||
//#docregion get-hero-service
|
// #docregion get-hero-service
|
||||||
heroService = _injector.get(HeroService);
|
heroService = _injector.get(HeroService);
|
||||||
//#enddocregion get-hero-service
|
// #enddocregion get-hero-service
|
||||||
hero = heroService.getHeroes()[0];
|
hero = heroService.getHeroes()[0];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
//#enddocregion injector
|
|
||||||
|
|
||||||
/**
|
String get rodent =>
|
||||||
* R.O.U.S. - Rodents Of Unusual Size
|
_injector.get(ROUS, "R.O.U.S.'s? I don't think they exist!");
|
||||||
* // https://www.youtube.com/watch?v=BOv5ZjAOpC8
|
}
|
||||||
*/
|
// #enddocregion injector
|
||||||
|
|
||||||
|
/// R.O.U.S. - Rodents Of Unusual Size
|
||||||
|
/// https://www.youtube.com/watch?v=BOv5ZjAOpC8
|
||||||
class ROUS {}
|
class ROUS {}
|
||||||
|
@ -3,10 +3,11 @@ import 'package:angular2/core.dart';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class Logger {
|
class Logger {
|
||||||
List<String> logs = [];
|
List<String> _logs = [];
|
||||||
|
List<String> get logs => _logs;
|
||||||
|
|
||||||
void log(String message) {
|
void log(String message) {
|
||||||
logs.add(message);
|
_logs.add(message);
|
||||||
print(message);
|
print(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
// Examples of provider arrays
|
// Examples of provider arrays
|
||||||
|
// #docplaster
|
||||||
//#docplaster
|
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
import 'app_config.dart';
|
import 'app_config.dart';
|
||||||
@ -9,20 +8,22 @@ import 'heroes/hero_service.dart';
|
|||||||
import 'logger_service.dart';
|
import 'logger_service.dart';
|
||||||
import 'user_service.dart';
|
import 'user_service.dart';
|
||||||
|
|
||||||
|
// TODO file an issue: cannot use the following const in metadata.
|
||||||
|
const template = '{{log}}';
|
||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'provider-1',
|
selector: 'provider-1',
|
||||||
template: '{{log}}',
|
template: '{{log}}',
|
||||||
providers:
|
// #docregion providers-1, providers-logger
|
||||||
//#docregion providers-1
|
providers: const [Logger]
|
||||||
const [Logger]
|
// #enddocregion providers-1, providers-logger
|
||||||
//#enddocregion providers-1
|
|
||||||
)
|
)
|
||||||
class ProviderComponent1 {
|
class ProviderComponent1 {
|
||||||
String log;
|
String log;
|
||||||
|
|
||||||
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.last;
|
log = logger.logs[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,29 +31,46 @@ class ProviderComponent1 {
|
|||||||
selector: 'provider-2',
|
selector: 'provider-2',
|
||||||
template: '{{log}}',
|
template: '{{log}}',
|
||||||
providers:
|
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 {
|
||||||
String log;
|
String log;
|
||||||
|
|
||||||
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.last;
|
log = logger.logs[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Component just used to ensure that shared E2E tests pass.
|
||||||
@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)]
|
||||||
|
)
|
||||||
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.last;
|
log = logger.logs[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Component just used to ensure that shared E2E tests pass.
|
||||||
|
@Component(
|
||||||
|
selector: 'provider-3a',
|
||||||
|
template: '{{log}}',
|
||||||
|
providers: const [const Provider(Logger, useClass: Logger)]
|
||||||
|
)
|
||||||
|
class ProviderComponent3a {
|
||||||
|
String log;
|
||||||
|
|
||||||
|
ProviderComponent3a(Logger logger) {
|
||||||
|
logger.log('Hello from logger provided with {provide: Logger, useClass: Logger}');
|
||||||
|
log = logger.logs[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,58 +81,55 @@ class BetterLogger extends Logger {}
|
|||||||
selector: 'provider-4',
|
selector: 'provider-4',
|
||||||
template: '{{log}}',
|
template: '{{log}}',
|
||||||
providers:
|
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 {
|
||||||
String log;
|
String log;
|
||||||
|
|
||||||
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.last;
|
log = logger.logs[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #docregion EvenBetterLogger
|
// #docregion EvenBetterLogger
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class EvenBetterLogger implements Logger {
|
class EvenBetterLogger extends Logger {
|
||||||
final UserService _userService;
|
final UserService _userService;
|
||||||
@override List<String> logs = [];
|
|
||||||
|
|
||||||
EvenBetterLogger(this._userService);
|
EvenBetterLogger(this._userService);
|
||||||
|
|
||||||
@override void log(String message) {
|
@override void log(String message) {
|
||||||
var msg = 'Message to ${_userService.user.name}: $message.';
|
var name = _userService.user.name;
|
||||||
print(msg);
|
super.log('Message to $name: $message');
|
||||||
logs.add(msg);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #enddocregion EvenBetterLogger
|
// #enddocregion EvenBetterLogger
|
||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'provider-5',
|
selector: 'provider-5',
|
||||||
template: '{{log}}',
|
template: '{{log}}',
|
||||||
providers:
|
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 {
|
||||||
String log;
|
String log;
|
||||||
|
|
||||||
ProviderComponent5(Logger logger) {
|
ProviderComponent5(Logger logger) {
|
||||||
logger.log('Hello from EvenBetterlogger');
|
logger.log('Hello from EvenBetterlogger');
|
||||||
log = logger.logs.last;
|
log = logger.logs[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class NewLogger extends Logger implements OldLogger {}
|
class NewLogger extends Logger implements OldLogger {}
|
||||||
|
|
||||||
class OldLogger {
|
class OldLogger extends Logger {
|
||||||
List<String> logs = [];
|
@override
|
||||||
|
|
||||||
void 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!');
|
||||||
}
|
}
|
||||||
@ -124,26 +139,23 @@ class OldLogger {
|
|||||||
selector: 'provider-6a',
|
selector: 'provider-6a',
|
||||||
template: '{{log}}',
|
template: '{{log}}',
|
||||||
providers:
|
providers:
|
||||||
//#docregion providers-6a
|
// #docregion providers-6a
|
||||||
const [NewLogger,
|
const [NewLogger,
|
||||||
// Not aliased! Creates two instances of `NewLogger`
|
// Not aliased! Creates two instances of `NewLogger`
|
||||||
const Provider(OldLogger, useClass: NewLogger)]
|
const Provider(OldLogger, useClass: NewLogger)]
|
||||||
//#enddocregion providers-6a
|
// #enddocregion providers-6a
|
||||||
)
|
)
|
||||||
class ProviderComponent6a {
|
class ProviderComponent6a {
|
||||||
String log;
|
String log;
|
||||||
|
|
||||||
ProviderComponent6a(NewLogger newLogger, OldLogger oldLogger) {
|
ProviderComponent6a(NewLogger newLogger, OldLogger oldLogger) {
|
||||||
if (identical(newLogger, oldLogger)) {
|
if (newLogger == oldLogger) {
|
||||||
throw new Exception('expected the two loggers to be different instances');
|
throw new Exception('expected the two loggers to be different instances');
|
||||||
}
|
}
|
||||||
oldLogger.log('Hello OldLogger (but we want NewLogger)');
|
oldLogger.log('Hello OldLogger (but we want NewLogger)');
|
||||||
// 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
|
log = newLogger.logs.isEmpty ? oldLogger.logs[0] : newLogger.logs[0];
|
||||||
? oldLogger.logs[0]
|
|
||||||
: newLogger.logs[0];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,17 +163,17 @@ class ProviderComponent6a {
|
|||||||
selector: 'provider-6b',
|
selector: 'provider-6b',
|
||||||
template: '{{log}}',
|
template: '{{log}}',
|
||||||
providers:
|
providers:
|
||||||
//#docregion providers-6b
|
// #docregion providers-6b
|
||||||
const [NewLogger,
|
const [NewLogger,
|
||||||
// Alias OldLogger with reference to NewLogger
|
// Alias OldLogger with reference to NewLogger
|
||||||
const Provider(OldLogger, useExisting: NewLogger)]
|
const Provider(OldLogger, useExisting: NewLogger)]
|
||||||
//#enddocregion providers-6b
|
// #enddocregion providers-6b
|
||||||
)
|
)
|
||||||
class ProviderComponent6b {
|
class ProviderComponent6b {
|
||||||
String log;
|
String log;
|
||||||
|
|
||||||
ProviderComponent6b(NewLogger newLogger, OldLogger oldLogger) {
|
ProviderComponent6b(NewLogger newLogger, OldLogger oldLogger) {
|
||||||
if (!identical(newLogger, oldLogger)) {
|
if (newLogger != oldLogger) {
|
||||||
throw new Exception('expected the two loggers to be the same instance');
|
throw new Exception('expected the two loggers to be the same instance');
|
||||||
}
|
}
|
||||||
oldLogger.log('Hello from NewLogger (via aliased OldLogger)');
|
oldLogger.log('Hello from NewLogger (via aliased OldLogger)');
|
||||||
@ -169,140 +181,102 @@ class ProviderComponent6b {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #docregion opaque-token
|
// #docregion silent-logger
|
||||||
const loggerPrefix = const OpaqueToken('Logger prefix');
|
// #docregion const-class
|
||||||
// #enddocregion opaque-token
|
class SilentLogger implements Logger {
|
||||||
|
@override
|
||||||
|
final List<String> logs = const ['Silent logger says "Shhhhh!". Provided via "useValue"'];
|
||||||
|
|
||||||
// #docregion configurable-logger
|
const SilentLogger();
|
||||||
@Injectable()
|
|
||||||
class ConfigurableLogger extends Logger {
|
|
||||||
final String _prefix;
|
|
||||||
|
|
||||||
// #docregion use-opaque-token
|
|
||||||
ConfigurableLogger(@Inject(loggerPrefix) this._prefix);
|
|
||||||
// #enddocregion use-opaque-token
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void log(String msg) {
|
void log(String message) { }
|
||||||
super.log('$_prefix: $msg');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// #enddocregion configurable-logger
|
// #enddocregion const-class
|
||||||
|
// #docregion const-object
|
||||||
|
|
||||||
@Component(selector: 'provider-7', template: '{{message}}',
|
const silentLogger = const SilentLogger();
|
||||||
//#docregion providers-7
|
// #enddocregion const-object
|
||||||
providers: const [
|
// #enddocregion silent-logger
|
||||||
const Provider(Logger, useClass: ConfigurableLogger),
|
|
||||||
//#docregion providers-usevalue
|
@Component(
|
||||||
const Provider(loggerPrefix, useValue: 'Testing')
|
selector: 'provider-7',
|
||||||
//#enddocregion providers-usevalue
|
template: '{{log}}',
|
||||||
]
|
providers:
|
||||||
//#enddocregion providers-7
|
// #docregion providers-7
|
||||||
|
const [const Provider(Logger, useValue: silentLogger)]
|
||||||
|
// #enddocregion providers-7
|
||||||
)
|
)
|
||||||
class ProviderComponent7 {
|
class ProviderComponent7 {
|
||||||
String message;
|
String log;
|
||||||
|
|
||||||
ProviderComponent7(Logger logger) {
|
ProviderComponent7(Logger logger) {
|
||||||
logger.log('Hello from configurable logger.');
|
logger.log('Hello from logger provided with useValue');
|
||||||
message = logger.logs.last;
|
log = logger.logs[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component(selector: 'provider-8', template: '{{log}}', providers: const [
|
|
||||||
const Provider(HeroService, useFactory: heroServiceFactory),
|
|
||||||
Logger,
|
|
||||||
UserService
|
|
||||||
])
|
|
||||||
class ProviderComponent8 {
|
|
||||||
// #docregion provider-8-ctor
|
|
||||||
ProviderComponent8(HeroService heroService);
|
|
||||||
|
|
||||||
// #enddocregion provider-8-ctor
|
|
||||||
|
|
||||||
// must be true else this component would have blown up at runtime
|
|
||||||
var log = 'Hero service injected successfully';
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'provider-9',
|
selector: 'provider-8',
|
||||||
template: '{{log}}',
|
template: '{{log}}',
|
||||||
providers:
|
providers: const [heroServiceProvider, Logger, UserService])
|
||||||
// #docregion providers-9
|
class ProviderComponent8 {
|
||||||
const [const Provider(AppConfig, useValue: config1)]
|
// #docregion provider-8-ctor
|
||||||
// #enddocregion providers-9
|
ProviderComponent8(HeroService heroService);
|
||||||
|
// #enddocregion provider-8-ctor
|
||||||
|
|
||||||
|
// must be true else this component would have blown up at runtime
|
||||||
|
var log = 'Hero service injected successfully via heroServiceProvider';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component(
|
||||||
|
selector: 'provider-9',
|
||||||
|
template: '{{log}}',
|
||||||
|
// #docregion providers-9
|
||||||
|
providers: const [
|
||||||
|
const Provider(APP_CONFIG, useValue: heroDiConfig)]
|
||||||
|
// #enddocregion providers-9
|
||||||
)
|
)
|
||||||
class ProviderComponent9 implements OnInit {
|
class ProviderComponent9 implements OnInit {
|
||||||
AppConfig _config;
|
Map _config;
|
||||||
String log;
|
String log;
|
||||||
|
|
||||||
ProviderComponent9(AppConfig this._config);
|
// #docregion provider-9-ctor
|
||||||
|
ProviderComponent9(@Inject(APP_CONFIG) this._config);
|
||||||
|
// #enddocregion provider-9-ctor
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void ngOnInit() {
|
void ngOnInit() {
|
||||||
log = 'appConfigToken Application title is ${_config.title}';
|
log = 'APP_CONFIG Application title is ${_config['title']}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal required logger
|
// Sample providers 1 to 7 illustrate a required logger dependency.
|
||||||
@Component(selector: 'provider-10a', template: '{{log}}',
|
// Optional logger, can be null.
|
||||||
//#docregion providers-logger
|
@Component(selector: 'provider-10', template: '{{log}}')
|
||||||
providers: const [Logger]
|
class ProviderComponent10 implements OnInit {
|
||||||
//#enddocregion providers-logger
|
|
||||||
)
|
|
||||||
class ProviderComponent10a {
|
|
||||||
String log;
|
|
||||||
|
|
||||||
ProviderComponent10a(Logger logger) {
|
|
||||||
logger.log('Hello from the required logger.');
|
|
||||||
log = logger.logs.last;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optional logger, can be null
|
|
||||||
@Component(selector: 'provider-10b', template: '{{log}}')
|
|
||||||
class ProviderComponent10b {
|
|
||||||
// #docregion provider-10-ctor
|
|
||||||
final Logger _logger;
|
final Logger _logger;
|
||||||
String log;
|
String log;
|
||||||
|
|
||||||
ProviderComponent10b(@Optional() Logger this._logger) {
|
/*
|
||||||
// . . .
|
|
||||||
// #enddocregion provider-10-ctor
|
|
||||||
_logger == null ? log = 'No logger exists' : log = _logger.logs.last;
|
|
||||||
// #docregion provider-10-ctor
|
// #docregion provider-10-ctor
|
||||||
|
HeroService(@Optional() this._logger) {
|
||||||
|
// #enddocregion provider-10-ctor
|
||||||
|
*/
|
||||||
|
ProviderComponent10(@Optional() this._logger) {
|
||||||
|
const someMessage = 'Hello from the injected logger';
|
||||||
|
// #docregion provider-10-ctor
|
||||||
|
if (_logger != null)
|
||||||
|
_logger.log(someMessage);
|
||||||
}
|
}
|
||||||
// #enddocregion provider-10-ctor
|
// #enddocregion provider-10-ctor
|
||||||
}
|
|
||||||
|
|
||||||
// Optional logger, non null
|
@override
|
||||||
@Component(selector: 'provider-10c', template: '{{log}}')
|
void ngOnInit() {
|
||||||
class ProviderComponent10c {
|
log = _logger == null ? 'Optional logger was not available' : _logger.logs[0];
|
||||||
// #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: '''
|
||||||
@ -310,20 +284,20 @@ class DoNothingLogger extends Logger {
|
|||||||
<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="p3a"><provider-3a></provider-3a></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="p8"><provider-9></provider-9></div>
|
<div id="p9"><provider-9></provider-9></div>
|
||||||
<div id="p10a"><provider-10a></provider-10a></div>
|
<div id="p10"><provider-10></provider-10></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,
|
||||||
ProviderComponent3,
|
ProviderComponent3,
|
||||||
|
ProviderComponent3a,
|
||||||
ProviderComponent4,
|
ProviderComponent4,
|
||||||
ProviderComponent5,
|
ProviderComponent5,
|
||||||
ProviderComponent6a,
|
ProviderComponent6a,
|
||||||
@ -331,8 +305,6 @@ class DoNothingLogger extends Logger {
|
|||||||
ProviderComponent7,
|
ProviderComponent7,
|
||||||
ProviderComponent8,
|
ProviderComponent8,
|
||||||
ProviderComponent9,
|
ProviderComponent9,
|
||||||
ProviderComponent10a,
|
ProviderComponent10
|
||||||
ProviderComponent10b,
|
|
||||||
ProviderComponent10c
|
|
||||||
])
|
])
|
||||||
class ProvidersComponent {}
|
class ProvidersComponent {}
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
// Simulate a simple test
|
||||||
|
// Reader should look to the testing chapter for the real thing
|
||||||
|
|
||||||
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
|
import 'heroes/hero.dart';
|
||||||
|
import 'heroes/hero_list_component.dart';
|
||||||
|
import 'heroes/hero_service.dart';
|
||||||
|
|
||||||
|
@Component(
|
||||||
|
selector: 'my-tests',
|
||||||
|
template: '''
|
||||||
|
<h2>Tests</h2>
|
||||||
|
<p id="tests">Tests {{results['pass']}}: {{results['message']}}</p>
|
||||||
|
''')
|
||||||
|
class TestComponent {
|
||||||
|
var results = runTests();
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockHeroService implements HeroService {
|
||||||
|
final List<Hero> _heroes;
|
||||||
|
MockHeroService(this._heroes);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Hero> getHeroes() => _heroes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////
|
||||||
|
dynamic runTests() {
|
||||||
|
//#docregion spec
|
||||||
|
var expectedHeroes = [new Hero(0, 'A'), new Hero(1, 'B')];
|
||||||
|
var mockService = new MockHeroService(expectedHeroes);
|
||||||
|
it('should have heroes when HeroListComponent created', () {
|
||||||
|
var hlc = new HeroListComponent(mockService);
|
||||||
|
expect(hlc.heroes.length).toEqual(expectedHeroes.length);
|
||||||
|
});
|
||||||
|
//#enddocregion spec
|
||||||
|
return testResults;
|
||||||
|
}
|
||||||
|
//////////////////////////////////
|
||||||
|
|
||||||
|
// Fake Jasmine infrastructure
|
||||||
|
String testName;
|
||||||
|
dynamic testResults;
|
||||||
|
dynamic expect(dynamic actual) => new ExpectResult(actual);
|
||||||
|
|
||||||
|
class ExpectResult {
|
||||||
|
final actual;
|
||||||
|
ExpectResult(this.actual);
|
||||||
|
|
||||||
|
void toEqual(dynamic expected) {
|
||||||
|
testResults = actual == expected
|
||||||
|
? {'pass': 'passed', 'message': testName}
|
||||||
|
: {
|
||||||
|
'pass': 'failed',
|
||||||
|
'message': '$testName; expected $actual to equal $expected.'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void it(String label, void test()) {
|
||||||
|
testName = label;
|
||||||
|
test();
|
||||||
|
}
|
||||||
|
|
@ -1,26 +1,23 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import 'package:angular2/core.dart';
|
import 'package:angular2/core.dart';
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
class UserService {
|
|
||||||
UserService() {
|
|
||||||
user = _bob;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Todo: get the user; don't 'new' it.
|
|
||||||
User _alice = new User('Alice', true);
|
|
||||||
User _bob = new User('Bob', false);
|
|
||||||
|
|
||||||
// initial user is Bob
|
|
||||||
User user;
|
|
||||||
|
|
||||||
// swaps users
|
|
||||||
User getNewUser() => user = user == _bob ? _alice : _bob;
|
|
||||||
}
|
|
||||||
|
|
||||||
class User {
|
class User {
|
||||||
String name;
|
final String name;
|
||||||
bool isAuthorized;
|
final bool isAuthorized;
|
||||||
|
|
||||||
User(this.name, [this.isAuthorized = false]);
|
User(this.name, [this.isAuthorized = false]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Todo: get the user; don't 'new' it.
|
||||||
|
final User _alice = new User('Alice', true);
|
||||||
|
final User _bob = new User('Bob', false);
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
class UserService {
|
||||||
|
User user;
|
||||||
|
|
||||||
|
UserService() : user = _bob;
|
||||||
|
|
||||||
|
// swap users
|
||||||
|
User getNewUser() => user = user == _bob ? _alice : _bob;
|
||||||
|
}
|
||||||
|
@ -9,13 +9,8 @@ import 'package:test/test.dart';
|
|||||||
///////////////////////////////////////
|
///////////////////////////////////////
|
||||||
////#docregion spec
|
////#docregion spec
|
||||||
List<Hero> expectedHeroes = [
|
List<Hero> expectedHeroes = [
|
||||||
new Hero()
|
new Hero(1, 'hero1'),
|
||||||
..id = 1
|
new Hero(2, 'hero2', true)
|
||||||
..name = 'hero1',
|
|
||||||
new Hero()
|
|
||||||
..id = 2
|
|
||||||
..name = 'hero2'
|
|
||||||
..isSecret = true
|
|
||||||
];
|
];
|
||||||
|
|
||||||
class HeroServiceMock implements HeroService {
|
class HeroServiceMock implements HeroService {
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
//#docregion
|
|
||||||
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/providers_component.dart';
|
import 'package:dependency_injection/providers_component.dart';
|
||||||
|
|
||||||
main() {
|
void main() {
|
||||||
//#docregion bootstrap
|
//#docregion bootstrap
|
||||||
bootstrap(AppComponent);
|
bootstrap(AppComponent);
|
||||||
//#enddocregion bootstrap
|
//#enddocregion bootstrap
|
||||||
|
@ -1,10 +1,21 @@
|
|||||||
import 'package:angular2/platform/browser.dart';
|
// **WARNING**
|
||||||
import 'package:dependency_injection/app_component.dart';
|
// To try out this version of the app, ensure that you update:
|
||||||
import 'package:dependency_injection/heroes/hero_service.dart';
|
// - web/index.html
|
||||||
|
// - pubspec.yaml
|
||||||
|
// to refer to this file instead of main.dart
|
||||||
|
|
||||||
main() {
|
import 'package:angular2/platform/browser.dart';
|
||||||
//#docregion bootstrap
|
|
||||||
bootstrap(AppComponent,
|
import 'package:dependency_injection/app_component_1.dart';
|
||||||
[HeroService]); // DISCOURAGED (but works)
|
import 'package:dependency_injection/heroes/hero_service_1.dart';
|
||||||
//#enddocregion bootstrap
|
|
||||||
|
void main() {
|
||||||
|
bootstrap(AppComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main_alt() {
|
||||||
|
// #docregion bootstrap-discouraged
|
||||||
|
bootstrap(AppComponent,
|
||||||
|
[HeroService]); // DISCOURAGED (but works)
|
||||||
|
// #enddocregion bootstrap-discouraged
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ describe('Dependency Injection Tests', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('P5 (useClass:EvenBetterLogger - dependency) displays as expected', function () {
|
it('P5 (useClass:EvenBetterLogger - dependency) displays as expected', function () {
|
||||||
expectedMsg = 'Message to Bob: Hello from EvenBetterlogger.';
|
expectedMsg = 'Message to Bob: Hello from EvenBetterlogger';
|
||||||
expect(element(by.css('#p5')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#p5')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -121,28 +121,18 @@ describe('Dependency Injection Tests', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('P8 (useFactory) displays as expected', function () {
|
it('P8 (useFactory) displays as expected', function () {
|
||||||
expectedMsg = 'Hero service injected successfully';
|
expectedMsg = 'Hero service injected successfully via heroServiceProvider';
|
||||||
expect(element(by.css('#p8')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#p8')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('P9a (string token) displays as expected', function () {
|
it('P9 (OpaqueToken) displays as expected', function () {
|
||||||
expectedMsg = '"app.config" Application title is Dependency Injection';
|
|
||||||
expect(element(by.css('#p9a')).getText()).toEqual(expectedMsg);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('P9b (OpaqueToken) displays as expected', function () {
|
|
||||||
expectedMsg = 'APP_CONFIG Application title is Dependency Injection';
|
expectedMsg = 'APP_CONFIG Application title is Dependency Injection';
|
||||||
expect(element(by.css('#p9b')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#p9')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('P10a (required dependency) displays as expected', function () {
|
it('P10 (optional dependency) displays as expected', function () {
|
||||||
expectedMsg = 'Hello from the required logger.';
|
expectedMsg = 'Optional logger was not available';
|
||||||
expect(element(by.css('#p10a')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#p10')).getText()).toEqual(expectedMsg);
|
||||||
});
|
|
||||||
|
|
||||||
it('P10b (optional dependency) displays as expected', function () {
|
|
||||||
expectedMsg = 'Optional logger was not available.';
|
|
||||||
expect(element(by.css('#p10b')).getText()).toEqual(expectedMsg);
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -19,14 +19,3 @@ import { HeroesComponent } from './heroes/heroes.component.1';
|
|||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
title = 'Dependency Injection';
|
title = 'Dependency Injection';
|
||||||
}
|
}
|
||||||
// #enddocregion
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
//#docregion ctor-di-fail
|
|
||||||
// FAIL! Injectable `config` is not a class!
|
|
||||||
constructor(heroService: HeroService, config: config) {
|
|
||||||
this.title = config.title;
|
|
||||||
}
|
|
||||||
//#enddocregion ctor-di-fail
|
|
||||||
*/
|
|
||||||
|
@ -5,7 +5,8 @@ import { CarComponent } from './car/car.component';
|
|||||||
import { HeroesComponent } from './heroes/heroes.component.1';
|
import { HeroesComponent } from './heroes/heroes.component.1';
|
||||||
|
|
||||||
import { provide, Inject } from '@angular/core';
|
import { provide, Inject } from '@angular/core';
|
||||||
import { Config, CONFIG } from './app.config';
|
import { APP_CONFIG, AppConfig,
|
||||||
|
HERO_DI_CONFIG } from './app.config';
|
||||||
import { Logger } from './logger.service';
|
import { Logger } from './logger.service';
|
||||||
// #enddocregion imports
|
// #enddocregion imports
|
||||||
|
|
||||||
@ -17,23 +18,19 @@ import { Logger } from './logger.service';
|
|||||||
<my-heroes></my-heroes>
|
<my-heroes></my-heroes>
|
||||||
`,
|
`,
|
||||||
directives:[CarComponent, HeroesComponent],
|
directives:[CarComponent, HeroesComponent],
|
||||||
// #docregion providers
|
|
||||||
providers: [
|
providers: [
|
||||||
Logger,
|
Logger,
|
||||||
// #docregion provider-config
|
// #docregion providers
|
||||||
provide('app.config', {useValue: CONFIG})
|
provide(APP_CONFIG, {useValue: HERO_DI_CONFIG})
|
||||||
// #enddocregion provider-config
|
// #enddocregion providers
|
||||||
]
|
]
|
||||||
// #docregion providers
|
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
title:string;
|
title:string;
|
||||||
|
|
||||||
// #docregion ctor
|
// #docregion ctor
|
||||||
constructor(@Inject('app.config') config:Config) {
|
constructor(@Inject(APP_CONFIG) config:AppConfig) {
|
||||||
|
|
||||||
this.title = config.title;
|
this.title = config.title;
|
||||||
}
|
}
|
||||||
// #docregion ctor
|
// #enddocregion ctor
|
||||||
}
|
}
|
||||||
// #enddocregion
|
|
||||||
|
@ -6,8 +6,8 @@ import { Component, Inject, provide } from '@angular/core';
|
|||||||
import { CarComponent } from './car/car.component';
|
import { CarComponent } from './car/car.component';
|
||||||
import { HeroesComponent } from './heroes/heroes.component';
|
import { HeroesComponent } from './heroes/heroes.component';
|
||||||
|
|
||||||
import { APP_CONFIG,
|
import { APP_CONFIG, AppConfig,
|
||||||
Config, CONFIG } from './app.config';
|
HERO_DI_CONFIG } from './app.config';
|
||||||
import { Logger } from './logger.service';
|
import { Logger } from './logger.service';
|
||||||
|
|
||||||
import { User, UserService } from './user.service';
|
import { User, UserService } from './user.service';
|
||||||
@ -33,22 +33,21 @@ import { ProvidersComponent } from './providers.component';
|
|||||||
`,
|
`,
|
||||||
directives:[CarComponent, HeroesComponent,
|
directives:[CarComponent, HeroesComponent,
|
||||||
InjectorComponent, TestComponent, ProvidersComponent],
|
InjectorComponent, TestComponent, ProvidersComponent],
|
||||||
// #docregion providers
|
// #docregion providers
|
||||||
providers: [
|
providers: [
|
||||||
Logger,
|
Logger,
|
||||||
UserService,
|
UserService,
|
||||||
provide(APP_CONFIG, {useValue: CONFIG})
|
provide(APP_CONFIG, {useValue: HERO_DI_CONFIG})
|
||||||
]
|
]
|
||||||
// #enddocregion providers
|
// #enddocregion providers
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
title:string;
|
title:string;
|
||||||
|
|
||||||
//#docregion ctor
|
// #docregion ctor
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(APP_CONFIG) config:Config,
|
@Inject(APP_CONFIG) config:AppConfig,
|
||||||
private userService: UserService) {
|
private userService: UserService) {
|
||||||
|
|
||||||
this.title = config.title;
|
this.title = config.title;
|
||||||
}
|
}
|
||||||
// #enddocregion ctor
|
// #enddocregion ctor
|
||||||
@ -62,4 +61,3 @@ export class AppComponent {
|
|||||||
`${this.isAuthorized ? '' : 'not'} authorized. `;
|
`${this.isAuthorized ? '' : 'not'} authorized. `;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// #enddocregion
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
//#docregion
|
|
||||||
// #docregion token
|
// #docregion token
|
||||||
import { OpaqueToken } from '@angular/core';
|
import { OpaqueToken } from '@angular/core';
|
||||||
|
|
||||||
@ -6,13 +5,12 @@ export let APP_CONFIG = new OpaqueToken('app.config');
|
|||||||
// #enddocregion token
|
// #enddocregion token
|
||||||
|
|
||||||
//#docregion config
|
//#docregion config
|
||||||
export interface Config {
|
export interface AppConfig {
|
||||||
apiEndpoint: string,
|
apiEndpoint: string,
|
||||||
title: string
|
title: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CONFIG:Config = {
|
export const HERO_DI_CONFIG: AppConfig = {
|
||||||
apiEndpoint: 'api.heroes.com',
|
apiEndpoint: 'api.heroes.com',
|
||||||
title: 'Dependency Injection'
|
title: 'Dependency Injection'
|
||||||
};
|
};
|
||||||
//#enddocregion config
|
|
@ -1,37 +1,27 @@
|
|||||||
// #docplaster
|
import { ReflectiveInjector } from '@angular/core';
|
||||||
//#docregion
|
|
||||||
import { ReflectiveInjector } from '@angular/core';
|
|
||||||
|
|
||||||
import { Car, Engine, Tires } from './car';
|
import { Car, Engine, Tires } from './car';
|
||||||
import { Logger } from '../logger.service';
|
import { Logger } from '../logger.service';
|
||||||
|
|
||||||
//#docregion injector
|
// #docregion injector
|
||||||
export function useInjector() {
|
export function useInjector() {
|
||||||
var injector:ReflectiveInjector;
|
var injector: ReflectiveInjector;
|
||||||
|
// #enddocregion injector
|
||||||
//#enddocregion injector
|
/*
|
||||||
/*
|
// #docregion injector-no-new
|
||||||
//#docregion injector-no-new
|
// Cannot instantiate an ReflectiveInjector like this!
|
||||||
// Cannot 'new' an ReflectiveInjector like this!
|
var injector = new ReflectiveInjector([Car, Engine, Tires]);
|
||||||
var injector = new ReflectiveInjector([Car, Engine, Tires, Logger]);
|
// #enddocregion injector-no-new
|
||||||
//#enddocregion injector-no-new
|
*/
|
||||||
*/
|
// #docregion injector, injector-create-and-call
|
||||||
|
injector = ReflectiveInjector.resolveAndCreate([Car, Engine, Tires]);
|
||||||
//#docregion injector
|
// #docregion injector-call
|
||||||
//#docregion injector-create-and-call
|
|
||||||
injector = ReflectiveInjector.resolveAndCreate([Car, Engine, Tires, Logger]);
|
|
||||||
//#docregion injector-call
|
|
||||||
var car = injector.get(Car);
|
var car = injector.get(Car);
|
||||||
//#enddocregion injector-call
|
// #enddocregion injector-call, injector-create-and-call
|
||||||
//#enddocregion injector-create-and-call
|
|
||||||
car.description = 'Injector';
|
car.description = 'Injector';
|
||||||
|
|
||||||
|
injector = ReflectiveInjector.resolveAndCreate([Logger]);
|
||||||
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());
|
||||||
|
|
||||||
return car;
|
return car;
|
||||||
}
|
}
|
||||||
//#enddocregion injector
|
|
||||||
|
|
||||||
|
|
||||||
//#enddocregion
|
|
||||||
|
@ -1,21 +1,15 @@
|
|||||||
// #docregion
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
// #docregion engine
|
|
||||||
export class Engine {
|
export class Engine {
|
||||||
public cylinders = 4; // default
|
public cylinders = 4;
|
||||||
}
|
}
|
||||||
// #enddocregion engine
|
|
||||||
|
|
||||||
// #docregion tires
|
|
||||||
export class Tires {
|
export class Tires {
|
||||||
public make = 'Flintstone';
|
public make = 'Flintstone';
|
||||||
public model = 'Square';
|
public model = 'Square';
|
||||||
}
|
}
|
||||||
// #enddocregion tires
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
// #docregion car
|
|
||||||
export class Car {
|
export class Car {
|
||||||
//#docregion car-ctor
|
//#docregion car-ctor
|
||||||
public description = 'DI';
|
public description = 'DI';
|
||||||
@ -29,4 +23,3 @@ export class Car {
|
|||||||
`${this.engine.cylinders} cylinders and ${this.tires.make} tires.`
|
`${this.engine.cylinders} cylinders and ${this.tires.make} tires.`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// #enddocregion car
|
|
||||||
|
@ -9,7 +9,7 @@ import { HEROES } from './mock-heroes';
|
|||||||
<div *ngFor="let hero of heroes">
|
<div *ngFor="let hero of heroes">
|
||||||
{{hero.id}} - {{hero.name}}
|
{{hero.id}} - {{hero.name}}
|
||||||
</div>
|
</div>
|
||||||
`,
|
`
|
||||||
})
|
})
|
||||||
export class HeroListComponent {
|
export class HeroListComponent {
|
||||||
heroes = HEROES;
|
heroes = HEROES;
|
||||||
|
@ -1,8 +1,16 @@
|
|||||||
|
// #docplaster
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
import { Hero } from './hero';
|
import { Hero } from './hero';
|
||||||
|
// #enddocregion
|
||||||
|
import { HeroService } from './hero.service.1';
|
||||||
|
/*
|
||||||
|
// #docregion
|
||||||
import { HeroService } from './hero.service';
|
import { HeroService } from './hero.service';
|
||||||
|
// #enddocregion
|
||||||
|
*/
|
||||||
|
// #docregion
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'hero-list',
|
selector: 'hero-list',
|
||||||
|
@ -17,8 +17,9 @@ export class HeroListComponent {
|
|||||||
heroes: Hero[];
|
heroes: Hero[];
|
||||||
|
|
||||||
// #docregion ctor-signature
|
// #docregion ctor-signature
|
||||||
constructor(heroService: HeroService) {
|
constructor(heroService: HeroService)
|
||||||
// #enddocregion ctor-signature
|
// #enddocregion ctor-signature
|
||||||
|
{
|
||||||
this.heroes = heroService.getHeroes();
|
this.heroes = heroService.getHeroes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Hero } from './hero';
|
import { Injectable } from '@angular/core';
|
||||||
import { HEROES } from './mock-heroes';
|
|
||||||
|
|
||||||
|
import { Hero } from './hero';
|
||||||
|
import { HEROES } from './mock-heroes';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class HeroService {
|
export class HeroService {
|
||||||
getHeroes() { return HEROES; }
|
getHeroes() { return HEROES; }
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,18 @@
|
|||||||
// #docplaster
|
// #docplaster
|
||||||
// #docregion
|
// #docregion full, v1
|
||||||
// #docregion v1
|
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
// #enddocregion full, v1
|
||||||
|
import { HeroListComponent } from './hero-list.component.2';
|
||||||
|
import { HeroService } from './hero.service.1';
|
||||||
|
/*
|
||||||
|
// #docregion full
|
||||||
import { HeroListComponent } from './hero-list.component';
|
import { HeroListComponent } from './hero-list.component';
|
||||||
// #enddocregion v1
|
|
||||||
import { HeroService } from './hero.service';
|
|
||||||
// #docregion v1
|
// #docregion v1
|
||||||
|
import { HeroService } from './hero.service';
|
||||||
|
// #enddocregion full, v1
|
||||||
|
*/
|
||||||
|
// #docregion full, v1
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-heroes',
|
selector: 'my-heroes',
|
||||||
@ -15,11 +21,8 @@ import { HeroService } from './hero.service';
|
|||||||
<hero-list></hero-list>
|
<hero-list></hero-list>
|
||||||
`,
|
`,
|
||||||
// #enddocregion v1
|
// #enddocregion v1
|
||||||
// #docregion providers
|
|
||||||
providers:[HeroService],
|
providers:[HeroService],
|
||||||
// #enddocregion providers
|
// #docregion v1
|
||||||
// #docregion v1
|
|
||||||
directives:[HeroListComponent]
|
directives:[HeroListComponent]
|
||||||
})
|
})
|
||||||
export class HeroesComponent { }
|
export class HeroesComponent { }
|
||||||
// #enddocregion v1
|
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
import { Hero } from './hero';
|
import { Hero } from './hero';
|
||||||
|
|
||||||
export var HEROES: Hero[] = [
|
export var HEROES: Hero[] = [
|
||||||
{ "id": 11, isSecret: false, "name": "Mr. Nice" },
|
{ id: 11, isSecret: false, name: "Mr. Nice" },
|
||||||
{ "id": 12, isSecret: false, "name": "Narco" },
|
{ id: 12, isSecret: false, name: "Narco" },
|
||||||
{ "id": 13, isSecret: false, "name": "Bombasto" },
|
{ id: 13, isSecret: false, name: "Bombasto" },
|
||||||
{ "id": 14, isSecret: false, "name": "Celeritas" },
|
{ id: 14, isSecret: false, name: "Celeritas" },
|
||||||
{ "id": 15, isSecret: false, "name": "Magneta" },
|
{ id: 15, isSecret: false, name: "Magneta" },
|
||||||
{ "id": 16, isSecret: false, "name": "RubberMan" },
|
{ id: 16, isSecret: false, name: "RubberMan" },
|
||||||
{ "id": 17, isSecret: false, "name": "Dynama" },
|
{ id: 17, isSecret: false, name: "Dynama" },
|
||||||
{ "id": 18, isSecret: true, "name": "Dr IQ" },
|
{ id: 18, isSecret: true, name: "Dr IQ" },
|
||||||
{ "id": 19, isSecret: true, "name": "Magma" },
|
{ id: 19, isSecret: true, name: "Magma" },
|
||||||
{ "id": 20, isSecret: true, "name": "Tornado" }
|
{ id: 20, isSecret: true, name: "Tornado" }
|
||||||
];
|
];
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
// #docplaster
|
// #docplaster
|
||||||
//#docregion
|
// #docregion
|
||||||
import { Component, Injector } from '@angular/core';
|
import { Component, Injector } from '@angular/core';
|
||||||
|
|
||||||
import { Car, Engine, Tires } from './car/car';
|
import { Car, Engine, Tires } from './car/car';
|
||||||
|
import { Hero } from './heroes/hero';
|
||||||
import { HeroService } from './heroes/hero.service';
|
import { HeroService } from './heroes/hero.service';
|
||||||
import { heroServiceProvider } from './heroes/hero.service.provider';
|
import { heroServiceProvider } from './heroes/hero.service.provider';
|
||||||
import { Logger } from './logger.service';
|
import { Logger } from './logger.service';
|
||||||
|
|
||||||
//#docregion injector
|
// #docregion injector
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-injectors',
|
selector: 'my-injectors',
|
||||||
template: `
|
template: `
|
||||||
@ -16,29 +17,24 @@ import { Logger } from './logger.service';
|
|||||||
<div id="hero">{{hero.name}}</div>
|
<div id="hero">{{hero.name}}</div>
|
||||||
<div id="rodent">{{rodent}}</div>
|
<div id="rodent">{{rodent}}</div>
|
||||||
`,
|
`,
|
||||||
|
providers: [Car, Engine, Tires, heroServiceProvider, Logger]
|
||||||
providers: [Car, Engine, Tires,
|
|
||||||
heroServiceProvider, Logger]
|
|
||||||
})
|
})
|
||||||
export class InjectorComponent {
|
export class InjectorComponent {
|
||||||
constructor(private injector: Injector) { }
|
constructor(private injector: Injector) { }
|
||||||
|
|
||||||
car:Car = this.injector.get(Car);
|
car: Car = this.injector.get(Car);
|
||||||
|
|
||||||
//#docregion get-hero-service
|
// #docregion get-hero-service
|
||||||
heroService:HeroService = this.injector.get(HeroService);
|
heroService: HeroService = this.injector.get(HeroService);
|
||||||
//#enddocregion get-hero-service
|
// #enddocregion get-hero-service
|
||||||
hero = this.heroService.getHeroes()[0];
|
hero: Hero = this.heroService.getHeroes()[0];
|
||||||
|
|
||||||
get rodent() {
|
get rodent() {
|
||||||
let rous = this.injector.get(ROUS, null);
|
let rousDontExist = "R.O.U.S.'s? I don't think they exist!";
|
||||||
if (rous) {
|
return this.injector.get(ROUS, rousDontExist);
|
||||||
throw new Error('Aaaargh!')
|
|
||||||
}
|
|
||||||
return "R.O.U.S.'s? I don't think they exist!";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//#enddocregion injector
|
// #enddocregion injector
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* R.O.U.S. - Rodents Of Unusual Size
|
* R.O.U.S. - Rodents Of Unusual Size
|
||||||
|
@ -4,7 +4,8 @@ import { Injectable } from '@angular/core';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class Logger {
|
export class Logger {
|
||||||
logs:string[] = []; // capture logs for testing
|
logs:string[] = []; // capture logs for testing
|
||||||
log(message: string){
|
|
||||||
|
log(message: string) {
|
||||||
this.logs.push(message);
|
this.logs.push(message);
|
||||||
console.log(message);
|
console.log(message);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
import { bootstrap } from '@angular/platform-browser-dynamic';
|
import { bootstrap } from '@angular/platform-browser-dynamic';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component.1';
|
||||||
import { HeroService } from './heroes/hero.service';
|
import { HeroService } from './heroes/hero.service.1';
|
||||||
|
|
||||||
//#docregion bootstrap
|
bootstrap(AppComponent);
|
||||||
bootstrap(AppComponent,
|
|
||||||
[HeroService]); // DISCOURAGED (but works)
|
function discouraged() {
|
||||||
//#enddocregion bootstrap
|
//#docregion bootstrap-discouraged
|
||||||
|
bootstrap(AppComponent,
|
||||||
|
[HeroService]); // DISCOURAGED (but works)
|
||||||
|
//#enddocregion bootstrap-discouraged
|
||||||
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
//#docregion
|
|
||||||
import { bootstrap } from '@angular/platform-browser-dynamic';
|
import { bootstrap } from '@angular/platform-browser-dynamic';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { ProvidersComponent } from './providers.component';
|
import { ProvidersComponent } from './providers.component';
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
import { Component, Inject, Injectable,
|
import { Component, Inject, Injectable,
|
||||||
provide, Provider } from '@angular/core';
|
provide, Provider } from '@angular/core';
|
||||||
|
|
||||||
import { APP_CONFIG,
|
import { APP_CONFIG, AppConfig,
|
||||||
Config, CONFIG } from './app.config';
|
HERO_DI_CONFIG } from './app.config';
|
||||||
|
|
||||||
import { HeroService } from './heroes/hero.service';
|
import { HeroService } from './heroes/hero.service';
|
||||||
import { heroServiceProvider } from './heroes/hero.service.provider';
|
import { heroServiceProvider } from './heroes/hero.service.provider';
|
||||||
@ -18,10 +18,9 @@ let template = '{{log}}';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'provider-1',
|
selector: 'provider-1',
|
||||||
template: template,
|
template: template,
|
||||||
providers:
|
// #docregion providers-1, providers-logger
|
||||||
// #docregion providers-1
|
providers: [Logger]
|
||||||
[Logger]
|
// #enddocregion providers-1, providers-logger
|
||||||
// #enddocregion providers-1
|
|
||||||
})
|
})
|
||||||
export class ProviderComponent1 {
|
export class ProviderComponent1 {
|
||||||
log: string;
|
log: string;
|
||||||
@ -104,15 +103,12 @@ export class ProviderComponent4 {
|
|||||||
//////////////////////////////////////////
|
//////////////////////////////////////////
|
||||||
// #docregion EvenBetterLogger
|
// #docregion EvenBetterLogger
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class EvenBetterLogger {
|
class EvenBetterLogger extends Logger {
|
||||||
logs: string[] = [];
|
constructor(private userService: UserService) { super(); }
|
||||||
|
|
||||||
constructor(private userService: UserService) { }
|
log(message: string) {
|
||||||
|
let name = this.userService.user.name;
|
||||||
log(message: string){
|
super.log(`Message to ${name}: ${message}`);
|
||||||
message = `Message to ${this.userService.user.name}: ${message}.`;
|
|
||||||
console.log(message);
|
|
||||||
this.logs.push(message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// #enddocregion EvenBetterLogger
|
// #enddocregion EvenBetterLogger
|
||||||
@ -218,117 +214,69 @@ export class ProviderComponent7 {
|
|||||||
template: template,
|
template: template,
|
||||||
providers: [heroServiceProvider, Logger, UserService]
|
providers: [heroServiceProvider, Logger, UserService]
|
||||||
})
|
})
|
||||||
export class ProviderComponent8{
|
export class ProviderComponent8 {
|
||||||
// #docregion provider-8-ctor
|
// #docregion provider-8-ctor
|
||||||
constructor(heroService: HeroService){ }
|
constructor(heroService: HeroService) { }
|
||||||
// #enddocregion provider-8-ctor
|
// #enddocregion provider-8-ctor
|
||||||
|
|
||||||
// must be true else this component would have blown up at runtime
|
// must be true else this component would have blown up at runtime
|
||||||
log = 'Hero service injected successfully';
|
log = 'Hero service injected successfully via heroServiceProvider';
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////
|
/////////////////
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'provider-9a',
|
selector: 'provider-9',
|
||||||
template: template,
|
template: template,
|
||||||
providers:
|
/*
|
||||||
/*
|
// #docregion providers-9-interface
|
||||||
// #docregion providers-9a-interface
|
// FAIL! Can't use interface as provider token
|
||||||
// FAIL! Can't use interface as provider token
|
[provide(AppConfig, {useValue: HERO_DI_CONFIG})]
|
||||||
[provide(Config, {useValue: CONFIG})]
|
// #enddocregion providers-9-interface
|
||||||
// #enddocregion providers-9a-interface
|
*/
|
||||||
*/
|
// #docregion providers-9
|
||||||
// #docregion providers-9a
|
providers: [provide(APP_CONFIG, {useValue: HERO_DI_CONFIG})]
|
||||||
// Use string as provider token
|
// #enddocregion providers-9
|
||||||
[provide('app.config', {useValue: CONFIG})]
|
|
||||||
// #enddocregion providers-9a
|
|
||||||
})
|
})
|
||||||
export class ProviderComponent9a {
|
export class ProviderComponent9 {
|
||||||
log: string;
|
log: string;
|
||||||
/*
|
/*
|
||||||
// #docregion provider-9a-ctor-interface
|
// #docregion provider-9-ctor-interface
|
||||||
// FAIL! Can't inject using the interface as the parameter type
|
// FAIL! Can't inject using the interface as the parameter type
|
||||||
constructor(private config: Config){ }
|
constructor(private config: AppConfig){ }
|
||||||
// #enddocregion provider-9a-ctor-interface
|
// #enddocregion provider-9-ctor-interface
|
||||||
*/
|
*/
|
||||||
|
// #docregion provider-9-ctor
|
||||||
// #docregion provider-9a-ctor
|
constructor(@Inject(APP_CONFIG) private config: AppConfig) { }
|
||||||
// @Inject(token) to inject the dependency
|
// #enddocregion provider-9-ctor
|
||||||
constructor(@Inject('app.config') private config: Config){ }
|
|
||||||
// #enddocregion provider-9a-ctor
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
this.log = '"app.config" Application title is ' + this.config.title;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'provider-9b',
|
|
||||||
template: template,
|
|
||||||
// #docregion providers-9b
|
|
||||||
providers: [provide(APP_CONFIG, {useValue: CONFIG})]
|
|
||||||
// #enddocregion providers-9b
|
|
||||||
})
|
|
||||||
export class ProviderComponent9b {
|
|
||||||
log: string;
|
|
||||||
// #docregion provider-9b-ctor
|
|
||||||
constructor(@Inject(APP_CONFIG) private config: Config){ }
|
|
||||||
// #enddocregion provider-9b-ctor
|
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.log = 'APP_CONFIG Application title is ' + this.config.title;
|
this.log = 'APP_CONFIG Application title is ' + this.config.title;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//////////////////////////////////////////
|
//////////////////////////////////////////
|
||||||
// Normal required logger
|
// Sample providers 1 to 7 illustrate a required logger dependency.
|
||||||
@Component({
|
// Optional logger, can be null
|
||||||
selector: 'provider-10a',
|
|
||||||
template: template,
|
|
||||||
// #docregion providers-logger
|
|
||||||
providers: [Logger]
|
|
||||||
// #enddocregion providers-logger
|
|
||||||
})
|
|
||||||
export class ProviderComponent10a {
|
|
||||||
log: string;
|
|
||||||
constructor(logger: Logger) {
|
|
||||||
logger.log('Hello from the required logger.');
|
|
||||||
this.log = logger.logs[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optional logger
|
|
||||||
// #docregion import-optional
|
// #docregion import-optional
|
||||||
import {Optional} from '@angular/core';
|
import {Optional} from '@angular/core';
|
||||||
// #enddocregion import-optional
|
// #enddocregion import-optional
|
||||||
|
|
||||||
|
let some_message: string = 'Hello from the injected logger';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'provider-10b',
|
selector: 'provider-10',
|
||||||
template: template
|
template: template
|
||||||
})
|
})
|
||||||
export class ProviderComponent10b {
|
export class ProviderComponent10 {
|
||||||
// #docregion provider-10-ctor
|
|
||||||
log: string;
|
log: string;
|
||||||
constructor(@Optional() private logger: Logger) { }
|
// #docregion provider-10-ctor
|
||||||
|
constructor(@Optional() private logger: Logger) {
|
||||||
|
if (this.logger)
|
||||||
|
this.logger.log(some_message);
|
||||||
|
}
|
||||||
// #enddocregion provider-10-ctor
|
// #enddocregion provider-10-ctor
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
// #docregion provider-10-logger
|
this.log = this.logger ? this.logger.logs[0] : 'Optional logger was not available';
|
||||||
// No logger? Make one!
|
|
||||||
if (!this.logger) {
|
|
||||||
this.logger = {
|
|
||||||
log: (msg: string) => this.logger.logs.push(msg),
|
|
||||||
logs: []
|
|
||||||
};
|
|
||||||
// #enddocregion provider-10-logger
|
|
||||||
this.logger.log('Optional logger was not available.');
|
|
||||||
// #docregion provider-10-logger
|
|
||||||
}
|
|
||||||
// #enddocregion provider-10-logger
|
|
||||||
else {
|
|
||||||
this.logger.log('Hello from the injected logger.');
|
|
||||||
this.log = this.logger.logs[0];
|
|
||||||
}
|
|
||||||
this.log = this.logger.logs[0];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,10 +295,8 @@ export class ProviderComponent10b {
|
|||||||
<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="p9"><provider-9></provider-9></div>
|
||||||
<div id="p9b"><provider-9b></provider-9b></div>
|
<div id="p10"><provider-10></provider-10></div>
|
||||||
<div id="p10a"><provider-10a></provider-10a></div>
|
|
||||||
<div id="p10b"><provider-10b></provider-10b></div>
|
|
||||||
`,
|
`,
|
||||||
directives: [
|
directives: [
|
||||||
ProviderComponent1,
|
ProviderComponent1,
|
||||||
@ -363,10 +309,8 @@ export class ProviderComponent10b {
|
|||||||
ProviderComponent6b,
|
ProviderComponent6b,
|
||||||
ProviderComponent7,
|
ProviderComponent7,
|
||||||
ProviderComponent8,
|
ProviderComponent8,
|
||||||
ProviderComponent9a,
|
ProviderComponent9,
|
||||||
ProviderComponent9b,
|
ProviderComponent10,
|
||||||
ProviderComponent10a,
|
|
||||||
ProviderComponent10b,
|
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ProvidersComponent { }
|
export class ProvidersComponent { }
|
||||||
|
@ -35,14 +35,14 @@ function runTests() {
|
|||||||
|
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
// Fake Jasmine infrastructure
|
// Fake Jasmine infrastructure
|
||||||
var testName:string;
|
var testName: string;
|
||||||
var testResults: {pass:string; message:string};
|
var testResults: {pass:string; message:string};
|
||||||
|
|
||||||
function expect(actual:any) {
|
function expect(actual:any) {
|
||||||
return {
|
return {
|
||||||
toEqual: function(expected:any){
|
toEqual: function(expected:any){
|
||||||
testResults = actual === expected?
|
testResults = actual === expected?
|
||||||
{pass:'passed', message: `${testName}`} :
|
{pass:'passed', message: testName} :
|
||||||
{pass:'failed', message: `${testName}; expected ${actual} to equal ${expected}.`};
|
{pass:'failed', message: `${testName}; expected ${actual} to equal ${expected}.`};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,22 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class UserService {
|
|
||||||
// Todo: get the user; don't 'new' it.
|
|
||||||
private _alice = new User('Alice', true);
|
|
||||||
private _bob = new User('Bob', false);
|
|
||||||
|
|
||||||
// initial user is Bob
|
|
||||||
user = this._bob;
|
|
||||||
|
|
||||||
// swaps users
|
|
||||||
getNewUser() {
|
|
||||||
return this.user = this.user === this._bob ? this._alice : this._bob;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class User {
|
export class User {
|
||||||
constructor(
|
constructor(
|
||||||
public name:string,
|
public name:string,
|
||||||
public isAuthorized:boolean = false) { }
|
public isAuthorized:boolean = false) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Todo: get the user; don't 'new' it.
|
||||||
|
let alice = new User('Alice', true);
|
||||||
|
let bob = new User('Bob', false);
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UserService {
|
||||||
|
user = bob; // initial user is Bob
|
||||||
|
|
||||||
|
// swap users
|
||||||
|
getNewUser() {
|
||||||
|
return this.user = this.user === bob ? alice : bob;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,279 +1,138 @@
|
|||||||
include ../_util-fns
|
extends ../../../ts/latest/guide/dependency-injection.jade
|
||||||
|
|
||||||
+includeShared('{ts}', 'intro')
|
block includes
|
||||||
|
include ../_util-fns
|
||||||
|
- var _thisDot = '';
|
||||||
|
|
||||||
:marked
|
block ctor-syntax
|
||||||
The complete source code for the example app in this chapter is
|
.l-sub-section
|
||||||
[in GitHub](https://github.com/angular/angular.io/tree/master/public/docs/_examples/dependency-injection/dart).
|
:marked
|
||||||
|
We also leveraged Dart's constructor syntax for declaring parameters and
|
||||||
|
initializing properties simultaneously.
|
||||||
|
|
||||||
+includeShared('{ts}', 'why-1')
|
block service-in-its-own-file
|
||||||
+makeExample('dependency-injection/dart/lib/car/car_no_di.dart', 'car', 'lib/car/car.dart (without DI)')
|
//- N/A
|
||||||
+includeShared('{ts}', 'why-2')
|
|
||||||
+makeTabs(
|
block one-class-per-file-ts-tradeoffs
|
||||||
'dependency-injection/dart/lib/car/car.dart, dependency-injection/dart/lib/car/car_no_di.dart',
|
//- N/A
|
||||||
'car-ctor, car-ctor',
|
|
||||||
'lib/car/car.dart (excerpt with DI), lib/car/car.dart (excerpt without DI)')(format=".")
|
block injectable-not-always-needed-in-ts
|
||||||
+includeShared('{ts}', 'why-3-1')
|
//- The [Angular 2 Dart Transformer](https://github.com/angular/angular/wiki/Angular-2-Dart-Transformer)
|
||||||
+includeShared('{ts}', 'why-3-2')
|
//- generates static code to replace the use of dart:mirrors. It requires that types be
|
||||||
- var stylePattern = { otl: /(new Car.*$)/gm };
|
//- identified as targets for static code generation. Generally this is achieved
|
||||||
+makeExample('dependency-injection/dart/lib/car/car_creations.dart', 'car-ctor-instantiation', '', stylePattern)(format=".")
|
//- by marking the class as @Injectable (though there are other mechanisms).
|
||||||
+includeShared('{ts}', 'why-4')
|
|
||||||
.l-sub-section
|
block ts-any-decorator-will-do
|
||||||
|
//- N/A
|
||||||
|
|
||||||
|
block always-include-paren
|
||||||
:marked
|
:marked
|
||||||
The _consumer_ of `Car` has the problem. The consumer must update the car creation code to
|
Always write `@Injectable()`, not just `@Injectable`.
|
||||||
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')
|
|
||||||
|
|
||||||
+includeShared('{ts}', 'di-1')
|
|
||||||
+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
|
A metadata annotation must be either a reference to a
|
||||||
compile-time constant variable or a call to a constant
|
compile-time constant variable or a call to a constant
|
||||||
constructor such as `Injectable()`.
|
constructor such as `Injectable()`.
|
||||||
|
|
||||||
If we forget the parentheses, the analyzer will complain:
|
If we forget the parentheses, the analyzer will complain:
|
||||||
"Annotation creation must have arguments". If we try to run the
|
"Annotation creation must have arguments". If we try to run the
|
||||||
app anyway, it won't work, and the console will say
|
app anyway, it won't work, and the console will say
|
||||||
"expression must be a compile-time constant".
|
"expression must be a compile-time constant".
|
||||||
+includeShared('{ts}', 'logger-service-1')
|
|
||||||
+makeExample(
|
block real-logger
|
||||||
'dependency-injection/dart/lib/logger_service.dart',null, 'lib/logger_service')
|
.l-sub-section
|
||||||
.l-sub-section
|
:marked
|
||||||
|
A real implementation would probably use the
|
||||||
|
[logging package](https://pub.dartlang.org/packages/logging).
|
||||||
|
|
||||||
|
block optional-logger
|
||||||
|
//- TBC.
|
||||||
|
|
||||||
|
block provider-function-etc
|
||||||
|
//- N/A
|
||||||
|
|
||||||
|
block provider-ctor-args
|
||||||
|
- var _secondParam = 'named parameter, such as <code>useClass</code>'
|
||||||
:marked
|
:marked
|
||||||
### Implementing a logger
|
We supply two arguments (or more) to the `Provider` constructor.
|
||||||
|
|
||||||
Our examples use a simple logger.
|
block dart-diff-const-metadata
|
||||||
A real implementation would probably use the
|
.callout.is-helpful
|
||||||
[logging package](https://pub.dartlang.org/packages/logging).
|
header Dart difference: Constants in metadata
|
||||||
:marked
|
:marked
|
||||||
We're likely to need the same logger service everywhere in our application,
|
In Dart, the value of a metadata annotation must be a compile-time constant.
|
||||||
so we put it in the `lib/` folder, and
|
For that reason, we can't call functions to get values
|
||||||
we register it in the `providers` array of the metadata for our application root component, `AppComponent`.
|
to use within an annotation.
|
||||||
+makeExample('dependency-injection/dart/lib/providers_component.dart','providers-logger', 'lib/app_component.dart (excerpt)')
|
Instead, we use constant literals or constant constructors.
|
||||||
+includeShared('{ts}', 'logger-service-3')
|
For example, a TypeScript program might use the
|
||||||
+includeShared('{ts}', 'logger-service-4')
|
function call `provide(Logger, {useClass: BetterLogger})`,
|
||||||
+includeShared('{ts}', 'logger-service-5')
|
which is equivalent to the TypeScript code
|
||||||
+makeExample('dependency-injection/dart/lib/providers_component.dart','provider-10-ctor')(format='.')
|
`new Provider(Logger, {useClass: BetterLogger})`.
|
||||||
+includeShared('{ts}', 'logger-service-6')
|
A Dart annotation would instead use the constant value `const Provider(Logger, useClass: BetterLogger)`.
|
||||||
+makeExample('dependency-injection/dart/lib/providers_component.dart','provider-10-logger')(format='.')
|
|
||||||
+includeShared('{ts}', 'logger-service-7')
|
|
||||||
|
|
||||||
+includeShared('{ts}', 'providers-1')
|
block dart-diff-const-metadata-ctor
|
||||||
+makeExample('dependency-injection/dart/lib/providers_component.dart','providers-logger')
|
.callout.is-helpful
|
||||||
+includeShared('{ts}', 'providers-2')
|
header Dart difference: Constants in metadata
|
||||||
+includeShared('{ts}', 'providers-provide-1')
|
:marked
|
||||||
:marked
|
Because Dart annotations must be compile-time constants,
|
||||||
### The *Provider* class
|
`useValue` is often used with string or list literals.
|
||||||
+includeShared('{ts}', 'providers-provide-1-1')
|
However, `useValue` works with any constant object.
|
||||||
+makeExample('dependency-injection/dart/lib/providers_component.dart','providers-1')
|
|
||||||
+includeShared('{ts}', 'providers-provide-2')
|
To create a class that can provide constant objects,
|
||||||
+makeExample('dependency-injection/dart/lib/providers_component.dart','providers-2')
|
ensure all its instance variables are `final`,
|
||||||
// includeShared('{ts}', 'providers-provide-3')
|
and give it a `const` constructor.
|
||||||
// includeShared('{ts}', 'providers-provide-4-1')
|
|
||||||
// Don't discuss provide function.
|
Create a constant instance of the class by using `const` instead of `new`.
|
||||||
:marked
|
|
||||||
We supply two arguments (or more) to the `Provider` constructor.
|
// - var stylePattern = { otl: /(useValue.*\))/gm };
|
||||||
+includeShared('{ts}', 'providers-provide-4-2')
|
// +makeExample('dependency-injection/dart/lib/providers_component.dart','providers-9','', stylePattern)(format='.')
|
||||||
:marked
|
|
||||||
The second is a named parameter, such as `useClass`,
|
block non-class-dep-eg
|
||||||
that we can think of as a *recipe* for creating the dependency value.
|
span string, list, map, or maybe a function.
|
||||||
There are many ways to create dependency values... and many ways to write a recipe.
|
|
||||||
+includeShared('{ts}', 'providers-alternative-1')
|
block config-obj-maps
|
||||||
+makeExample('dependency-injection/dart/lib/providers_component.dart','providers-4')
|
| . They can be
|
||||||
.callout.is-helpful
|
| <b><a href="https://api.dartlang.org/stable/dart-core/Map-class.html">Map</a></b>
|
||||||
header Dart difference: Constants in metadata
|
| literals
|
||||||
|
|
||||||
|
block what-should-we-use-as-token
|
||||||
:marked
|
:marked
|
||||||
In Dart, the value of a metadata annotation must be a compile-time constant.
|
But what should we use as the token?
|
||||||
For that reason, we can't call functions to get values
|
While we _could_ use **[Map][]**, we _should not_ because (like
|
||||||
to use within an annotation.
|
`String`) `Map` is too general. Our app might depend on several maps, each
|
||||||
Instead, we use constant literals or constant constructors.
|
for a different purpose.
|
||||||
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')
|
[Map]: https://api.dartlang.org/stable/dart-core/Map-class.html
|
||||||
: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,
|
.callout.is-helpful
|
||||||
`useValue` is often used with string or list literals.
|
header Dart difference: Interfaces are valid tokens
|
||||||
However, `useValue` works with any constant object.
|
:marked
|
||||||
|
In TypeScript, interfaces don't work as provider tokens.
|
||||||
|
Dart doesn't have this limitation;
|
||||||
|
every class implicitly defines an interface,
|
||||||
|
so interface names are just class names.
|
||||||
|
`Map` is a *valid* token even though it's the name of an abstract class;
|
||||||
|
it's just *unsuitable* as a token because it's too general.
|
||||||
|
|
||||||
To create a class that can provide constant objects,
|
block dart-map-alternative
|
||||||
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
|
:marked
|
||||||
In TypeScript, interfaces don't work as provider tokens.
|
As an alternative to using a configuration `Map`, we can define
|
||||||
Dart doesn't have this problem;
|
a custom configuration class:
|
||||||
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=".")
|
|
||||||
|
|
||||||
|
+makeExample('dependency-injection/ts/app/app.config.ts','config-alt','app/app-config.ts (alternative config)')(format='.')
|
||||||
|
|
||||||
+includeShared('{ts}', 'summary')
|
:marked
|
||||||
|
Defining a configuration class has a few benefits. One key benefit
|
||||||
|
is strong static checking: we'll be warned early if we misspell a property
|
||||||
|
name or assign it a value of the wrong type.
|
||||||
|
The Dart [cascade notation][cascade] (`..`) provides a convenient means of initializing
|
||||||
|
a configuration object.
|
||||||
|
|
||||||
+includeShared('{ts}', 'appendix-explicit-injector-1')
|
If we use cascades, the configuration object can't be declared `const` and
|
||||||
+makeExample('dependency-injection/dart/lib/injector_component.dart', 'injector', 'lib/injector_component.dart')
|
we can't use a [value provider](#value-provider).
|
||||||
+includeShared('{ts}', 'appendix-explicit-injector-2')
|
A solution is to use a [factory provider](#factory-provider).
|
||||||
|
We illustrate this next. We also show how to provide and inject the
|
||||||
|
configuration object in our top-level `AppComponent`:
|
||||||
|
|
||||||
|
[cascade]: https://www.dartlang.org/docs/dart-up-and-running/ch02.html#cascade
|
||||||
|
|
||||||
|
+makeExcerpt('lib/app_component.dart','providers')
|
||||||
|
+makeExcerpt('lib/app_component.dart','ctor')
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user