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