web-dev-qa-db-fra.com

Lier une propriété de service à une propriété de composant avec un suivi des modifications approprié

Considérez le service Angular 2 tout à fait simple:

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

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

Et puis le composant utilisant ce service:

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;
    };
}

Supposons que le modèle de catégorie défini de manière appropriée et qu'un autre composant place quelque part la catégorie activeCategory du service sur une catégorie valide à un moment donné. Supposons que le service de catégorie est défini en tant que fournisseur à un niveau supérieur approprié.

Lorsque cela se produit, la première ligne du modèle affiche correctement le nom de la catégorie, mais pas la deuxième ligne. J'ai essayé d'utiliser des getters et des setters contre un accès brut sur le service; J'ai essayé les types primitifs vs objets vs propriétés d'objet; Je ne peux pas croire que la première ligne est le paradigme approprié pour ce type d'accès. Quelqu'un peut-il me dire le moyen le plus simple de lier une propriété de service à une propriété de composant qui effectuera correctement le suivi des modifications dans un angle deux?

CLARIFICATION: / Je sais que je pourrais utiliser des observables que je crée et que je pousse pour moi-même. Ce que je demande, c’est de savoir s’il existe déjà une quelconque méthode dans ce cadre (cela ne me demande pas d’écrire la quantité énorme de passe-partout pour un observable) qui crée simplement un écart variable entre le service et le composant. .

15
Robert

Observables peut être utilisé sans beaucoup de passe-partout en utilisant 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){};
}

Vous pouvez également simplement vous lier aux propriétés de votre service

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

Si vous utilisez l'opérateur Elvis (ou navigation sécurisée), vous ne recevez pas d'erreur si la variable activeCategory n'obtient qu'une valeur ultérieurement, par exemple lorsqu'un appel asynchrone se termine.

15

Vous pouvez essayer de substituer ngOnInit() avec ngDoCheck(). Je ne suis pas sûr (en fait, j'en doute) que c'est la bonne chose à faire, dans tous les cas, vous pouvez essayer.

Cette méthode est exécutée à chaque cycle de détection de changement (au lieu de l'algorithme angulaire standard, je suppose, et voici le problème potentiel) et vous devez donc avoir la propriété category de MySelectorComponent à jour avec les modifications apportées au service.

Encore une fois besoin de faire attention aux effets secondaires (qui ne sont pas claires pour moi).

2
Picci

Pas trop gros, mais ce qui se passe ici, c’est que, lorsque le service est créé ou que vous passez un appel, vous voulez le gérer. Votre composant le saura via l'abonnement, puis mettra à jour votre variable locale avec la nouvelle valeur. Vous permettant d'y accéder en tant que 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
inoabrian