diff --git a/public/docs/ts/latest/cookbook/aot-compiler.jade b/public/docs/ts/latest/cookbook/aot-compiler.jade index dab6b7e19c..9b8128b840 100644 --- a/public/docs/ts/latest/cookbook/aot-compiler.jade +++ b/public/docs/ts/latest/cookbook/aot-compiler.jade @@ -7,6 +7,7 @@ include ../_util-fns a#toc :marked # Contents + - [Overview](overview) - [Ahead-of-time (AOT) vs just-in-time (JIT)](#aot-jit) - [Why do AOT compilation?](#why-aot) @@ -282,8 +283,8 @@ a#rollup-plugins Luckily, there is a Rollup plugin that modifies _RxJs_ to use the ES `import` and `export` statements that Rollup requires. - Rollup then preserves the parts of `RxJS` referenced by the application - in the final bundle. Using it is straigthforward. Add the following to + Rollup then preserves the parts of `RxJS` referenced by the application + in the final bundle. Using it is straigthforward. Add the following to the `plugins` !{_array} in `rollup-config.js`: +makeExample('cb-aot-compiler/ts/rollup-config.js','commonjs','rollup-config.js (CommonJs to ES2015 Plugin)')(format='.') @@ -292,7 +293,7 @@ a#rollup-plugins *Minification* Rollup tree shaking reduces code size considerably. Minification makes it smaller still. - This cookbook relies on the _uglify_ Rollup plugin to minify and mangle the code. + This cookbook relies on the _uglify_ Rollup plugin to minify and mangle the code. Add the following to the `plugins` !{_array}: +makeExample('cb-aot-compiler/ts/rollup-config.js','uglify','rollup-config.js (CommonJs to ES2015 Plugin)')(format='.') @@ -398,14 +399,14 @@ code-example(language="none" class="code-shell"). :marked That compiles the app with JIT and launches the server. The server loads `index.html` which is still the AOT version, which you can confirm in the browser console. - Change the address bar to `index-jit.html` and it loads the JIT version. + Change the address bar to `index-jit.html` and it loads the JIT version. This is also evident in the browser console. Develop as usual. The server and TypeScript compiler are in "watch mode" so your changes are reflected immediately in the browser. To see those changes in AOT, switch to the original terminal and re-run `npm run build:aot`. - When it finishes, go back to the browser and use the back button to + When it finishes, go back to the browser and use the back button to return to the AOT version in the default `index.html`. Now you can develop JIT and AOT, side-by-side. diff --git a/public/docs/ts/latest/cookbook/dependency-injection.jade b/public/docs/ts/latest/cookbook/dependency-injection.jade index 0c64185a47..f0877df636 100644 --- a/public/docs/ts/latest/cookbook/dependency-injection.jade +++ b/public/docs/ts/latest/cookbook/dependency-injection.jade @@ -1,9 +1,9 @@ include ../_util-fns :marked - Dependency Injection is a powerful pattern for managing code dependencies. + Dependency Injection is a powerful pattern for managing code dependencies. In this cookbook we will explore many of the features of Dependency Injection (DI) in Angular. - + :marked ## Table of contents @@ -23,6 +23,7 @@ include ../_util-fns [Inject the component's DOM element](#component-element) [Define dependencies with providers](#providers) + * [The *provide* object literal](#provide) * [useValue - the *value provider*](#usevalue) * [useClass - the *class provider*](#useclass) @@ -30,38 +31,40 @@ include ../_util-fns * [useFactory - the *factory provider*](#usefactory) [Provider token alternatives](#tokens) + * [class-interface](#class-interface) * [OpaqueToken](#opaque-token) - + [Inject into a derived class](#di-inheritance) - + [Find a parent component by injection](#find-parent) + * [Find parent with a known component type](#known-parent) * [Cannot find a parent by its base class](#base-parent) * [Find a parent by its class-interface](#class-interface-parent) * [Find a parent in a tree of parents (*@SkipSelf*)](#parent-tree) * [A *provideParent* helper function](#provideparent) - + [Break circularities with a forward class reference (*forwardRef*)](#forwardref) - + :marked **See the ** - of the code supporting this cookbook. - + of the code supporting this cookbook. + .l-main-section :marked - ## Application-wide dependencies + ## Application-wide dependencies Register providers for dependencies used throughout the application in the root application component, `AppComponent`. - - In the following example, we import and register several services + + In the following example, we import and register several services (the `LoggerService`, `UserContext`, and the `UserService`) in the `@Component` metadata `providers` array. -+makeExample('cb-dependency-injection/ts/src/app/app.component.ts','import-services','src/app/app.component.ts (excerpt)')(format='.') ++makeExample('cb-dependency-injection/ts/src/app/app.component.ts','import-services','src/app/app.component.ts (excerpt)')(format='.') :marked - All of these services are implemented as classes. + All of these services are implemented as classes. Service classes can act as their own providers which is why listing them in the `providers` array is all the registration we need. .l-sub-section @@ -70,26 +73,26 @@ include ../_util-fns Angular creates a service instance from a class provider by "new-ing" it. Learn more about providers [below](#providers). :marked - Now that we've registered these services, + Now that we've registered these services, Angular can inject them into the constructor of *any* component or service, *anywhere* in the application. -+makeExample('cb-dependency-injection/ts/src/app/hero-bios.component.ts','ctor','src/app/hero-bios.component.ts (component constructor injection)')(format='.') ++makeExample('cb-dependency-injection/ts/src/app/hero-bios.component.ts','ctor','src/app/hero-bios.component.ts (component constructor injection)')(format='.') + ++makeExample('cb-dependency-injection/ts/src/app/user-context.service.ts','ctor','src/app/user-context.service.ts (service constructor injection)')(format='.') -+makeExample('cb-dependency-injection/ts/src/app/user-context.service.ts','ctor','src/app/user-context.service.ts (service constructor injection)')(format='.') - .l-main-section :marked ## External module configuration We often register providers in the `NgModule` rather than in the root application component. - + We do this when (a) we expect the service to be injectable everywhere or (b) we must configure another application global service _before it starts_. - + We see an example of the second case here, where we configure the Component Router with a non-default - [location strategy](../guide/router.html#location-strategy) by listing its provider + [location strategy](../guide/router.html#location-strategy) by listing its provider in the `providers` list of the `AppModule`. - -+makeExample('cb-dependency-injection/ts/src/app/app.module.ts','providers','src/app/app.module.ts (providers)')(format='.') + ++makeExample('cb-dependency-injection/ts/src/app/app.module.ts','providers','src/app/app.module.ts (providers)')(format='.') a(id="injectable") a(id="nested-dependencies") @@ -99,44 +102,44 @@ a(id="nested-dependencies") The consumer of an injected service does not know how to create that service. It shouldn't care. It's the dependency injection's job to create and cache that service. - + Sometimes a service depends on other services ... which may depend on yet other services. Resolving these nested dependencies in the correct order is also the framework's job. At each step, the consumer of dependencies simply declares what it requires in its constructor and the framework takes over. - - For example, we inject both the `LoggerService` and the `UserContext` in the `AppComponent`. -+makeExample('cb-dependency-injection/ts/src/app/app.component.ts','ctor','src/app/app.component.ts')(format='.') + + For example, we inject both the `LoggerService` and the `UserContext` in the `AppComponent`. ++makeExample('cb-dependency-injection/ts/src/app/app.component.ts','ctor','src/app/app.component.ts')(format='.') :marked - The `UserContext` in turn has dependencies on both the `LoggerService` (again) and + The `UserContext` in turn has dependencies on both the `LoggerService` (again) and a `UserService` that gathers information about a particular user. - + +makeExample('cb-dependency-injection/ts/src/app/user-context.service.ts','injectables','user-context.service.ts (injection)')(format='.') :marked - When Angular creates an`AppComponent`, the dependency injection framework creates an instance of the `LoggerService` and + When Angular creates an`AppComponent`, the dependency injection framework creates an instance of the `LoggerService` and starts to create the `UserContextService`. - The `UserContextService` needs the `LoggerService`, which the framework already has, and the `UserService`, which it has yet to create. + The `UserContextService` needs the `LoggerService`, which the framework already has, and the `UserService`, which it has yet to create. The `UserService` has no dependencies so the dependency injection framework can just `new` one into existence. - - The beauty of dependency injection is that the author of `AppComponent` didn't care about any of this. + + The beauty of dependency injection is that the author of `AppComponent` didn't care about any of this. The author simply declared what was needed in the constructor (`LoggerService` and `UserContextService`) and the framework did the rest. - + Once all the dependencies are in place, the `AppComponent` displays the user information: - + figure.image-display img(src="/resources/images/cookbooks/dependency-injection/logged-in-user.png" alt="Logged In User") :marked ### *@Injectable()* - Notice the `@Injectable()`decorator on the `UserContextService` class. + Notice the `@Injectable()`decorator on the `UserContextService` class. +makeExample('cb-dependency-injection/ts/src/app/user-context.service.ts','injectable','user-context.service.ts (@Injectable)')(format='.') :marked That decorator makes it possible for Angular to identify the types of its two dependencies, `LoggerService` and `UserService`. - + Technically, the `@Injectable()`decorator is only _required_ for a service class that has _its own dependencies_. The `LoggerService` doesn't depend on anything. The logger would work if we omitted `@Injectable()` - and the generated code would be slightly smaller. - + and the generated code would be slightly smaller. + But the service would break the moment we gave it a dependency and we'd have to go back and add `@Injectable()` to fix it. We add `@Injectable()` from the start for the sake of consistency and to avoid future pain. @@ -156,36 +159,36 @@ figure.image-display .l-main-section :marked ## Limit service scope to a component subtree - - All injected service dependencies are singletons meaning that, - for a given dependency injector ("injector"), there is only one instance of service. - + + All injected service dependencies are singletons meaning that, + for a given dependency injector ("injector"), there is only one instance of service. + But an Angular application has multiple dependency injectors, arranged in a tree hierarchy that parallels the component tree. So a particular service can be *provided* (and created) at any component level and multiple times if provided in multiple components. - - By default, a service dependency provided in one component is visible to all of its child components and + + By default, a service dependency provided in one component is visible to all of its child components and Angular injects the same service instance into all child components that ask for that service. - + Accordingly, dependencies provided in the root `AppComponent` can be injected into *any* component *anywhere* in the application. - - That isn't always desirable. + + That isn't always desirable. Sometimes we want to restrict service availability to a particular region of the application. - + We can limit the scope of an injected service to a *branch* of the application hierarchy by providing that service *at the sub-root component for that branch*. Here we provide the `HeroService` to the `HeroesBaseComponent` by listing it in the `providers` array: +makeExample('cb-dependency-injection/ts/src/app/sorted-heroes.component.ts','injection','src/app/sorted-heroes.component.ts (HeroesBaseComponent excerpt)') :marked - When Angular creates the `HeroesBaseComponent`, it also creates a new instance of `HeroService` + When Angular creates the `HeroesBaseComponent`, it also creates a new instance of `HeroService` that is visible only to the component and its children (if any). - - We could also provide the `HeroService` to a *different* component elsewhere in the application. + + We could also provide the `HeroService` to a *different* component elsewhere in the application. That would result in a *different* instance of the service, living in a *different* injector. .l-sub-section :marked - We examples of such scoped `HeroService` singletons appear throughout the accompanying sample code, - including the `HeroBiosComponent`, `HeroOfTheMonthComponent`, and `HeroesBaseComponent`. + We examples of such scoped `HeroService` singletons appear throughout the accompanying sample code, + including the `HeroBiosComponent`, `HeroOfTheMonthComponent`, and `HeroesBaseComponent`. Each of these components has its own `HeroService` instance managing its own independent collection of heroes. .l-main-section @@ -194,68 +197,68 @@ figure.image-display ### Take a break! This much Dependency Injection knowledge may be all that many Angular developers ever need to build their applications. It doesn't always have to be more complicated. - + .l-main-section :marked ## Multiple service instances (sandboxing) - + Sometimes we want multiple instances of a service at *the same level of the component hierarchy*. - - A good example is a service that holds state for its companion component instance. + + A good example is a service that holds state for its companion component instance. We need a separate instance of the service for each component. Each service has its own work-state, isolated from the service-and-state of a different component. We call this *sandboxing* because each service and component instance has its own sandbox to play in. - + - Imagine a `HeroBiosComponent` that presents three instances of the `HeroBioComponent`. + Imagine a `HeroBiosComponent` that presents three instances of the `HeroBioComponent`. +makeExample('cb-dependency-injection/ts/src/app/hero-bios.component.ts','simple','ap/hero-bios.component.ts') :marked - Each `HeroBioComponent` can edit a single hero's biography. + Each `HeroBioComponent` can edit a single hero's biography. A `HeroBioComponent` relies on a `HeroCacheService` to fetch, cache, and perform other persistence operations on that hero. -+makeExample('cb-dependency-injection/ts/src/app/hero-cache.service.ts','service','src/app/hero-cache.service.ts') ++makeExample('cb-dependency-injection/ts/src/app/hero-cache.service.ts','service','src/app/hero-cache.service.ts') :marked - Clearly the three instances of the `HeroBioComponent` can't share the same `HeroCacheService`. + Clearly the three instances of the `HeroBioComponent` can't share the same `HeroCacheService`. They'd be competing with each other to determine which hero to cache. - - Each `HeroBioComponent` gets its *own* `HeroCacheService` instance + + Each `HeroBioComponent` gets its *own* `HeroCacheService` instance by listing the `HeroCacheService` in its metadata `providers` array. -+makeExample('cb-dependency-injection/ts/src/app/hero-bio.component.ts','component','src/app/hero-bio.component.ts') ++makeExample('cb-dependency-injection/ts/src/app/hero-bio.component.ts','component','src/app/hero-bio.component.ts') :marked The parent `HeroBiosComponent` binds a value to the `heroId`. - The `ngOnInit` pass that `id` to the service which fetches and caches the hero. + The `ngOnInit` pass that `id` to the service which fetches and caches the hero. The getter for the `hero` property pulls the cached hero from the service. And the template displays this data-bound property. - + Find this example in live code - and confirm that the three `HeroBioComponent` instances have their own cached hero data. + and confirm that the three `HeroBioComponent` instances have their own cached hero data. figure.image-display - img(src="/resources/images/cookbooks/dependency-injection/hero-bios.png" alt="Bios") - + img(src="/resources/images/cookbooks/dependency-injection/hero-bios.png" alt="Bios") + a(id="optional") a(id="qualify-dependency-lookup") .l-main-section :marked ## Qualify dependency lookup with *@Optional* and *@Host* - We learned that dependencies can be registered at any level in the component hierarchy. - - When a component requests a dependency, Angular starts with that component's injector and walks up the injector tree - until it finds the first suitable provider. Angular throws an error if it can't find the dependency during that walk. - - We *want* this behavior most of the time. + We learned that dependencies can be registered at any level in the component hierarchy. + + When a component requests a dependency, Angular starts with that component's injector and walks up the injector tree + until it finds the first suitable provider. Angular throws an error if it can't find the dependency during that walk. + + We *want* this behavior most of the time. But sometimes we need to limit the search and/or accommodate a missing dependency. We can modify Angular's search behavior with the `@Host` and `@Optional` qualifying decorators, used individually or together. - - The `@Optional` decorator tells Angular to continue when it can't find the dependency. + + The `@Optional` decorator tells Angular to continue when it can't find the dependency. Angular sets the injection parameter to `null` instead. - - The `@Host` decorator stops the upward search at the *host component*. - - The host component is typically the component requesting the dependency. + + The `@Host` decorator stops the upward search at the *host component*. + + The host component is typically the component requesting the dependency. But when this component is projected into a *parent* component, that parent component becomes the host. We look at this second, more interesting case in our next example. - + ### Demonstration The `HeroBiosAndContactsComponent` is a revision of the `HeroBiosComponent` that we looked at [above](#hero-bios-component). +makeExample('cb-dependency-injection/ts/src/app/hero-bios.component.ts','hero-bios-and-contacts','src/app/hero-bios.component.ts (HeroBiosAndContactsComponent)') @@ -264,13 +267,13 @@ a(id="qualify-dependency-lookup") +makeExample('cb-dependency-injection/ts/src/app/hero-bios.component.ts','template')(format='.') :marked We've inserted a `` element between the `` tags. - Angular *projects* (*transcludes*) the corresponding `HeroContactComponent` into the `HeroBioComponent` view, + Angular *projects* (*transcludes*) the corresponding `HeroContactComponent` into the `HeroBioComponent` view, placing it in the `` slot of the `HeroBioComponent` template: +makeExample('cb-dependency-injection/ts/src/app/hero-bio.component.ts','template','src/app/hero-bio.component.ts (template)')(format='.') :marked It looks like this, with the hero's telephone number from `HeroContactComponent` projected above the hero description: figure.image-display - img(src="/resources/images/cookbooks/dependency-injection/hero-bio-and-content.png" alt="bio and contact") + img(src="/resources/images/cookbooks/dependency-injection/hero-bio-and-content.png" alt="bio and contact") :marked Here's the `HeroContactComponent` which demonstrates the qualifying decorators that we're talking about in this section: +makeExample('cb-dependency-injection/ts/src/app/hero-contact.component.ts','component','src/app/hero-contact.component.ts') @@ -278,17 +281,17 @@ figure.image-display Focus on the constructor parameters +makeExample('cb-dependency-injection/ts/src/app/hero-contact.component.ts','ctor-params','src/app/hero-contact.component.ts')(format='.') :marked - The `@Host()` function decorating the `heroCache` property ensures that + The `@Host()` function decorating the `heroCache` property ensures that we get a reference to the cache service from the parent `HeroBioComponent`. Angular throws if the parent lacks that service, even if a component higher in the component tree happens to have that service. - + A second `@Host()` function decorates the `loggerService` property. We know the only `LoggerService` instance in the app is provided at the `AppComponent` level. The host `HeroBioComponent` doesn't have its own `LoggerService` provider. - + Angular would throw an error if we hadn't also decorated the property with the `@Optional()` function. Thanks to `@Optional()`, Angular sets the `loggerService` to null and the rest of the component adapts. - + .l-sub-section :marked We'll come back to the `elementRef` property shortly. @@ -297,13 +300,13 @@ figure.image-display figure.image-display img(src="/resources/images/cookbooks/dependency-injection/hero-bios-and-contacts.png" alt="Bios with contact into") :marked - If we comment out the `@Host()` decorator, Angular now walks up the injector ancestor tree + If we comment out the `@Host()` decorator, Angular now walks up the injector ancestor tree until it finds the logger at the `AppComponent` level. The logger logic kicks in and the hero display updates with the gratuitous "!!!", indicating that the logger was found. figure.image-display - img(src="/resources/images/cookbooks/dependency-injection/hero-bio-contact-no-host.png" alt="Without @Host") + img(src="/resources/images/cookbooks/dependency-injection/hero-bio-contact-no-host.png" alt="Without @Host") :marked - On the other hand, if we restore the `@Host()` decorator and comment out `@Optional`, + On the other hand, if we restore the `@Host()` decorator and comment out `@Optional`, the application fails for lack of the required logger at the host component level.
`EXCEPTION: No provider for LoggerService! (HeroContactComponent -> LoggerService)` @@ -311,23 +314,23 @@ figure.image-display :marked ## Inject the component's element - - On occasion we might need to access a component's corresponding DOM element. + + On occasion we might need to access a component's corresponding DOM element. Although we strive to avoid it, many visual effects and 3rd party tools (such as jQuery) - require DOM access. - - To illustrate, we've written a simplified version of the `HighlightDirective` from + require DOM access. + + To illustrate, we've written a simplified version of the `HighlightDirective` from the [Attribute Directives](../guide/attribute-directives.html) chapter. +makeExample('cb-dependency-injection/ts/src/app/highlight.directive.ts','','src/app/highlight.directive.ts') :marked The directive sets the background to a highlight color when the user mouses over the DOM element to which it is applied. - - Angular set the constructor's `el` parameter to the injected `ElementRef` which is - a wrapper around that DOM element. + + Angular set the constructor's `el` parameter to the injected `ElementRef` which is + a wrapper around that DOM element. Its `nativeElement` property exposes the DOM element for the directive to manipulate. - - The sample code applies the directive's `myHighlight` attribute to two `
` tags, + + The sample code applies the directive's `myHighlight` attribute to two `
` tags, first without a value (yielding the default color) and then with an assigned color value. +makeExample('cb-dependency-injection/ts/src/app/app.component.html','highlight','src/app/app.component.html (highlight)')(format='.') :marked @@ -340,93 +343,93 @@ figure.image-display .l-main-section :marked ## Define dependencies with providers - + In this section we learn to write providers that deliver dependent services. - + ### Background - We get a service from a dependency injector by giving it a ***token***. - + We get a service from a dependency injector by giving it a ***token***. + We usually let Angular handle this transaction for us by specifying a constructor parameter and its type. - The parameter type serves as the injector lookup *token*. + The parameter type serves as the injector lookup *token*. Angular passes this token to the injector and assigns the result to the parameter. Here's a typical example: -+makeExample('cb-dependency-injection/ts/src/app/hero-bios.component.ts','ctor','src/app/hero-bios.component.ts (component constructor injection)')(format='.') ++makeExample('cb-dependency-injection/ts/src/app/hero-bios.component.ts','ctor','src/app/hero-bios.component.ts (component constructor injection)')(format='.') :marked Angular asks the injector for the service associated with the `LoggerService` and assigns the returned value to the `logger` parameter. - + Where did the injector get that value? - It may already have that value in its internal container. + It may already have that value in its internal container. If it doesn't, it may be able to make one with the help of a ***provider***. A *provider* is a recipe for delivering a service associated with a *token*. .l-sub-section :marked - If the injector doesn't have a provider for the requested *token*, it delegates the request - to its parent injector, where the process repeats until there are no more injectors. + If the injector doesn't have a provider for the requested *token*, it delegates the request + to its parent injector, where the process repeats until there are no more injectors. If the search is futile, the injector throws an error ... unless the request was [optional](#optional). - + Let's return our attention to providers themselves. :marked A new injector has no providers. Angular initializes the injectors it creates with some providers it cares about. - We have to register our _own_ application providers manually, + We have to register our _own_ application providers manually, usually in the `providers` array of the `Component` or `Directive` metadata: -+makeExample('cb-dependency-injection/ts/src/app/app.component.ts','providers','src/app/app.component.ts (providers)') ++makeExample('cb-dependency-injection/ts/src/app/app.component.ts','providers','src/app/app.component.ts (providers)') :marked ### Defining providers - + The simple class provider is the most typical by far. We mention the class in the `providers` array and we're done. -+makeExample('cb-dependency-injection/ts/src/app/hero-bios.component.ts','class-provider','src/app/hero-bios.component.ts (class provider)')(format='.') ++makeExample('cb-dependency-injection/ts/src/app/hero-bios.component.ts','class-provider','src/app/hero-bios.component.ts (class provider)')(format='.') :marked It's that simple because the most common injected service is an instance of a class. But not every dependency can be satisfied by creating a new instance of a class. We need other ways to deliver dependency values and that means we need other ways to specify a provider. - - The `HeroOfTheMonthComponent` example demonstrates many of the alternatives and why we need them. - + + The `HeroOfTheMonthComponent` example demonstrates many of the alternatives and why we need them. + figure.image-display img(src="/resources/images/cookbooks/dependency-injection/hero-of-month.png" alt="Hero of the month" width="300px") :marked It's visually simple: a few properties and the output of a logger. The code behind it gives us plenty to talk about. -+makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','hero-of-the-month','hero-of-the-month.component.ts') ++makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','hero-of-the-month','hero-of-the-month.component.ts') .l-main-section a(id='provide') :marked #### The *provide* object literal - + The `provide` object literal takes a *token* and a *definition object*. The *token* is usually a class but [it doesn't have to be](#tokens). - - The *definition* object has one main property, (e.g. `useValue`) that indicates how the provider + + The *definition* object has one main property, (e.g. `useValue`) that indicates how the provider should create or return the provided value. .l-main-section a(id='usevalue') :marked #### useValue - the *value provider* - + Set the `useValue` property to a ***fixed value*** that the provider can return as the dependency object. - + Use this technique to provide *runtime configuration constants* such as web-site base addresses and feature flags. We often use a *value provider* in a unit test to replace a production service with a fake or mock. - + The `HeroOfTheMonthComponent` example has two *value providers*. - The first provides an instance of the `Hero` class; + The first provides an instance of the `Hero` class; the second specifies a literal string resource: -+makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','use-value')(format='.') ++makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','use-value')(format='.') :marked The `Hero` provider token is a class which makes sense because the value is a `Hero` and the consumer of the injected hero would want the type information. - + The `TITLE` provider token is *not a class*. It's a special kind of provider lookup key called an [OpaqueToken](#opaquetoken). We often use an `OpaqueToken` when the dependency is a simple value like a string, a number, or a function. - - The value of a *value provider* must be defined *now*. We can't create the value later. - Obviously the title string literal is immediately available. + + The value of a *value provider* must be defined *now*. We can't create the value later. + Obviously the title string literal is immediately available. The `someHero` variable in this example was set earlier in the file: +makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','some-hero') :marked @@ -436,20 +439,20 @@ a(id='usevalue') a(id='useclass') :marked #### useClass - the *class provider* - + The `useClass` provider creates and returns new instance of the specified class. - + Use this technique to ***substitute an alternative implementation*** for a common or default class. The alternative could implement a different strategy, extend the default class, or fake the behavior of the real class in a test case. - + We see two examples in the `HeroOfTheMonthComponent`: -+makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','use-class')(format='.') ++makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','use-class')(format='.') :marked The first provider is the *de-sugared*, expanded form of the most typical case in which the - class to be created (`HeroService`) is also the provider's injection token. + class to be created (`HeroService`) is also the provider's injection token. We wrote it in this long form to de-mystify the preferred short form. - + The second provider substitutes the `DateLoggerService` for the `LoggerService`. The `LoggerService` is already registered at the `AppComponent` level. When _this component_ requests the `LoggerService`, it receives the `DateLoggerService` instead. @@ -458,15 +461,15 @@ a(id='useclass') This component and its tree of child components receive the `DateLoggerService` instance. Components outside the tree continue to receive the original `LoggerService` instance. :marked - The `DateLoggerService` inherits from `LoggerService`; it appends the current date/time to each message: -+makeExample('cb-dependency-injection/ts/src/app/date-logger.service.ts','date-logger-service','src/app/date-logger.service.ts')(format='.') + The `DateLoggerService` inherits from `LoggerService`; it appends the current date/time to each message: ++makeExample('cb-dependency-injection/ts/src/app/date-logger.service.ts','date-logger-service','src/app/date-logger.service.ts')(format='.') .l-main-section a(id='useexisting') :marked #### useExisting - the *alias provider* - - The `useExisting` provider maps one token to another. + + The `useExisting` provider maps one token to another. In effect, the first token is an ***alias*** for the service associated with second token, creating ***two ways to access the same service object***. +makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','use-existing') @@ -476,13 +479,13 @@ a(id='useexisting') Imagine that the `LoggerService` had a large API (it's actually only three methods and a property). We want to shrink that API surface to just the two members exposed by the `MinimalLogger` [*class-interface*](#class-interface): -+makeExample('cb-dependency-injection/ts/src/app/date-logger.service.ts','minimal-logger','src/app/date-logger.service.ts (MinimalLogger)')(format='.') ++makeExample('cb-dependency-injection/ts/src/app/date-logger.service.ts','minimal-logger','src/app/date-logger.service.ts (MinimalLogger)')(format='.') :marked The constructor's `logger` parameter is typed as `MinimalLogger` so only its two members are visible in TypeScript: figure.image-display img(src="/resources/images/cookbooks/dependency-injection/minimal-logger-intellisense.png" alt="MinimalLogger restricted API") :marked - Angular actually sets the `logger` parameter to the injector's full version of the `LoggerService` + Angular actually sets the `logger` parameter to the injector's full version of the `LoggerService` which happens to be the `DateLoggerService` thanks to the override provider registered previously via `useClass`. The following image, which displays the logging date, confirms the point: figure.image-display @@ -492,39 +495,39 @@ figure.image-display a(id='usefactory') :marked #### useFactory - the *factory provider* - + The `useFactory` provider creates a dependency object by calling a factory function as seen in this example. +makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','use-factory') :marked - Use this technique to ***create a dependency object*** + Use this technique to ***create a dependency object*** with a factory function whose inputs are some ***combination of injected services and local state***. - + The *dependency object* doesn't have to be a class instance. It could be anything. In this example, the *dependency object* is a string of the names of the runners-up to the "Hero of the Month" contest. The local state is the number `2`, the number of runners-up this component should show. - We execute `runnersUpFactory` immediately with `2`. - + We execute `runnersUpFactory` immediately with `2`. + The `runnersUpFactory` itself isn't the provider factory function. The true provider factory function is the function that `runnersUpFactory` returns. - -+makeExample('cb-dependency-injection/ts/src/app/runners-up.ts','factory-synopsis','runners-up.ts (excerpt)')(format='.') + ++makeExample('cb-dependency-injection/ts/src/app/runners-up.ts','factory-synopsis','runners-up.ts (excerpt)')(format='.') :marked That returned function takes a winning `Hero` and a `HeroService` as arguments. - - Angular supplies these arguments from injected values identified by - the two *tokens* in the `deps` array. + + Angular supplies these arguments from injected values identified by + the two *tokens* in the `deps` array. The two `deps` values are *tokens* that the injector uses to provide these factory function dependencies. - - After some undisclosed work, the function returns the string of names + + After some undisclosed work, the function returns the string of names and Angular injects it into the `runnersUp` parameter of the `HeroOfTheMonthComponent`. - + .l-sub-section :marked - The function retrieves candidate heroes from the `HeroService`, + The function retrieves candidate heroes from the `HeroService`, takes `2` of them to be the runners-up, and returns their concatenated names. Look at the for the full source code. @@ -536,34 +539,34 @@ a(id="tokens") Angular dependency injection is easiest when the provider *token* is a class that is also the type of the returned dependency object (what we usually call the *service*). - + But the token doesn't have to be a class and even when it is a class, it doesn't have to be the same type as the returned object. - That's the subject of our next section. - + That's the subject of our next section. + ### class-interface In the previous *Hero of the Month* example, we used the `MinimalLogger` class as the token for a provider of a `LoggerService`. +makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','use-existing') :marked - The `MinimalLogger` is an abstract class. -+makeExample('cb-dependency-injection/ts/src/app/date-logger.service.ts','minimal-logger')(format='.') + The `MinimalLogger` is an abstract class. ++makeExample('cb-dependency-injection/ts/src/app/date-logger.service.ts','minimal-logger')(format='.') :marked We usually inherit from an abstract class. But `LoggerService` doesn't inherit from `MinimalLogger`. *No class* inherits from it. Instead, we use it like an interface. - + Look again at the declaration for `DateLoggerService` -+makeExample('cb-dependency-injection/ts/src/app/date-logger.service.ts','date-logger-service-signature')(format='.') ++makeExample('cb-dependency-injection/ts/src/app/date-logger.service.ts','date-logger-service-signature')(format='.') :marked `DateLoggerService` inherits (extends) from `LoggerService`, not `MinimalLogger`. The `DateLoggerService` *implements* `MinimalLogger` as if `MinimalLogger` were an *interface*. - + We call a class used in this way a ***class-interface***. The key benefit of a *class-interface* is that we can get the strong-typing of an interface and we can ***use it as a provider token*** in the same manner as a normal class. - + A ***class-interface*** should define *only* the members that its consumers are allowed to call. Such a narrowing interface helps decouple the concrete class from its consumers. The `MinimalLogger` defines just two of the `LoggerClass` members. @@ -571,17 +574,17 @@ a(id="tokens") .l-sub-section :marked #### Why *MinimalLogger* is a class and not an interface - We can't use an interface as a provider token because - interfaces are not JavaScript objects. - They exist only in the TypeScript design space. + We can't use an interface as a provider token because + interfaces are not JavaScript objects. + They exist only in the TypeScript design space. They disappear after the code is transpiled to JavaScript. - - A provider token must be a real JavaScript object of some kind: + + A provider token must be a real JavaScript object of some kind: a function, an object, a string ... a class. - + Using a class as an interface gives us the characteristics of an interface in a JavaScript object. - - The minimize memory cost, the class should have *no implementation*. + + The minimize memory cost, the class should have *no implementation*. The `MinimalLogger` transpiles to this unoptimized, pre-minified JavaScript: +makeExample('cb-dependency-injection/ts/src/app/date-logger.service.ts','minimal-logger-transpiled')(format='.') :marked @@ -590,50 +593,50 @@ a(id="tokens") a(id='opaque-token') :marked ### OpaqueToken - - Dependency objects can be simple values like dates, numbers and strings or + + Dependency objects can be simple values like dates, numbers and strings or shapeless objects like arrays and functions. - + Such objects don't have application interfaces and therefore aren't well represented by a class. - They're better represented by a token that is both unique and symbolic, - a JavaScript object that has a friendly name but won't conflict with + They're better represented by a token that is both unique and symbolic, + a JavaScript object that has a friendly name but won't conflict with another token that happens to have the same name. - + The `OpaqueToken` has these characteristics. - We encountered them twice in the *Hero of the Month* example, + We encountered them twice in the *Hero of the Month* example, in the *title* value provider and in the *runnersUp* factory provider. -+makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','provide-opaque-token')(format='.') ++makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','provide-opaque-token')(format='.') :marked We created the `TITLE` token like this: -+makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','opaque-token')(format='.') ++makeExample('cb-dependency-injection/ts/src/app/hero-of-the-month.component.ts','opaque-token')(format='.') + - a(id="di-inheritance") .l-main-section :marked ## Inject into a derived class We must take care when writing a component that inherits from another component. - If the base component has injected dependencies, + If the base component has injected dependencies, we must re-provide and re-inject them in the derived class and then pass them down to the base class through the constructor. - - In this contrived example, `SortedHeroesComponent` inherits from `HeroesBaseComponent` + + In this contrived example, `SortedHeroesComponent` inherits from `HeroesBaseComponent` to display a *sorted* list of heroes. - + figure.image-display img(src="/resources/images/cookbooks/dependency-injection/sorted-heroes.png" alt="Sorted Heroes") :marked The `HeroesBaseComponent` could stand on its own. It demands its own instance of the `HeroService` to get heroes and displays them in the order they arrive from the database. - -+makeExample('cb-dependency-injection/ts/src/app/sorted-heroes.component.ts','heroes-base','src/app/sorted-heroes.component.ts (HeroesBaseComponent)') + ++makeExample('cb-dependency-injection/ts/src/app/sorted-heroes.component.ts','heroes-base','src/app/sorted-heroes.component.ts (HeroesBaseComponent)') .l-sub-section :marked We strongly prefer simple constructors. They should do little more than initialize variables. This rule makes the component safe to construct under test without fear that it will do something dramatic like talk to the server. That's why we call the `HeroService` from within the `ngOnInit` rather than the constructor. - + We explain the mysterious `afterGetHeroes` below. :marked Users want to see the heroes in alphabetical order. @@ -641,50 +644,50 @@ figure.image-display `SortedHeroesComponent` that sorts the heroes before presenting them. The `SortedHeroesComponent` lets the base class fetch the heroes. (we said it was contrived). - + Unfortunately, Angular cannot inject the `HeroService` directly into the base class. - We must provide the `HeroService` again for *this* component, + We must provide the `HeroService` again for *this* component, then pass it down to the base class inside the constructor. - -+makeExample('cb-dependency-injection/ts/src/app/sorted-heroes.component.ts','sorted-heroes','src/app/sorted-heroes.component.ts (SortedHeroesComponent)') + ++makeExample('cb-dependency-injection/ts/src/app/sorted-heroes.component.ts','sorted-heroes','src/app/sorted-heroes.component.ts (SortedHeroesComponent)') :marked - Now take note of the `afterGetHeroes` method. + Now take note of the `afterGetHeroes` method. Our first instinct was to create an `ngOnInit` method in `SortedHeroesComponent` and do the sorting there. - But Angular calls the *derived* class's `ngOnInit` *before* calling the base class's `ngOnInit` + But Angular calls the *derived* class's `ngOnInit` *before* calling the base class's `ngOnInit` so we'd be sorting the heroes array *before they arrived*. That produces a nasty error. - + Overriding the base class's `afterGetHeroes` method solves the problem - - These complications argue for *avoiding component inheritance*. + + These complications argue for *avoiding component inheritance*. a(id="find-parent") .l-main-section :marked ## Find a parent component by injection - + Application components often need to share information. We prefer the more loosely coupled techniques such as data binding and service sharing. But sometimes it makes sense for one component to have a direct reference to another component perhaps to access values or call methods on that component. - + Obtaining a component reference is a bit tricky in Angular. Although an Angular application is a tree of components, - there is no public API for inspecting and traversing that tree. - + there is no public API for inspecting and traversing that tree. + There is an API for acquiring a child reference (checkout `Query`, `QueryList`, `ViewChildren`, and `ContentChildren`). - + There is no public API for acquiring a parent reference. But because every component instance is added to an injector's container, we can use Angular dependency injection to reach a parent component. - + This section describes some techniques for doing that. - + ### Find a parent component of known type - + We use standard class injection to acquire a parent component whose type we know. - + In the following example, the parent `AlexComponent` has several children including a `CathyComponent`: a(id='alex') +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','alex-1','parent-finder.component.ts (AlexComponent v.1)')(format='.') @@ -699,47 +702,47 @@ a(id='alex') ### Cannot find a parent by its base class - + What if we do *not* know the concrete parent component class? - + A re-usable component might be a child of multiple components. Imagine a component for rendering breaking news about a financial instrument. - For sound (cough) business reasons, this news component makes frequent calls + For sound (cough) business reasons, this news component makes frequent calls directly into its parent instrument as changing market data stream by. - + The app probably defines more than a dozen financial instrument components. If we're lucky, they all implement the same base class whose API our `NewsComponent` understands. - + .l-sub-section :marked Looking for components that implement an interface would be better. That's not possible because TypeScript interfaces disappear from the transpiled JavaScript which doesn't support interfaces. There's no artifact we could look for. :marked - We're not claiming this is good design. + We're not claiming this is good design. We are asking *can a component inject its parent via the parent's base class*? - - The sample's `CraigComponent` explores this question. [Looking back](#alex) + + The sample's `CraigComponent` explores this question. [Looking back](#alex) we see that the `Alex` component *extends* (*inherits*) from a class named `Base`. +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','alex-class-signature','parent-finder.component.ts (Alex class signature)')(format='.') :marked The `CraigComponent` tries to inject `Base` into its `alex` constructor parameter and reports if it succeeded. +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','craig','parent-finder.component.ts (CraigComponent)')(format='.') :marked - Unfortunately, this does not work. + Unfortunately, this does not work. The confirms that the `alex` parameter is null. *We cannot inject a parent by its base class.* - + ### Find a parent by its class-interface - + We can find a parent component with a [class-interface](#class-interface). - The parent must cooperate by providing an *alias* to itself in the name of a *class-interface* token. + The parent must cooperate by providing an *alias* to itself in the name of a *class-interface* token. - Recall that Angular always adds a component instance to its own injector; + Recall that Angular always adds a component instance to its own injector; that's why we could inject *Alex* into *Cathy* [earlier](#known-parent). We write an [*alias provider*](#useexisting) — a `provide` object literal with a `useExisting` definition — @@ -748,7 +751,7 @@ a(id='alex') a(id="alex-providers") +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','alex-providers','parent-finder.component.ts (AlexComponent providers)')(format='.') :marked - [Parent](#parent-token) is the provider's *class-interface* token. + [Parent](#parent-token) is the provider's *class-interface* token. The [*forwardRef*](#forwardref) breaks the circular reference we just created by having the `AlexComponent` refer to itself. *Carol*, the third of *Alex*'s child components, injects the parent into its `parent` parameter, the same way we've done it before: @@ -757,24 +760,24 @@ a(id="alex-providers") Here's *Alex* and family in action: figure.image-display img(src="/resources/images/cookbooks/dependency-injection/alex.png" alt="Alex in action") - + a(id="parent-tree") :marked ### Find the parent in a tree of parents - - Imagine one branch of a component hierarchy: *Alice* -> *Barry* -> *Carol*. + + Imagine one branch of a component hierarchy: *Alice* -> *Barry* -> *Carol*. Both *Alice* and *Barry* implement the `Parent` *class-interface*. - + *Barry* is the problem. He needs to reach his parent, *Alice*, and also be a parent to *Carol*. That means he must both *inject* the `Parent` *class-interface* to get *Alice* and *provide* a `Parent` to satisfy *Carol*. - + Here's *Barry*: +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','barry','parent-finder.component.ts (BarryComponent)')(format='.') :marked *Barry*'s `providers` array looks just like [*Alex*'s](#alex-providers). If we're going to keep writing [*alias providers*](#useexisting) like this we should create a [helper function](#provideparent). - + For now, focus on *Barry*'s constructor: +makeTabs( 'cb-dependency-injection/ts/src/app/parent-finder.component.ts, cb-dependency-injection/ts/src/app/parent-finder.component.ts', @@ -783,21 +786,21 @@ a(id="parent-tree") :marked :marked It's identical to *Carol*'s constructor except for the additional `@SkipSelf` decorator. - + `@SkipSelf` is essential for two reasons: - + 1. It tells the injector to start its search for a `Parent` dependency in a component *above* itself, which *is* what parent means. - + 2. Angular throws a cyclic dependency error if we omit the `@SkipSelf` decorator. - + `Cannot instantiate cyclic dependency! (BethComponent -> Parent -> BethComponent)` Here's *Alice*, *Barry* and family in action: figure.image-display img(src="/resources/images/cookbooks/dependency-injection/alice.png" alt="Alice in action") - + a(id="parent-token") :marked ### The *Parent* class-interface @@ -806,27 +809,27 @@ a(id="parent-token") Our example defines a `Parent` *class-interface* . +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','parent','parent-finder.component.ts (Parent class-interface)')(format='.') :marked - The `Parent` *class-interface* defines a `name` property with a type declaration but *no implementation*., + The `Parent` *class-interface* defines a `name` property with a type declaration but *no implementation*., The `name` property is the only member of a parent component that a child component can call. Such a narrowing interface helps decouple the child component class from its parent components. - + A component that could serve as a parent *should* implement the *class-interface* as the `AliceComponent` does: +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','alice-class-signature','parent-finder.component.ts (AliceComponent class signature)')(format='.') :marked - Doing so adds clarity to the code. But it's not technically necessary. - Although the `AlexComponent` has a `name` property (as required by its `Base` class) + Doing so adds clarity to the code. But it's not technically necessary. + Although the `AlexComponent` has a `name` property (as required by its `Base` class) its class signature doesn't mention `Parent`: +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','alex-class-signature','parent-finder.component.ts (AlexComponent class signature)')(format='.') .l-sub-section :marked - The `AlexComponent` *should* implement `Parent` as a matter of proper style. - It doesn't in this example *only* to demonstrate that the code will compile and run without the interface - + The `AlexComponent` *should* implement `Parent` as a matter of proper style. + It doesn't in this example *only* to demonstrate that the code will compile and run without the interface + a(id="provideparent") :marked ### A *provideParent* helper function - - Writing variations of the same parent *alias provider* gets old quickly, + + Writing variations of the same parent *alias provider* gets old quickly, especially this awful mouthful with a [*forwardRef*](#forwardref): +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','alex-providers')(format='.') :marked @@ -838,7 +841,7 @@ a(id="provideparent") :marked We can do better. The current version of the helper function can only alias the `Parent` *class-interface*. Our application might have a variety of parent types, each with its own *class-interface* token. - + Here's a revised version that defaults to `parent` but also accepts an optional second parameter for a different parent *class-interface*. +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','provide-parent')(format='.') :marked @@ -850,25 +853,25 @@ a(id="forwardref") .l-main-section :marked ## Break circularities with a forward class reference (*forwardRef*) - + The order of class declaration matters in TypeScript. We can't refer directly to a class until it's been defined. - + This isn't usually a problem, especially if we adhere to the recommended *one class per file* rule. - But sometimes circular references are unavoidable. + But sometimes circular references are unavoidable. We're in a bind when class 'A refers to class 'B' and 'B' refers to 'A'. - One of them has to be defined first. - + One of them has to be defined first. + The Angular `forwardRef` function creates an *indirect* reference that Angular can resolve later. - + The *Parent Finder* sample is full of circular class references that are impossible to break. - + :marked We face this dilemma when a class makes *a reference to itself* - as does the `AlexComponent` in its `providers` array. + as does the `AlexComponent` in its `providers` array. The `providers` array is a property of the `@Component` decorator function which must appear *above* the class definition. - + We break the circularity with `forwardRef`: +makeExample('cb-dependency-injection/ts/src/app/parent-finder.component.ts','alex-providers','parent-finder.component.ts (AlexComponent providers)')(format='.') :marked diff --git a/public/docs/ts/latest/cookbook/form-validation.jade b/public/docs/ts/latest/cookbook/form-validation.jade index efe33f75af..3943968098 100644 --- a/public/docs/ts/latest/cookbook/form-validation.jade +++ b/public/docs/ts/latest/cookbook/form-validation.jade @@ -4,11 +4,11 @@ a#top :marked Improve overall data quality by validating user input for accuracy and completeness. - This cookbook shows how to validate user input in the UI and display useful validation messages + This cookbook shows how to validate user input in the UI and display useful validation messages using first the template-driven forms and then the reactive forms approach. .l-sub-section :marked - Read more about these choices in the [Forms](../guide/forms.html) + Read more about these choices in the [Forms](../guide/forms.html) and the [Reactive Forms](../guide/reactive-forms.html) guides. a#toc @@ -40,20 +40,20 @@ a#template1 :marked ## Simple template-driven forms - In the template-driven approach, you arrange + In the template-driven approach, you arrange [form elements](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Forms_in_HTML) in the component's template. You add Angular form directives (mostly directives beginning `ng...`) to help Angular construct a corresponding internal control model that implements form functionality. In template-drive forms, the control model is _implicit_ in the template. - To validate user input, you add [HTML validation attributes](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation) + To validate user input, you add [HTML validation attributes](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation) to the elements. Angular interprets those as well, adding validator functions to the control model. - Angular exposes information about the state of the controls including + Angular exposes information about the state of the controls including whether the user has "touched" the control or made changes and if the control values are valid. - In this first template validation example, + In this first template validation example, notice the HTML that reads the control state and updates the display appropriately. Here's an excerpt from the template HTML for a single input control bound to the hero name: +makeExample('cb-form-validation/ts/src/app/template/hero-form-template1.component.html','name-with-error-msg','template/hero-form-template1.component.html (Hero name)')(format='.') @@ -66,9 +66,9 @@ a#template1 with an Angular form control called `name` in its internal control model. - The `[(ngModel)]` directive allows two-way data binding between the input box to the `hero.name` property. - + - The template variable (`#name`) has the value `"ngModel"` (always `ngModel`). - This gives you a reference to the Angular `NgModel` directive + This gives you a reference to the Angular `NgModel` directive associated with this control that you can use _in the template_ to check for control states such as `valid` and `dirty`. @@ -102,7 +102,7 @@ a#why-check +makeTabs( `cb-form-validation/ts/src/app/template/hero-form-template1.component.html, cb-form-validation/ts/src/app/template/hero-form-template1.component.ts`, - '', + '', `template/hero-form-template1.component.html, template/hero-form-template1.component.ts`) @@ -111,24 +111,24 @@ a#template2 :marked ## Template-driven forms with validation messages in code - While the layout is straightforward, + While the layout is straightforward, there are obvious shortcomings with the way it's handling validation messages: - * It takes a lot of HTML to represent all possible error conditions. + * It takes a lot of HTML to represent all possible error conditions. This gets out of hand when there are many controls and many validation rules. - + * There's a lot of JavaScript logic in the HTML. - * The messages are static strings, hard-coded into the template. + * The messages are static strings, hard-coded into the template. It's easier to maintain _dynamic_ messages in the component class. - In this example, you can move the logic and the messages into the component with a few changes to + In this example, you can move the logic and the messages into the component with a few changes to the template and component. - - Here's the hero name again, excerpted from the revised template + + Here's the hero name again, excerpted from the revised template (Template 2), next to the original version: +makeTabs( - `cb-form-validation/ts/src/app/template/hero-form-template2.component.html, + `cb-form-validation/ts/src/app/template/hero-form-template2.component.html, cb-form-validation/ts/src/app/template/hero-form-template1.component.html`, 'name-with-error-msg, name-with-error-msg', `hero-form-template2.component.html (name #2), @@ -140,24 +140,24 @@ a#template2 - There's a new attribute, `forbiddenName`, that is actually a custom validation directive. It invalidates the control if the user enters "bob" in the name ``([try it](#live-example)). - See the [custom validation](#custom-validation) section later in this cookbook for more information + See the [custom validation](#custom-validation) section later in this cookbook for more information on custom validation directives. - The `#name` template variable is gone because the app no longer refers to the Angular control for this element. - Binding to the new `formErrors.name` property is sufficent to display all name validation error messages. -a#component-class +a#component-class :marked ### Component class - The original component code for Template 1 stayed the same; however, - Template 2 requires some changes in the component. This section covers the code - necessary in Template 2's component class to acquire the Angular + The original component code for Template 1 stayed the same; however, + Template 2 requires some changes in the component. This section covers the code + necessary in Template 2's component class to acquire the Angular form control and compose error messages. The first step is to acquire the form control that Angular created from the template by querying for it. - Look back at the top of the component template at the + Look back at the top of the component template at the `#heroForm` template variable in the `
` element: +makeExample('cb-form-validation/ts/src/app/template/hero-form-template1.component.html','form-tag','template/hero-form-template1.component.html (form tag)')(format='.') @@ -169,22 +169,22 @@ a#component-class :marked Some observations: - - Angular `@ViewChild` queries for a template variable when you pass it + - Angular `@ViewChild` queries for a template variable when you pass it the name of that variable as a string (`'heroForm'` in this case). - The `heroForm` object changes several times during the life of the component, most notably when you add a new hero. Periodically inspecting it reveals these changes. - - Angular calls the `ngAfterViewChecked` [lifecycle hook method](../guide/lifecycle-hooks.html#afterview) + - Angular calls the `ngAfterViewChecked` [lifecycle hook method](../guide/lifecycle-hooks.html#afterview) when anything changes in the view. That's the right time to see if there's a new `heroForm` object. - + - When there _is_ a new `heroForm` model, `formChanged()` subscribes to its `valueChanges` _Observable_ property. - The `onValueChanged` handler looks for validation errors after every keystroke. + The `onValueChanged` handler looks for validation errors after every keystroke. +makeExample('cb-form-validation/ts/src/app/template/hero-form-template2.component.ts','handler','template/hero-form-template2.component.ts (handler)')(format='.') :marked - The `onValueChanged` handler interprets user data entry. + The `onValueChanged` handler interprets user data entry. The `data` object passed into the handler contains the current element values. The handler ignores them. Instead, it iterates over the fields of the component's `formErrors` object. @@ -194,11 +194,11 @@ a#component-class For each field, the `onValueChanged` handler does the following: - Clears the prior error message, if any. - - Acquires the field's corresponding Angular form control. - - If such a control exists _and_ it's been changed ("dirty") + - Acquires the field's corresponding Angular form control. + - If such a control exists _and_ it's been changed ("dirty") _and_ it's invalid, the handler composes a consolidated error message for all of the control's errors. - Next, the component needs some error messages of course—a set for each validated property with + Next, the component needs some error messages of course—a set for each validated property with one message per validation rule: +makeExample('cb-form-validation/ts/src/app/template/hero-form-template2.component.ts','messages','template/hero-form-template2.component.ts (messages)')(format='.') :marked @@ -207,25 +207,25 @@ a#component-class a#improvement :marked ### The benefits of messages in code - - Clearly the template got substantially smaller while the component code got substantially larger. + + Clearly the template got substantially smaller while the component code got substantially larger. It's not easy to see the benefit when there are just three fields and only two of them have validation rules. - Consider what happens as the number of validated + Consider what happens as the number of validated fields and rules increases. - In general, HTML is harder to read and maintain than code. - The initial template was already large and threatening to get rapidly worse + In general, HTML is harder to read and maintain than code. + The initial template was already large and threatening to get rapidly worse with the addition of more validation message `
` elements. - - After moving the validation messaging to the component, + + After moving the validation messaging to the component, the template grows more slowly and proportionally. Each field has approximately the same number of lines no matter its number of validation rules. The component also grows proportionally, at the rate of one line per validated field and one line per validation message. - + Both trends are manageable. - Now that the messages are in code, you have more flexibility and can compose messages more efficiently. + Now that the messages are in code, you have more flexibility and can compose messages more efficiently. You can refactor the messages out of the component, perhaps to a service class that retrieves them from the server. In short, there are more opportunities to improve message handling now that text and logic have moved from template to code. @@ -233,9 +233,9 @@ a#formmodule :marked ### _FormModule_ and template-driven forms - Angular has two different forms modules—`FormsModule` and - `ReactiveFormsModule`—that correspond with the - two approaches to form development. Both modules come + Angular has two different forms modules—`FormsModule` and + `ReactiveFormsModule`—that correspond with the + two approaches to form development. Both modules come from the same `@angular/forms` library package. You've been reviewing the "Template-driven" approach which requires the `FormsModule`. @@ -245,8 +245,8 @@ a#formmodule .l-sub-section :marked This guide hasn't talked about the `SharedModule` or its `SubmittedComponent` which appears at the bottom of every - form template in this cookbook. - + form template in this cookbook. + They're not germane to the validation story. Look at the [live example](#live-example) if you're interested. .l-main-section @@ -254,11 +254,11 @@ a#reactive :marked ## Reactive forms with validation in code - In the template-driven approach, you markup the template with form elements, validation attributes, + In the template-driven approach, you markup the template with form elements, validation attributes, and `ng...` directives from the Angular `FormsModule`. At runtime, Angular interprets the template and derives its _form control model_. - - **Reactive Forms** takes a different approach. + + **Reactive Forms** takes a different approach. You create the form control model in code. You write the template with form elements and `form...` directives from the Angular `ReactiveFormsModule`. At runtime, Angular binds the template elements to your control model based on your instructions. @@ -279,7 +279,7 @@ a#reactive-forms-module The application module for the reactive forms feature in this sample looks like this: +makeExample('cb-form-validation/ts/src/app/reactive/hero-form-reactive.module.ts','','src/app/reactive/hero-form-reactive.module.ts')(format='.') :marked - The reactive forms feature module and component are in the `src/app/reactive` folder. + The reactive forms feature module and component are in the `src/app/reactive` folder. Focus on the `HeroFormReactiveComponent` there, starting with its template. a#reactive-component-template @@ -287,7 +287,7 @@ a#reactive-component-template ### Component template Begin by changing the `` tag so that it binds the Angular `formGroup` directive in the template - to the `heroForm` property in the component class. + to the `heroForm` property in the component class. The `heroForm` is the control model that the component class builds and maintains. +makeExample('cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.html','form-tag')(format='.') @@ -295,7 +295,7 @@ a#reactive-component-template Next, modify the template HTML elements to match the _reactive forms_ style. Here is the "name" portion of the template again, revised for reactive forms and compared with the template-driven version: +makeTabs( - `cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.html, + `cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.html, cb-form-validation/ts/src/app/template/hero-form-template2.component.html`, 'name-with-error-msg, name-with-error-msg', `hero-form-reactive.component.html (name #3), @@ -303,16 +303,16 @@ a#reactive-component-template :marked Key changes are: - - The validation attributes are gone (except `required`) because + - The validation attributes are gone (except `required`) because validating happens in code. - - `required` remains, not for validation purposes (that's in the code), + - `required` remains, not for validation purposes (that's in the code), but rather for css styling and accessibility. .l-sub-section :marked A future version of reactive forms will add the `required` HTML validation attribute to the DOM element - (and perhaps the `aria-required` attribute) when the control has the `required` validator function. + (and perhaps the `aria-required` attribute) when the control has the `required` validator function. Until then, apply the `required` attribute _and_ add the `Validator.required` function to the control model, as you'll see below. @@ -321,7 +321,7 @@ a#reactive-component-template - The `formControlName` replaces the `name` attribute; it serves the same purpose of correlating the input with the Angular form control. - - The two-way `[(ngModel)]` binding is gone. + - The two-way `[(ngModel)]` binding is gone. The reactive approach does not use data binding to move data into and out of the form controls. That's all in code. @@ -332,10 +332,10 @@ a#reactive-component-class :marked ### Component class - The component class is now responsible for defining and managing the form control model. - + The component class is now responsible for defining and managing the form control model. + Angular no longer derives the control model from the template so you can no longer query for it. - You can create the Angular form control model explicitly with + You can create the Angular form control model explicitly with the help of the `FormBuilder` class. Here's the section of code devoted to that process, paired with the template-driven code it replaces: @@ -355,35 +355,35 @@ a#reactive-component-class A real app would retrieve the hero asynchronously from a data service, a task best performed in the `ngOnInit` hook. :marked - The `buildForm` method uses the `FormBuilder`, `fb`, to declare the form control model. - Then it attaches the same `onValueChanged` handler (there's a one line difference) - to the form's `valueChanges` event and calls it immediately + Then it attaches the same `onValueChanged` handler (there's a one line difference) + to the form's `valueChanges` event and calls it immediately to set error messages for the new control model. a#formbuilder :marked #### _FormBuilder_ declaration - The `FormBuilder` declaration object specifies the three controls of the sample's hero form. + The `FormBuilder` declaration object specifies the three controls of the sample's hero form. - Each control spec is a control name with an array value. + Each control spec is a control name with an array value. The first array element is the current value of the corresponding hero field. The optional second value is a validator function or an array of validator functions. - + Most of the validator functions are stock validators provided by Angular as static methods of the `Validators` class. Angular has stock validators that correspond to the standard HTML validation attributes. - The `forbiddenNames` validator on the `"name"` control is a custom validator, + The `forbiddenNames` validator on the `"name"` control is a custom validator, discussed in a separate [section below](#custom-validation). -.l-sub-section +.l-sub-section :marked - Learn more about `FormBuilder` in the [Introduction to FormBuilder](../guide/reactive-forms.html#formbuilder) section of Reactive Forms guide. + Learn more about `FormBuilder` in the [Introduction to FormBuilder](../guide/reactive-forms.html#formbuilder) section of Reactive Forms guide. a#committing-changes :marked #### Committing hero value changes - + In two-way data binding, the user's changes flow automatically from the controls back to the data model properties. - Reactive forms do not use data binding to update data model properties. + Reactive forms do not use data binding to update data model properties. The developer decides _when and how_ to update the data model from control values. This sample updates the model twice: @@ -406,7 +406,7 @@ a#committing-changes Here's the complete reactive component file, compared to the two template-driven component files. +makeTabs( `cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.ts, - cb-form-validation/ts/src/app/template/hero-form-template2.component.ts, + cb-form-validation/ts/src/app/template/hero-form-template2.component.ts, cb-form-validation/ts/src/app/template/hero-form-template1.component.ts`, '', `reactive/hero-form-reactive.component.ts (#3), @@ -422,7 +422,7 @@ a#committing-changes a#custom-validation :marked ## Custom validation - This cookbook sample has a custom `forbiddenNamevalidator()` function that's applied to both the + This cookbook sample has a custom `forbiddenNamevalidator()` function that's applied to both the template-driven and the reactive form controls. It's in the `src/app/shared` folder and declared in the `SharedModule`. @@ -432,10 +432,10 @@ a#custom-validation The function is actually a factory that takes a regular expression to detect a _specific_ forbidden name and returns a validator function. - In this sample, the forbidden name is "bob"; + In this sample, the forbidden name is "bob"; the validator rejects any hero name containing "bob". Elsewhere it could reject "alice" or any name that the configuring regular expression matches. - + The `forbiddenNameValidator` factory returns the configured validator function. That function takes an Angular control object and returns _either_ null if the control value is valid _or_ a validation error object. @@ -445,7 +445,7 @@ a#custom-validation a#custom-validation-directive :marked ### Custom validation directive - In the reactive forms component, the `'name'` control's validator function list + In the reactive forms component, the `'name'` control's validator function list has a `forbiddenNameValidator` at the bottom. +makeExample('cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.ts','name-validators', 'reactive/hero-form-reactive.component.ts (name validators)')(format='.') :marked @@ -454,7 +454,7 @@ a#custom-validation-directive +makeExample('cb-form-validation/ts/src/app/template/hero-form-template2.component.html','name-input', 'template/hero-form-template2.component.html (name input)')(format='.') :marked The corresponding `ForbiddenValidatorDirective` is a wrapper around the `forbiddenNameValidator`. - + Angular `forms` recognizes the directive's role in the validation process because the directive registers itself with the `NG_VALIDATORS` provider, a provider with an extensible collection of validation directives. +makeExample('cb-form-validation/ts/src/app/shared/forbidden-name.directive.ts','directive-providers', 'shared/forbidden-name.directive.ts (providers)')(format='.') @@ -465,22 +465,22 @@ a#custom-validation-directive :marked .l-sub-section :marked - If you are familiar with Angular validations, you may have noticed - that the custom validation directive is instantiated with `useExisting` - rather than `useClass`. The registered validator must be _this instance_ of - the `ForbiddenValidatorDirective`—the instance in the form with - its `forbiddenName` property bound to “bob". If you were to replace - `useExisting` with `useClass`, then you’d be registering a new class instance, one that + If you are familiar with Angular validations, you may have noticed + that the custom validation directive is instantiated with `useExisting` + rather than `useClass`. The registered validator must be _this instance_ of + the `ForbiddenValidatorDirective`—the instance in the form with + its `forbiddenName` property bound to “bob". If you were to replace + `useExisting` with `useClass`, then you’d be registering a new class instance, one that doesn’t have a `forbiddenName`. - - To see this in action, run the example and then type “bob” in the name of Hero Form 2. - Notice that you get a validation error. Now change from `useExisting` to `useClass` and try again. + + To see this in action, run the example and then type “bob” in the name of Hero Form 2. + Notice that you get a validation error. Now change from `useExisting` to `useClass` and try again. This time, when you type “bob”, there's no "bob" error message. :marked .l-sub-section :marked - For more information on attaching behavior to elements, + For more information on attaching behavior to elements, see [Attribute Directives](../guide/attribute-directives.html). .l-main-section @@ -497,11 +497,11 @@ a#testing They do not require the `Angular TestBed` or asynchronous testing practices. That's not possible with _template-driven_ forms. - The template-driven approach relies on Angular to produce the control model and + The template-driven approach relies on Angular to produce the control model and to derive validation rules from the HTML validation attributes. You must use the `Angular TestBed` to create component test instances, write asynchronous tests, and interact with the DOM. - While not difficult, this takes more time, work and - skill—factors that tend to diminish test code + While not difficult, this takes more time, work and + skill—factors that tend to diminish test code coverage and quality. diff --git a/public/docs/ts/latest/cookbook/ngmodule-faq.jade b/public/docs/ts/latest/cookbook/ngmodule-faq.jade index 29b24fb03a..cf19160d4e 100644 --- a/public/docs/ts/latest/cookbook/ngmodule-faq.jade +++ b/public/docs/ts/latest/cookbook/ngmodule-faq.jade @@ -14,6 +14,7 @@ block includes :marked Declarations + * [What classes should I add to _declarations_?](#q-what-to-declare) * [What is a _declarable_?](#q-declarable) * [What classes should I _not_ add to _declarations_?](#q-what-not-to-declare) @@ -21,17 +22,20 @@ block includes * [What does "Can't bind to 'x' since it isn't a known property of 'y'" mean?](#q-why-cant-bind-to) Imports + * [What should I import?](#q-what-to-import) * [Should I import _BrowserModule_ or _CommonModule_?](#q-browser-vs-common-module) * [What if I import the same module twice?](#q-reimport) Exports + * [What should I export?](#q-what-to-export) * [What should I *not* export?](#q-what-not-to-export) * [Can I re-export imported classes and modules?](#q-re-export) * [What is the _forRoot_ method?](#q-for-root) Service Providers + * [Why is a service provided in a feature module visible everywhere?](#q-module-provider-visibility) * [Why is a service provided in a _lazy-loaded_ module visible only to that module?](#q-lazy-loaded-module-provider-visibility) * [What if two modules provide the same service?](#q-module-provider-duplicates) @@ -43,12 +47,14 @@ block includes * [How can I tell if a module or service was previously loaded?](#q-is-it-loaded) Entry Components + * [What is an _entry component_?](#q-entry-component-defined) * [What is the difference between a _bootstrap_ component and an _entry component_?](#q-bootstrap_vs_entry_component) * [When do I add components to _entryComponents_?](#q-when-entry-components) * [Why does Angular need _entryComponents_?](#q-why-entry-components) General + * [What kinds of modules should I have and how should I use them?](#q-module-recommendations) * [What's the difference between Angular and JavaScript Modules?](#q-ng-vs-js-modules) * [How does Angular find components, directives, and pipes in a template?](#q-template-reference) diff --git a/public/docs/ts/latest/cookbook/ts-to-js.jade b/public/docs/ts/latest/cookbook/ts-to-js.jade index e9fef6f927..0d337bc12d 100644 --- a/public/docs/ts/latest/cookbook/ts-to-js.jade +++ b/public/docs/ts/latest/cookbook/ts-to-js.jade @@ -5,8 +5,8 @@ include ../../../../_includes/_util-fns in JavaScript. Translating from one language to the other is mostly a matter of changing the way you organize your code and access Angular APIs. - _TypeScript_ is a popular language option for Angular development. - Most code examples on the Internet as well as on this site are written in _TypeScript_. + _TypeScript_ is a popular language option for Angular development. + Most code examples on the Internet as well as on this site are written in _TypeScript_. This cookbook contains recipes for translating _TypeScript_ code examples to _ES6_ and to _ES5_ so that JavaScript developers can read and write Angular apps in their preferred dialect. @@ -15,16 +15,16 @@ a#toc :marked ## Table of contents - [_TypeScript_ to _ES6_ to _ES5_](#from-ts)
- [Modularity: imports and exports](#modularity)
- [Classes and Class Metadata](#class-metadata)
- [_ES5_ DSL](#dsl)
- [Interfaces](#interfaces)
- [Input and Output Metadata](#io-decorators)
- [Dependency Injection](#dependency-injection)
- [Host Binding](#host-binding)
- [View and Child Decorators](#view-child-decorators)
- [AOT compilation in _TypeScript_ Only](#aot)
+ * [_TypeScript_ to _ES6_ to _ES5_](#from-ts)
+ * [Modularity: imports and exports](#modularity)
+ * [Classes and Class Metadata](#class-metadata)
+ * [_ES5_ DSL](#dsl)
+ * [Interfaces](#interfaces)
+ * [Input and Output Metadata](#io-decorators)
+ * [Dependency Injection](#dependency-injection)
+ * [Host Binding](#host-binding)
+ * [View and Child Decorators](#view-child-decorators)
+ * [AOT compilation in _TypeScript_ Only](#aot)
**Run and compare the live _TypeScript_ and JavaScript code shown in this cookbook.** @@ -33,8 +33,8 @@ a#from-ts .l-main-section :marked ## _TypeScript_ to _ES6_ to _ES5_ - - _TypeScript_ + + _TypeScript_ is a typed superset of _ES6 JavaScript_. _ES6 JavaScript_ is a superset of _ES5 JavaScript_. _ES5_ is the kind of JavaScript that runs natively in all modern browsers. The transformation of _TypeScript_ code all the way down to _ES5_ code can be seen as "shedding" features. @@ -44,29 +44,29 @@ a#from-ts * _ES6-with-decorators_ to _ES6-without-decorators_ ("_plain ES6_") * _ES6-without-decorators_ to _ES5_ - When translating from _TypeScript_ to _ES6-with-decorators_, remove + When translating from _TypeScript_ to _ES6-with-decorators_, remove [class property access modifiers](http://www.typescriptlang.org/docs/handbook/classes.html#public-private-and-protected-modifiers) such as `public` and `private`. - Remove most of the - [type declarations](https://www.typescriptlang.org/docs/handbook/basic-types.html), + Remove most of the + [type declarations](https://www.typescriptlang.org/docs/handbook/basic-types.html), such as `:string` and `:boolean` but **keep the constructor parameter types which are used for dependency injection**. - - From _ES6-with-decorators_ to _plain ES6_, remove all + + From _ES6-with-decorators_ to _plain ES6_, remove all [decorators](https://www.typescriptlang.org/docs/handbook/decorators.html) and the remaining types. You must declare properties in the class constructor (`this.title = '...'`) rather than in the body of the class. Finally, from _plain ES6_ to _ES5_, the main missing features are `import` - statements and `class` declarations. + statements and `class` declarations. - For _plain ES6_ transpilation you can _start_ with a setup similar to the - [_TypeScript_ quickstart](https://github.com/angular/quickstart) and adjust the application code accordingly. - Transpile with [Babel](https://babeljs.io/) using the `es2015` preset. - To use decorators and annotations with Babel, install the + For _plain ES6_ transpilation you can _start_ with a setup similar to the + [_TypeScript_ quickstart](https://github.com/angular/quickstart) and adjust the application code accordingly. + Transpile with [Babel](https://babeljs.io/) using the `es2015` preset. + To use decorators and annotations with Babel, install the [`angular2`](https://github.com/shuhei/babel-plugin-angular2-annotations) preset as well. - + a#modularity .l-main-section @@ -78,7 +78,7 @@ a#modularity In both _TypeScript_ and _ES6_, you import Angular classes, functions, and other members with _ES6_ `import` statements. In _ES5_, you access the Angular entities of the [the Angular packages](../glossary.html#scoped-package) - through the global `ng` object. + through the global `ng` object. Anything you can import from `@angular` is a nested member of this `ng` object: +makeTabs(` @@ -100,23 +100,23 @@ a#modularity Each file in a _TypeScript_ or _ES6_ Angular application constitutes an _ES6_ module. When you want to make something available to other modules, you `export` it. - _ES5_ lacks native support for modules. - In an Angular _ES5_ application, you load each file manually by adding a `