Книга: Angular 2 Cookbook
Назад: Service injection aliasing with useClass and useExisting
Дальше: Building a provider-configured service with useFactory

Injecting a value as a service with useValue and OpaqueTokens

In Angular 1, there was a broad selection of service types you could use in your application. A subset of these types allowed you to inject a static value instead of a service instance, and this useful ability is continued in Angular 2.

Note

The code, links, and a live example of this are available at .

Getting ready

Begin with the following simple application:

[app/app.module.ts]      import {NgModule} from '@angular/core';   import {BrowserModule} from '@angular/platform-browser';   import {RootComponent} from './root.component';   import {ArticleComponent} from './article.component';      @NgModule({     imports: [       BrowserModule     ],     declarations: [       RootComponent,       ArticleComponent     ],     bootstrap: [       RootComponent     ]   })   export class AppModule {}   [app/root.component.ts]      import {Component} from '@angular/core';      @Component({     selector: 'root',     template: `       <article></article>     `   })   export class RootComponent {}   [app/article.component.ts]      import {Component} from '@angular/core';      @Component({     selector: 'article',     template: `       <img src="{{logoUrl}}">       <h2>Fool and His Money Reunited at Last</h2>       <p>Author: Jake Hsu</p>     `   })   export class ArticleComponent {}   

How to do it...

Although a formal service class declaration and @Injectable decorator designation is no longer necessary for injecting a value, token/provider mapping is still needed. Since there is no longer a class available that can be used to type the injectable, something else will have to act as its replacement.

Angular 2 solves this problem with OpaqueToken. This module allows you to create a classless token that can be used to pair the injected value with the constructor argument. This can be used alongside the useValue provide option, which simply directly provides whatever its contents are as injected values.

Define a token using a unique string in its constructor:

[app/logo-url.token.ts]      import {OpaqueToken} from '@angular/core';      export const LOGO_URL = new OpaqueToken('logo.url');   

Incorporate this token into the application module definition as you normally would. However, you must specify what it will actually point to when it is injected. In this case, it should resolve to an image URL string:

[app/app.module.ts]      import {NgModule} from '@angular/core';   import {BrowserModule} from '@angular/platform-browser';   import {RootComponent} from './root.component';   import {ArticleComponent} from './article.component';   import {LOGO_URL} from './logo-url.token';      @NgModule({     imports: [       BrowserModule     ],     declarations: [       RootComponent,       ArticleComponent     ],     providers: [       {provide: LOGO_URL, useValue:          'https://angular.io/resources/images/logos/standard/logo-nav.png'}     ],     bootstrap: [       RootComponent     ]   })   export class AppModule {}   

Finally, you'll be able to inject this into a component. However, since you're injecting something that wasn't defined with the @Injectable() decoration, you'll need to use @Inject() inside the constructor to tell Angular that it should be provided, using dependency injection. Furthermore, the injection will not attach itself to the component's this, so you'll need to do this manually as well:

[app/article.component.ts]      import {Component, Inject} from '@angular/core';   import {LOGO_URL} from './logo-url.token';      @Component({     selector: 'article',     template: `       <img src="{{logoUrl}}">       <h2>Fool and His Money Reunited at Last</h2>       <p>Author: Jake Hsu</p>     `   })   export class ArticleComponent {     logoUrl:string;          constructor(@Inject(LOGO_URL) private logoUrl_) {       this.logoUrl = logoUrl_;     }   }   

With this, you should be able to see the image rendered in your browser!

How it works...

OpaqueToken allows you to use non-class types inside Angular 2's class-centric provider schema. It generates a simple class instance that essentially is just a wrapper for the custom string you provided. This class is what the dependency injection framework will use when attempting to map injection tokens to provider declarations. This gives you the ability to more widely utilize dependency injection throughout your application since you can now feed any type of value wherever a service type can be injected.

There's more...

One other way in which injecting values is useful is that it gives you the ability to stub out services. Suppose you wanted to define a default stub service that should be overridden with an explicit provider to enable useful behavior. In such a case, you can imagine a default article entity that could be differently configured via a directive while reusing the same component:

[app/root.component.ts]      import {Component} from '@angular/core';      @Component({     selector: 'root',     template: `       <article></article>       <article editor-view></article>     `   })   export class RootComponent {}      [app/editor-article.service.ts]      import {Injectable} from '@angular/core';      export const MockEditorArticleService = {     getArticle: () => ({        title: "Mock title",       body: "Mock body"     })   };      @Injectable()   export class EditorArticleService {     private title_:string =        "Prominent Vegan Embroiled in Scrambled Eggs Scandal";     private body_:string =        "Tofu Farming Alliance retracted their endorsement.";        getArticle() {       return {         title: this.title_,         body: this.body_       };     }   }   [app/editor-view.directive.ts]      import {Directive} from '@angular/core';   import {EditorArticleService} from './editor-article.service';      @Directive({     selector: '[editor-view]',     providers: [EditorArticleService]   })   export class EditorViewDirective {}   [app/article.component.ts]      import {Component, Inject} from '@angular/core';   import {EditorArticleService} from './editor-article.service';      @Component({     selector: 'article',     template: `       <h2>{{title}}</h2>       <p>{{body}}</p>     `   })   export class ArticleComponent {     title:string;     body:string;          constructor(private editorArticleService_:EditorArticleService) {       let article = editorArticleService_.getArticle();       this.title = article.title;       this.body = article.body;     }   }   

With this, your ArticleComponent, as defined in the preceding code, would use the mock service when the directive is not attached and the actual service when it is attached.

See also

  • Controlling service instance creation and injection with NgModule gives a broad overview of how Angular 2 architects provider hierarchies using modules
  • Service injection aliasing with useClass and useExisting demonstrates how to intercept dependency injection provider requests
  • Building a provider-configured service with useFactory details the process of setting up a service factory to create configurable service definitions
Назад: Service injection aliasing with useClass and useExisting
Дальше: Building a provider-configured service with useFactory

thank you
Flame
cant read the code since it is all on a single line. Also this comments section is russian
Rakuneque
DATA COLLECTION AND ANALYSIS Two reviewers extracted data and assessed methodological quality independently lasix torsemide conversion Many others were in that space already