angular-docs-cn/public/docs/dart/latest/guide/dependency-injection.jade

157 lines
6.3 KiB
Plaintext

extends ../../../ts/_cache/guide/dependency-injection.jade
block includes
include ../_util-fns
- var _thisDot = '';
block ctor-syntax
.l-sub-section
:marked
We also leveraged Dart's constructor syntax for declaring parameters and
initializing properties simultaneously.
block register-provider-ngmodule
:marked
Before we do, let's see an example of provider registration during bootstrapping:
+makeExcerpt('app/main.1.ts (discouraged)', 'bootstrap-discouraged', '')
:marked
The injector now knows about our `HeroService`.
An instance of our `HeroService` will be available for injection across our entire application.
Of course we can't help wondering about that comment telling us not to do it this way.
It *will* work. It's just not a best practice.
The bootstrap provider option is intended for configuring and overriding Angular's own
preregistered services, such as its routing support.
The preferred approach is to register application providers in application components.
Because the `HeroService` is used within the *Heroes* feature area —
and nowhere else — the ideal place to register it is in the top-level `HeroesComponent`.
block ngmodule-vs-component
:marked
Look at the `providers` part of the `@Component` annotation.
An instance of the `HeroService` is now available for injection in this `HeroesComponent`
and all of its child components.
The `HeroesComponent` itself doesn't happen to need the `HeroService`.
But its child `HeroListComponent` does, so we head there next.
block injectable-not-always-needed-in-ts
//- The [Angular 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 always-include-paren
:marked
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".
block real-logger
.l-sub-section
:marked
A real implementation would probably use the
[logging package](https://pub.dartlang.org/packages/logging).
block provider-shorthand
:marked
This is actually a shorthand expression for a provider registration
that creates a new instance of the
[Provider](../api/angular2.core/Provider-class.html) class:
block provider-ctor-args
- var _secondParam = 'named parameter, such as <code>useClass</code>'
:marked
We supply two arguments (or more) to the `Provider` constructor.
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 will use the
object literal `{ provide: Logger, useClass: BetterLogger }`.
A Dart annotation would instead use the constant value
`const Provider(Logger, useClass: BetterLogger)`.
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
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.
[Map]: https://api.dartlang.org/stable/dart-core/Map-class.html
.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.
block dart-map-alternative
:marked
As an alternative to using a configuration `Map`, we can define
a custom configuration class:
+makeExcerpt('lib/app_config.dart (alternative config)','config-alt')
: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.
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')