Bind a service property to a component property with proper change tracking

前端 未结 3 852
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-01-01 12:19

Consider the utterly simple Angular 2 service:

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

@Injectabl         


        
相关标签:
3条回答
  • 2021-01-01 12:44

    Observables can be used without much boilerplate using Behaviors.

    @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){};
    }
    

    You also can just bind to properties of your service

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

    Using the Elvis (or safe-navigation) operator you don't get an error if the activeCategory only gets a value later, for example when an async call completes.

    0 讨论(0)
  • 2021-01-01 12:46

    You may try to substiture ngOnInit() with ngDoCheck(). I am not sure (actually I doubt) this is the right thing to do, in any case you can try.

    This method is run at every change detection cycle (instead of the standard Angular algorithm I guess, and here is the potential issue) and therefore you shoud have the category property of MySelectorComponent up to date with the changes in the service.

    Again need to be carefull of side effects (which are not clear to me).

    0 讨论(0)
  • 2021-01-01 12:59

    Not too big of a boilerplate chunk. But what's going on here is that when the service is created or you make a call however your want to handle it. Your component will know about it through the subscription and then update your local variable to the new value. Allowing you to access it as 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 讨论(0)
提交回复
热议问题