Какой лучший способ внедрить одну услугу в другую в angular 2 (бета)?

Я знаю, как внедрить сервис в компонент (через @Component), но как я могу использовать DI для передачи сервисов за пределы компонентов?

Другими словами, я не хочу делать это:

export class MyFirstSvc {

}

export class MySecondSvc {
    constructor() {
        this.helpfulService = new MyFirstSvc();
    }
}

export class MyThirdSvc {
    constructor() {
        this.helpfulService = new MyFirstSvc();
    }
}

7 ответов

Да, первое, что нужно добавить @Injectable декоратор на каждом сервисе, который вы хотите внедрить. На самом деле, Injectable имя немного коварное Это не означает, что класс будет "инъецируемым", но он будет украшать, чтобы можно было вводить параметры конструктора. Посмотрите эту проблему Github для более подробной информации: https://github.com/angular/angular/issues/4404.

Вот мое понимание механизма впрыска. При настройке @Injectable декоратор для класса, Angular попытается создать или получить экземпляры для соответствующих типов в инжекторе для текущей цепочки выполнения. На самом деле, существует не только один инжектор для приложения Angular2, но и дерево инжекторов. Они неявно связаны со всем приложением и компонентами. Одной из ключевых особенностей на этом уровне является то, что они связаны друг с другом иерархически. Это дерево инжекторов отображает дерево компонентов. Инжекторы не определены для "услуг".

Давайте возьмем образец. У меня есть следующее приложение:

  • Составная часть AppComponent: основной компонент моего приложения, который предоставляется при создании приложения Angular2 в bootstrap функция

    @Component({
      selector: 'my-app', 
        template: `
          <child></child>
        `,
        (...)
        directives: [ ChildComponent ]
    })
    export class AppComponent {
    }
    
  • Составная часть ChildComponent: подкомпонент, который будет использоваться в AppComponent составная часть

    @Component({
        selector: 'child', 
        template: `
          {{data | json}}<br/>
          <a href="#" (click)="getData()">Get data</a>
        `,
        (...)
    })
    export class ChildComponent {
      constructor(service1:Service1) {
        this.service1 = service1;
      }
    
      getData() {
        this.data = this.service1.getData();
          return false; 
      }
    }
    
  • Две услуги, Service1 а также Service2: Service1 используется ChildComponent а также Service2 от Service1

    @Injectable()
    export class Service1 {
      constructor(service2:Service2) {
        this.service2 = service2;
      }
    
      getData() {
        return this.service2.getData();
      }
    }
    

    @Injectable()
    export class Service2 {
    
      getData() {
        return [
          { message: 'message1' },
          { message: 'message2' }
        ];
      }
    }
    

Вот краткий обзор всех этих элементов и их связей:

Application
     |
AppComponent
     |
ChildComponent
  getData()     --- Service1 --- Service2

В таком приложении у нас есть три инжектора:

  • Инжектор приложения, который можно настроить с помощью второго параметра bootstrap функция
  • AppComponent инжектор, который можно настроить с помощью providers атрибут этого компонента. Он может "видеть" элементы, определенные в инжекторе приложения. Это означает, что если поставщик не найден в этом поставщике, он будет автоматически искать этот родительский инжектор. Если в последнем случае не найдено, будет выдана ошибка "поставщик не найден".
  • ChildComponent инжектор, который будет следовать тем же правилам, что и AppComponent один. Чтобы внедрить элементы, включенные в цепочку инъекций, выполняемых для компонента, поставщики будут искать сначала в этом инжекторе, затем в AppComponent один и, наконец, в приложении один.

Это означает, что при попытке ввести Service1 в ChildComponent конструктор, Angular2 рассмотрит ChildComponent инжектор, затем в AppComponent один и, наконец, в приложение один.

поскольку Service2 должен быть введен в Service1будет выполнена та же обработка разрешения: ChildComponent инжектор, AppComponent один и приложение один.

Это означает, что оба Service1 а также Service2 можно указать на каждом уровне в соответствии с вашими потребностями, используя providers атрибут для компонентов и второй параметр bootstrap функция для применения инжектора.

Это позволяет обмениваться экземплярами зависимостей для набора элементов:

  • Если вы определяете провайдера на уровне приложения, созданный соответствующий экземпляр будет совместно использоваться всем приложением (всеми компонентами, всеми службами,...).
  • Если вы определяете поставщика на уровне компонента, экземпляр будет совместно использоваться самим компонентом, его подкомпонентами и всеми "службами", включенными в цепочку зависимостей.

Так что это очень мощно, и вы можете организовывать, как вы хотите и для ваших нужд.

Вот соответствующий plunkr, чтобы вы могли поиграть с ним: https://plnkr.co/edit/PsySVcX6OKtD3A9TuAEw?p=preview.

Эта ссылка из документации Angular2 может помочь вам: https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html.

Надеюсь, это поможет вам (и извините за длинный ответ), Тьерри

  • "Предоставляйте" свои услуги где-то на уровне или выше, где вы собираетесь их использовать, например, вы можете поместить их в корень своего приложения, используя bootstrap() если вы только один раз экземпляр каждого сервиса (синглтоны).
  • Использовать @Injectable() Декоратор на любом сервисе, который зависит от другого.
  • Добавьте другие сервисы в конструктор зависимого сервиса.

