【问题标题】:updating variable changes in components from a service with angular2使用 angular2 从服务更新组件中的变量更改
【发布时间】:2017-06-07 18:59:39
【问题描述】:

我的应用有一个包含名称的 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) {
  }
}

【问题讨论】:

    标签: javascript angular


    【解决方案1】:

    在服务中提供事件并在组件中订阅:

    @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 - COMPONENT INTERACTION - Parent and children communicate via a service

    【讨论】:

    • 还要记得取消订阅组件中的事件,否则组件实例不会被释放。需要对subscribe函数返回的对象调用unsubscribe。
    • let subscription=nameService.nameChange.subscribe((value) =&gt; { this.name = value; }); subscription.unsubscribe()。我遇到了这个问题,因此免责声明。如果订阅必须从其他方法取消,变量应该是类级别
    • @micronyks 问题不在于在重新加载恕我直言时保持价值。为此,您需要将数据持久化到服务器并重新加载,或者至少使用本地存储,将其存储在浏览器中。
    • 我一直在尝试实施此解决方案,但服务中出现property 'emit' does not exist on type 'Subject&lt;string&gt;' 错误。我在上述解决方案中遗漏了什么吗?这个解决方案有一个有效的 Plunkr 吗?
    • @Scipion 一般来说,如果你强制订阅,你也应该强制取消订阅。在这种情况下,订阅此服务的组件可能仍会在名称更改时执行传递给subscribe() 的回调,即使它已经被销毁,因为订阅保持活动状态直到组件被垃圾收集,这可能不会发生,直到订阅已取消。
    【解决方案2】:

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

    您应该在此处应用 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 中也可能适用,具体取决于用例。没有它,使用事件发射器来同步模型会变得乏味。
    • 哇,与 EventEmitter 方式相比,为什么这种方式有效?感觉工作量少了很多?
    • @micronyks 在 angular2 中,您可以拥有多个服务实例。您不应在两个组件上定义 providers。这样每个组件都有自己的NameServiceplnkr.co/edit/nScZgT1hsIKHZ7Z5E6u3?p=preview
    • @BHull,它之所以有效,是因为对于一个对象,服务和组件都引用同一个对象......并且它们每个都有一个对那个对象的 reference .对于原始类型(字符串、数字、布尔值),组件在构造函数中获取自己的原始值副本。您对服务中的原始name 属性所做的任何更改都与您对组件中的原始name 属性所做的任何更改无关。
    • "如果您将 name 更改为 NameService,您的服务和组件中就会有不同的实例。"即使您不更改值,组件也会在构造函数中获得自己的name 原始属性。这个新的原始属性的初始值是分配时服务中name 属性的值。我要说明的一点是,对于原始类型,您永远不会拥有相同的“实例”。一个具有引用类型的你可以得到两个(或更多)引用同一个实例的东西。
    【解决方案3】:

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

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

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

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

    希望对你有帮助 蒂埃里

    【讨论】:

      猜你喜欢
      • 2018-06-09
      • 1970-01-01
      • 2016-06-03
      • 2016-08-08
      • 2023-03-17
      • 2019-07-13
      • 2018-03-07
      • 2021-03-30
      • 2017-08-26
      相关资源
      最近更新 更多