оновлення змінних змін у компонентах із сервісу за допомогою angular2


77

У моєму додатку є NameService, який містить це ім’я.

Існує два дочірні компоненти App, Navbar та TheContent, які посилаються на цю послугу. Щоразу, коли ім'я змінюється в службі, я хочу, щоб воно оновлювалося в обох інших компонентах. Як я можу це зробити?

import {Component, Injectable} from 'angular2/core'

// Name Service

@Injectable()
class NameService {
  name: any;
  constructor() {
    this.name = "Jack";
  }
  change(){
    this.name = "Jane";
  }
}

// The navbar
@Component({
  selector: 'navbar',
  template: '<div>This is the navbar, user name is {{name}}.</div>'
})
export class Navbar {
  name: any;
  constructor(nameService: NameService) {
    this.name = nameService.name;
  }
}

// The content area
@Component({
  selector: 'thecontent',
  template: '<div>This is the content area. Hello user {{name}}. <button (click)=changeMyName()>Change the name</button></div>'
})
export class TheContent {

  name: any;

  constructor(public nameService: NameService) {
    this.name = nameService.name;
  }
  changeMyName() {
       this.nameService.change();
     console.log(this.nameService.name);
  }
}


@Component({
  selector: 'app',
  providers: [NameService],
  directives: [TheContent, Navbar],
  template: '<navbar></navbar><thecontent></thecontent>'
})
export class App {
  constructor(public nameService: NameService) {
  }
}

Відповіді:


104

Надайте подію в службі та підпишіться на неї в компонентах:

@Injectable()
class NameService {
  name: any;
  // EventEmitter should not be used this way - only for `@Output()`s
  //nameChange: EventEmitter<string> = new EventEmitter<string>();
  nameChange: Subject<string> = new Subject<string>();
  constructor() {
    this.name = "Jack";
  }
  change(){
    this.name = 'Jane';
    this.nameChange.next(this.name);
  }
}
export class SomeComponent { 
  constructor(private nameService: NameService) {
    this.name = nameService.name;
    this._subscription = nameService.nameChange.subscribe((value) => { 
      this.name = value; 
    });
  }

  ngOnDestroy() {
   //prevent memory leak when component destroyed
    this._subscription.unsubscribe();
  }
}

Див. Також
angular.io - ВЗАЄМОДІЯ КОМПОНЕНТІВ - Батьки та діти спілкуються через послугу


1
Також не забудьте скасувати підписку на подію в компоненті else, екземпляр компонента не буде звільнений. Потрібно викликати скасування підписки на об'єкт, що повертається функцією підписки.
Чандермані

3
let subscription=nameService.nameChange.subscribe((value) => { this.name = value; }); subscription.unsubscribe(). Я зіткнувся з цим питанням, отже, і застереженням. Якщо підписку доведеться скасувати за допомогою іншого методу, змінна повинна бути на рівні класу
Chandermani

1
@micronyks Питання не в тому, щоб зберегти значення при перезавантаженні IMHO. Для цього вам потрібно зберегти дані на сервері та перезавантажити їх або, принаймні, використовувати localstorage, щоб зберегти їх у браузері.
Günter Zöchbauer

1
Я намагався впровадити це рішення, але в службі з’являється повідомлення про помилку property 'emit' does not exist on type 'Subject<string>'. Мені щось не вистачає у наведеному вище рішенні? Чи існує робочий Plunkr цього рішення?
Стів Шраб

3
@Scipion Загалом, якщо ви підписуєтесь імперативно, вам слід також відмовитись від підписки. У цьому випадку компонент, який підписується на цю послугу, імовірно, все одно буде виконувати зворотний виклик, переданий subscribe()при зміні імені, навіть коли він уже знищений, оскільки передплата залишається активною, поки компонент не отримає сміття, що, ймовірно, не відбудеться, доки підписка не буде скасовано.
Günter Zöchbauer

23

Оскільки namein NameService- примітивний тип, ви отримаєте різні екземпляри в службі та ваших компонентах. При зміні nameв NameService, складові властивості до цих пір мають початкове значення і зв'язування не працює , як очікувалося.

Тут слід застосувати "правило крапки" angular1 і прив'язати до посилального типу. Змініть, NameServiceщоб зберегти об’єкт, що містить ім’я.

export interface Info {
   name:string;
}

@Injectable()
class NameService {
  info: Info = { name : "Jack" };
  change(){
    this.info.name = "Jane";
  }
}

Ви можете прив’язати цей об’єкт і отримувати оновлення nameвластивості автоматично.

// The navbar
@Component({
  selector: 'navbar',
  template: '<div>This is the navbar, user name is {{info.name}}.</div>'
})
export class Navbar {
  info: Info;
  constructor(nameService: NameService) {
    this.info = nameService.info;
  }
}

Це шлях я б теж зробив. У кутовому 1 "завжди мати крапку у вашій моделі" - це хороший спосіб зменшити зусилля для синхронізації ваших прив'язок, це може бути доречним і в Angular2, залежно від випадку використання. Без цього використання випромінювачів подій для синхронізації моделі може стати нудним.
пікселі

вау, чому це працює в порівнянні зі способом EventEmitter? здається, набагато менше роботи?
B Hull

7
@micronyks У angular2 ви можете мати кілька екземплярів служб. Не слід визначати providersобидва компоненти. Таким чином, кожен компонент має свій власний NameService plnkr.co/edit/nScZgT1hsIKHZ7Z5E6u3?p=preview
hansmaad,

3
@BHull, це працює, оскільки з об’єктом як служба, так і компонент посилаються на один і той самий об’єкт ... і кожен з них має посилання на цей об’єкт. З примітивним типом (рядок, число, логічне значення) компонент отримує власну копію примітивного значення в конструкторі. Будь-які зміни, які ви вносите в примітивну nameвластивість у службі, не пов’язані з будь-якою зміною, яку ви вносите в примітивну nameвластивість у компоненті.
Mark Rajcok

1
"Якщо ви внесете зміни, nameу NameServiceвас буде інший екземпляр служби та ваших компонентів." Навіть якщо ви не зміните значення, компонент отримує своє власне nameпримітивне властивість у конструкторі. Початкове значення цієї нової примітивної властивості - це вартість nameвластивості в службі на момент призначення. Я намагаюся сказати, що з примітивними типами у вас ніколи не буває однакових "екземплярів". Один із типів посилань може отримати дві (або більше) речі, що посилаються на один екземпляр.
Mark Rajcok

12

Я думаю, що рішення, яке пропонує Гюнтер, є найкращим.

Тим не менш, ви повинні знати, що послуги Angular2 є єдиними, які розміщуються у дереві форсунок. Це означає що:

  • якщо ви визначаєте свою службу на рівні програми (у межах другого параметра bootstrapметоду), екземпляр може бути спільним для всіх елементів (компонентів та служби).
  • якщо Ви визначаєте свою послугу на рівні компонентів (усередині providersатрибута), екземпляр буде специфічним для компонента та його підкомпонентів.

Щоб отримати докладнішу інформацію про такий аспект, ви можете поглянути на документ "Впорскування ієрархічної залежності": https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html

Сподіваюся, це допоможе тобі, Тьєррі

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.