boot.ts

import {bootstrap} from 'angular2/platform/browser';
import {AppComponent} from './app.component';
import {MyFirstSvc} from '../services/MyFirstSvc';
import {MySecondSvc} from '../services/MySecondSvc';

bootstrap(AppComponent, [MyFirstSvc, MySecondSvc]);

MySecondSvc.ts

import {Injectable} from 'angular2/core';
import {MyFirstSvc} from '../services/MyFirstSvc';

@Injectable()
export class MySecondSvc {
  constructor(private _firstSvc:MyFirstSvc) {}
  getValue() {
    return this._firstSvc.value;
  }
}

Смотрите Plunker для других файлов.

Что немного странно в Service DI, так это то, что он все еще зависит от компонентов. Например, MySecondSvc создается, когда компонент запрашивает его, и в зависимости от того, где MyFirstSvc был "предоставлен" в дереве компонентов, что может повлиять на MyFirstSvc экземпляр вводится в MySecondSvc, Это обсуждается более подробно здесь: можете ли вы вводить сервисы в сервисы только через загрузчик?

Сервис считается разделенным между компонентами. Итак, скажем, если у меня есть один сервис, я могу использовать его в разных компонентах.

Здесь В этом ответе я показываю вам один сервис, который принимает данные от одного компонента и отправляет эти данные другому компоненту.

Я использовал концепцию маршрутизации, Shared-Service, Shared-Object. Я надеюсь, что это поможет вам понять основы Share-сервиса.

Примечание: @Injectable decorater используется, чтобы сделать сервис инъекционным.

Ответ

Boot.ts

import {Component,bind} from 'angular2/core';

import {bootstrap} from 'angular2/platform/browser';

import {Router,ROUTER_PROVIDERS,RouteConfig, ROUTER_DIRECTIVES,APP_BASE_HREF,LocationStrategy,RouteParams,ROUTER_BINDINGS} from 'angular2/router';

import {SharedService} from 'src/sharedService';

import {ComponentFirst} from 'src/cone';
import {ComponentTwo} from 'src/ctwo';


@Component({
  selector: 'my-app',
  directives: [ROUTER_DIRECTIVES],
  template: `
    <h1>
      Home
    </h1> 

    <router-outlet></router-outlet>
      `,

})

@RouteConfig([
  {path:'/component-first', name: 'ComponentFirst', component: ComponentFirst}
  {path:'/component-two', name: 'ComponentTwo', component: ComponentTwo}

])

export class AppComponent implements OnInit {

  constructor(router:Router)
  {
    this.router=router;
  }

    ngOnInit() {
    console.log('ngOnInit'); 
    this.router.navigate(['/ComponentFirst']);
  }



}

    bootstrap(AppComponent, [SharedService,
    ROUTER_PROVIDERS,bind(APP_BASE_HREF).toValue(location.pathname)
    ]);

FirstComponent

import {Component,View,bind} from 'angular2/core';
import {SharedService} from 'src/sharedService';
import {Router,ROUTER_PROVIDERS,RouteConfig, ROUTER_DIRECTIVES,APP_BASE_HREF,LocationStrategy,RouteParams,ROUTER_BINDINGS} from 'angular2/router';
@Component({
  //selector: 'f',
  template: `
    <div><input #myVal type="text" >
    <button (click)="send(myVal.value)">Send</button>
      `,

})

export class ComponentFirst   {

  constructor(service:SharedService,router:Router){
    this.service=service;
    this.router=router;
  }

  send(str){
    console.log(str);
    this.service.saveData(str); 
    console.log('str');
    this.router.navigate(['/ComponentTwo']);
  }

}

SecondComponent

import {Component,View,bind} from 'angular2/core';
import {SharedService} from 'src/sharedService';
import {Router,ROUTER_PROVIDERS,RouteConfig, ROUTER_DIRECTIVES,APP_BASE_HREF,LocationStrategy,RouteParams,ROUTER_BINDINGS} from 'angular2/router';
@Component({
  //selector: 'f',
  template: `
    <h1>{{myName}}</h1>
    <button (click)="back()">Back<button>
      `,

})

export class ComponentTwo   {

  constructor(router:Router,service:SharedService)
  {
    this.router=router;
    this.service=service;
    console.log('cone called');
    this.myName=service.getData();
  }
  back()
  {
     console.log('Back called');
    this.router.navigate(['/ComponentFirst']);
  }

}

SharedService и общий объект

import {Component, Injectable,Input,Output,EventEmitter} from 'angular2/core'

// Name Service
export interface myData {
   name:string;
}



@Injectable()
export class SharedService {
  sharingData: myData={name:"nyks"};
  saveData(str){
    console.log('save data function called' + str + this.sharingData.name);
    this.sharingData.name=str; 
  }
  getData:string()
  {
    console.log('get data function called');
    return this.sharingData.name;
  }
} 

