使用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) {
  }
}

Answers:


103

在服务中提供一个事件并在组件中进行订阅:

@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
还请记住要取消订阅组件中的事件,否则,组件实例将不会被释放。需要在订阅函数返回的对象上调用取消订阅。
Chandermani

3
let subscription=nameService.nameChange.subscribe((value) => { this.name = value; }); subscription.unsubscribe()。我已经面临这个问题,因此免责声明。如果必须通过其他方法取消订阅,则该变量应为类级别
Chandermani

1
@micronyks问题不是关于重新加载恕我直言时保持值。为此,您需要将数据持久保存到服务器并重新加载,或者至少使用localstorage将其存储在浏览器中。
君特Zöchbauer

1
我一直在尝试实现此解决方案,但在服务中显示错误property 'emit' does not exist on type 'Subject<string>'。我在上述解决方案中缺少什么吗?这个解决方案有没有工作的Plunkr?
史蒂夫·施拉布

3
@Scipion通常,如果您强制性订阅,则也应强制性取消订阅。在这种情况下,预订此服务的组件可能仍会执行subscribe()更改名称时传递给的回调,即使它已被销毁,因为该预订在该组件被垃圾回收之前一直保持活动状态,直到该预订被回收为止取消。
君特Zöchbauer

23

由于nameinNameService是原始类型,因此您将在服务和组件中获得不同的实例。当您更改nameNameService,组件属性仍然具有初始值,并且绑定无法按预期工作。

您应在此处应用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;
  }
}

我也是这样做的。在angular 1中,“始终在模型中有一个点”是减少同步绑定工作的好方法,根据使用情况,在Angular2中也可能适用。没有它,使用事件发射器来同步模型可能会很乏味。
pixelbits

哇,与EventEmitter方式相比,为什么这样做有效?感觉工作量减少了很多?
B赫尔

7
@micronyks在angular2中,您可以有多个服务实例。您不应该providers在两个组件上都定义。这样,每个组件都有其自己的NameService plnkr.co/edit/nScZgT1hsIKHZ7Z5E6u3?p=preview
hansmaad

3
@BHull,之所以起作用,是因为对于一个对象,服务和组件都引用同一个对象...并且它们各自都引用了该对象。对于基本类型(字符串,数字,布尔值),组件在构造函数中获取其自己的原始值副本。您name对服务中的原始属性所做的任何更改都与您name对组件中的原始属性所做的任何更改均无关。
Mark Rajcok '16

1
“如果进行更改nameNameService则服务和组件中的实例将有所不同。” 即使您不更改值,该组件也会name在构造函数中获得其自己的原始属性。这个新的原始属性的初始值是name分配时服务中该属性的值。我要说明的一点是,对于原始类型,您永远不会拥有相同的“实例”。一种具有引用类型的引用,您可以得到两(或更多)引用同一实例的事物。
Mark Rajcok '16

12

我认为Günter提供的解决方案是最好的。

就是说,您必须意识到Angular2服务是单例的,它发生在注入器树中。这意味着:

  • 如果在应用程序级别(在方法的第二个参数内)定义服务bootstrap,则实例可以被所有元素(组件和服务)共享。
  • 如果您在组件级别(在providers属性内)定义服务,则该实例将特定于组件及其子组件。

有关此方面的更多详细信息,可以查看“分层依赖注入”文档:https : //angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html

希望对您有帮助,蒂埃里

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.