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

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

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

export class MyFirstSvc {

}

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

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

Ответ 1

Да, сначала нужно добавить декоратор @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.

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

Ответ 2

  • "Предоставляйте" свои услуги где-то там или там, где вы собираетесь их использовать, например, вы можете поместить их в корень вашего приложения, используя 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. Это обсуждается подробнее здесь: Можете ли вы только внедрять услуги в службы через бутстрап?

Ответ 3

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

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

Я использовал концепцию Routing, Shared-Service, Shared-Object. Надеюсь, это поможет вам понять основную часть общего обслуживания.

Примечание. Декоратер @Injectable используется для создания инъекций.

Отвечать

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;
  }
} 

Ответ 4

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

Я использовал этот подход:

  • Укажите все службы в поле поставщиков @ComponentA.
  • В 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");
    }
}

Ответ 5

не уверен, что ответ по-прежнему требуется, поэтому я хотел бы продолжить и попытаться ответить на этот вопрос.

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

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

Ответ 6

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

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

@Injectable()
export class MyFirstSvc {

}

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

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

Ответ 7

Сначала вам необходимо предоставить свою услугу

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

bootstrap(AppComponent,[MyFirstSvc]);

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

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

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

export class MySecondSvc {
      constructor(private myFirstSvc : MyFirstSvc ){}
}