问题 如何使用角度2中的正确更改跟踪将服务属性绑定到组件属性


考虑完全简单的angular2服务:

import { Injectable } from '@angular/core';
import {Category} from "../models/Category.model";

@Injectable()
export class CategoryService {
    activeCategory: Category|{} = {};
    constructor() {};
}

然后使用此服务的组件:

import { Component, OnInit } from '@angular/core';
import {CategoryService} from "../shared/services/category.service";
import {Category} from "../shared/models/Category.model";

@Component({
    selector: 'my-selector',
    template: `
    {{categoryService.activeCategory.Name}}<br/>
    {{category.Name}}<br/>
`,
})
export class MySelectorComponent implements OnInit {
    category:Category|{} = {};

    constructor(public categoryService:CategoryService){};

    ngOnInit() {
        this.category = this.categoryService.activeCategory;
    };
}

假设适当定义的类别模型并假设某个地方的另一个组件在某个时刻将服务上的activeCategory设置为有效的类别。假设categoryservice被设置为适当更高级别的提供者。

当发生这种情况时,模板中的第一行将正确显示类别名称,但第二行不会。我尝试过使用getter和setter与原始访问服务;我尝试过原始类型与对象和对象属性;我无法相信第一行是适合此类访问的范例。有人能告诉我将服务属性绑定到组件属性的最简单方法,该属性将正确地改变角度2中的跟踪吗?

澄清:我知道我可以使用我创建并为自己推动的可观察量。我要问的是,是否有任何已经融入框架的方式(这不需要我为可观察的文本编写大量的样板),只是在服务和组件之间建立一个可变轨道。


3708
2018-05-20 15:58


起源

你有没有尝试过的观察者? - eko
我知道我可以创建自己的observable,但是对于每个服务以及使用该服务的该属性的每个组件中的每个服务属性,都有很多样板代码。我希望这不是答案。 - Robert
实际上,我肯定会使用Observables。它为您提供了大量的好处,并已成为Angular 2应用程序的常见模式。使用Observables你可以使用 ChangeDetectionStaregy.OnPush 这也为你提供了很好的性能提升。然后你可以使用 async 在您的视图中管道并直接绑定Observable。 - LordTribual
如果您愿意,我可以使用Observables发布解决方案,如果您不知道如何使用它们。 - LordTribual
但是直接在模板中调用服务方法或引用服务属性并不是很好。我会说不好的做法。 - LordTribual


答案:


Observables可以在没有太多样板的情况下使用 Behavior秒。

@Injectable() 
export class CategoryService {
  activeCategory:BehaviorSubject<{category:Category}> = new BehaviorSubject({category:null});
  // or just `Subject` depending on your requirements
}
@Component({
  selector: 'my-selector',
  template: `
  {{(categoryService.activeCategory | async)?.Name}}<br/>
`,
})
export class MySelectorComponent implements OnInit {
  constructor(public categoryService:CategoryService){};
}

您也可以绑定到服务的属性

@Component({
  selector: 'my-selector',
  template: `
  {{categoryService?.activeCategory?.Name}}<br/>
`,
})
export class MySelectorComponent implements OnInit {
  constructor(public categoryService:CategoryService){};
}    

使用Elvis(或安全导航)操作符,如果是,则不会出现错误 activeCategory 仅在稍后获取值,例如异步调用完成时。


13
2018-05-20 16:39



仍然看起来像直接在模板中访问categoryService ...我希望能够说myLocalComponentProperty = service.property并正确地使用该轨道。 - Robert
答案已被接受,但我建议为行为主题添加所需的默认值和类型。例如 _activeCategory:BehaviorSubject<{category:Category}> = new BehaviorSubject({category:null}); 或类似的 - Robert
谢谢!我自己不使用TS(仅限Dart)并且不熟悉泛型类型参数的确切语法。 - Günter Zöchbauer
@GünterZöchbauer最后一个例子是否与* ngFor类似? *ngFor="let order of orderService.orders" 例如,给我错误。 - DarkNeuron
当然。也许是 orders 被取为异步和 null 当Angular呈现视图时。我不知道你的意思 .orders 是。 *ngFor="let order of orderService?.orders" 可能有用。如果不是,最好使用代码来创建一个新问题,该代码演示您尝试过的内容和完整的错误消息。 - Günter Zöchbauer


你可以尝试替代 ngOnInit() 同 ngDoCheck()。我不确定(实际上我怀疑)这是正确的做法,无论如何你都可以尝试。

这个方法在每个变化检测周期运行(而不是我猜的标准Angular算法,这是潜在的问题)因此你应该有 category 的财产 MySelectorComponent 最新的服务变更。

再次需要注意副作用(我不清楚)。


2
2018-05-20 20:53





不是太大的样板块。 但是这里发生的事情是,当创建服务或者你打电话时,你想要处理它。您的组件将通过订阅了解它,然后将您的本地变量更新为新值。允许您以this.activeCategory的身份访问它。

import { Injectable } from '@angular/core';
import {Category} from "../models/Category.model";
import {Subscription}   from 'rxjs/Subscription';

@Injectable()
export class CategoryService {
    private _categoryObject = new Subject<any>();
    categoryObjectAnnounced$ = this._categoryObject;
    private _activeCategoryObject = new Subject<any>();
    activeCategoryObjectAnnounced$ = this._activeCategoryObject;
    activeCategory: Category|{} = {};
    constructor() {};
}


import { Component, OnInit } from '@angular/core';
import {CategoryService} from "../shared/services/category.service";
import {Category} from "../shared/models/Category.model";
import {Subscription} from 'rxjs/Subscription';
@Component({
    selector: 'my-selector',
    template: `
    {{activeCategory.Name}}<br/>
    {{category.Name}}<br/>
`,
})
export class MySelectorComponent implements OnInit {
    category:Category|{} = {};
    activeCategory:ActiveCategory|{} = {};
    activeCategorySubscription : Subscription;
    categorySubscription : Subscription;
    constructor(public categoryService:CategoryService){
          this.categorySubscription= this.categoryService.categoryObjectAnnounced$.subscribe((category) => {
             this.category= category;
          });

          this.activeCategorySubscription= this.categoryService.activeCategoryObjectAnnounced$.subscribe((active) => {
             this.activeCategory= active;
          });
    };
    ngOnInit() {

    };
}

0
2018-05-20 16:43