Angular 通过正确的更改跟踪将服务属性绑定到组件属性

Angular 通过正确的更改跟踪将服务属性绑定到组件属性,angular,angular2-services,Angular,Angular2 Services,考虑一下非常简单的Angular 2服务: import { Injectable } from '@angular/core'; import {Category} from "../models/Category.model"; @Injectable() export class CategoryService { activeCategory: Category|{} = {}; constructor() {}; } 然后是使用此服务的组件: import { Co

考虑一下非常简单的Angular 2服务:

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;
    };
}
从'@angular/core'导入{Component,OnInit};
从“./shared/services/category.service”导入{CategoryService};
从“./shared/models/Category.model”导入{Category};
@组成部分({
选择器:“我的选择器”,
模板:`
{{categoryService.activeCategory.Name}
{{category.Name}}
`, }) 导出类MySelectorComponent实现OnInit{ 类别:类别{}={}; 构造函数(公共categoryService:categoryService){}; 恩戈尼尼特(){ this.category=this.categoryService.activeCategory; }; }
假设适当定义的类别模型,并假设另一个组件在某处将服务上的activeCategory设置为某个有效类别。假设categoryservice被设置为适当更高级别的提供者

发生这种情况时,模板中的第一行将正确显示类别名称,但第二行不会。我尝试过在服务上使用getter和setter,而不是原始访问;我尝试过基本类型、对象和对象属性;我不相信第一行是这种访问的合适范例。有人能告诉我将服务属性绑定到组件属性的最简单方法吗?这两种方法可以正确地进行更改跟踪


澄清:我知道我可以使用我自己创建和推动的观察结果。我想问的是,是否有任何一种已经融入到框架中的方法(这不需要我为一个可观察的对象编写大量的样板文件)能够在服务和组件之间形成一个可变的轨迹。

可观察的
s可以在没有太多样板文件的情况下使用
行为
s

@Injectable() 
export class CategoryService {
  activeCategory:BehaviorSubject<{category:Category}> = new BehaviorSubject({category:null});
  // or just `Subject` depending on your requirements
}
@Injectable()
导出类类别服务{
activeCategory:BehaviorSubject=新的BehaviorSubject({category:null});
//或者只是“主题”,取决于你的要求
}
@组件({
选择器:“我的选择器”,
模板:`
{(categoryService.activeCategory | async)?.Name}
`, }) 导出类MySelectorComponent实现OnInit{ 构造函数(公共categoryService:categoryService){}; }
您还可以只绑定到服务的属性

@Component({
  selector: 'my-selector',
  template: `
  {{categoryService?.activeCategory?.Name}}<br/>
`,
})
export class MySelectorComponent implements OnInit {
  constructor(public categoryService:CategoryService){};
}    
@组件({
选择器:“我的选择器”,
模板:`
{{categoryService?.activeCategory?.Name}
`, }) 导出类MySelectorComponent实现OnInit{ 构造函数(公共categoryService:categoryService){}; }

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

样板文件块不太大。 但是这里发生的事情是,当服务被创建或者你打电话时,不管你想怎么处理它。组件将通过订阅了解它,然后将局部变量更新为新值。允许您作为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() {

    };
}
从'@angular/core'导入{Injectable};
从“./models/Category.model”导入{Category};
从'rxjs/Subscription'导入{Subscription};
@可注射()
导出类类别服务{
private _categoryObject=新主题();
categoryObject宣布$=此。_categoryObject;
private _activeCategoryObject=新主题();
activeCategoryObject宣布$=此。\u activeCategoryObject;
活动类别:类别{}={};
构造函数(){};
}
从“@angular/core”导入{Component,OnInit};
从“./shared/services/category.service”导入{CategoryService};
从“./shared/models/Category.model”导入{Category};
从'rxjs/Subscription'导入{Subscription};
@组成部分({
选择器:“我的选择器”,
模板:`
{{activeCategory.Name}}
{{category.Name}}
`, }) 导出类MySelectorComponent实现OnInit{ 类别:类别{}={}; activeCategory:activeCategory{}={}; activeCategorySubscription:订阅; 类别认购:认购; 构造函数(公共类别服务:类别服务){ this.categorySubscription=this.categoryService.categoryObject.subscribe((category)=>{ 这个。类别=类别; }); this.activeCategorySubscription=this.categoryService.ActiveCategoryObject已宣布$.subscribe((活动)=>{ this.activeCategory=active; }); }; 恩戈尼尼特(){ }; }
您可以尝试用
ngDoCheck()
取代
ngOnInit()
。我不确定(实际上我怀疑)这是正确的做法,在任何情况下你都可以尝试

此方法在每个更改检测周期运行(而不是我猜的标准角度算法,这是潜在的问题),因此您应该使
MySelectorComponent的
category
属性与服务中的更改保持最新


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

您是否尝试过观察物?事实上,我肯定会使用观察物。它给你带来了很多好处,并且已经成为Angular 2应用程序的常见模式。对于Observable,您可以使用
ChangeDetectionStaregy.OnPush
,这也可以极大地提高性能。然后,您可以在视图中使用
async
管道并直接将可观察对象绑定到那里。如果您需要,如果您不知道如何使用可观察对象,我可以发布一个使用可观察对象的解决方案。但是直接调用服务方法或引用模板中的服务属性并不是一件好事。根据下面的答案,我接受使用主题/行为范式和手动订阅组件是ng2中唯一可行的方法。在“得到”它方面帮了很多忙。看起来仍然像accessi
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() {

    };
}