Я не уверен, что ответ все еще требуется, поэтому я бы попробовал ответить на него.

Рассмотрим следующий пример, где у нас есть Компонент, который использует сервис для заполнения некоторых значений в своем шаблоне, как показано ниже

testComponent.component.ts

import { Component } from "@angular/core"
import { DataService } from "./data.service"
@Component({
    selector:"test-component",
    template:`<ul>
             <li *ngFor="let person of persons">{{ person.name }}</li>
             </ul>
})

export class TestComponent {
  persons:<Array>;
  constructor(private _dataService:DataService){
    this.persons = this._dataService.getPersons()
  }
}

Приведенный выше код довольно прост, и он будет пытаться получить то, что getPersons возвращает из DataService. Файл DataService доступен ниже.

data.service.ts

export class DataService {

persons:<Array>;

constructor(){
    this.persons = [
      {name: "Apoorv"},
      {name: "Bryce"},
      {name: "Steve"}
    ]
}

getPersons(){

return this.persons

}

Приведенный выше фрагмент кода будет прекрасно работать без использования декоратора @Injectable. Но проблема начнется, когда нашему сервису (в данном случае DataService) потребуются некоторые зависимости, например, для. Http. если мы изменим наш data.service.ts файл, как показано ниже, мы получим сообщение об ошибке Cannot resolve all parameters for DataService(?). Make sure they all have valid type or annotations.

import { Http } from '@angular/http';
export class DataService {

persons:<Array>;

constructor(){
    this.persons = [
      {name: "Apoorv"},
      {name: "Bryce"},
      {name: "Steve"}
    ]
}

getPersons(){

return this.persons

}

Это как-то связано с тем, как декораторы функционируют в Angular 2. Пожалуйста, прочтите https://blog.thoughtram.io/angular/2015/05/03/the-difference-between-annotations-and-decorators.html чтобы получить в глубоком понимании этого вопроса.

Вышеприведенный код также не будет работать, так как мы также должны импортировать HTTP в наш модуль начальной загрузки.

Но правило большого пальца, которое я могу предложить, состоит в том, что если ваш служебный файл нуждается в зависимости, то вы должны украсить этот класс декоратором @Injectable.

ссылка: https://blog.thoughtram.io/angular/2015/09/17/resolve-service-dependencies-in-angular-2.html

Каким-то образом @Injectable не работает для меня в Angular 2.0.0-beta.17 при подключении ComponentA -> ServiceB -> ServiceC.

Я выбрал этот подход:

  1. Ссылка на все услуги в поле поставщиков @ComponentA.
  2. В ServiceB используйте аннотацию @Inject в конструкторе, чтобы связать ServiceC.

Запустите этот Plunker, чтобы увидеть пример или посмотреть код ниже

app.ts

@Component({selector: 'my-app',
    template: `Hello! This is my app <br/><br/><overview></overview>`,
    directives: [OverviewComponent]
})
class AppComponent {}

bootstrap(AppComponent);

overview.ts

import {Component, bind} from 'angular2/core';
import {OverviewService} from "../services/overview-service";
import {PropertiesService} from "../services/properties-service";

@Component({
    selector: 'overview',
    template: `Overview listing here!`,
    providers:[OverviewService, PropertiesService] // Include BOTH services!
})

export default class OverviewComponent {

    private propertiesService : OverviewService;

    constructor( overviewService: OverviewService) {
        this.propertiesService = overviewService;
        overviewService.logHello();
    }
}

Обзор-service.ts

import {PropertiesService} from "./properties-service";
import {Inject} from 'angular2/core';

export class OverviewService {

    private propertiesService:PropertiesService;

    // Using @Inject in constructor
    constructor(@Inject(PropertiesService) propertiesService:PropertiesService){
        this.propertiesService = propertiesService;
    }

    logHello(){
        console.log("hello");
        this.propertiesService.logHi();
    }
}

свойства-service.ts

// Using @Injectable here doesn't make a difference
export class PropertiesService {

    logHi(){
        console.log("hi");
    }
}

Первое, что нужно сделать, это аннотировать все услуги с помощью @Injectable аннотаций. Обратите внимание на круглые скобки в конце аннотации, без этого это решение не будет работать.

Как только это будет сделано, мы можем внедрить сервисы друг в друга, используя конструктор:

@Injectable()
export class MyFirstSvc {

}

@Injectable()
export class MySecondSvc {
    constructor(helpfulService: MyFirstSvc) {        
    }
}

@Injectable()
export class MyThirdSvc {
    constructor(helpfulService: MyFirstSvc) {        
    }
}

Во-первых, вы должны предоставить свои услуги

Вы можете предоставить это либо в методе начальной загрузки:

bootstrap(AppComponent,[MyFirstSvc]);

или в компоненте приложения, или в любом другом компоненте, в зависимости от ваших потребностей.

@Component({
    ...
      providers:[MyFirstSvc]
}
...

затем просто добавьте свой сервис с помощью конструктора:

export class MySecondSvc {
      constructor(private myFirstSvc : MyFirstSvc ){}
}
Другие вопросы по тегам