web-dev-qa-db-fra.com

Angular 2: Observable / Abonnement ne se déclenchant pas

Je l'ai fait plusieurs fois dans mon application. C'est simple, ça devrait marcher ... Mais cette fois ça ne marche pas.

Mon problème:

J'appelle une méthode dans un service à partir d'un composant A, mon composant B est abonné mais ne réagit ni ne reçoit rien. subscribe() ne se déclenche pas!

navigation-elements.service.ts

@Injectable()
export class NavigationElementsService {
    updateIBOsNavigation$: Observable<any>;

    private updateIBOsNavigationSubject = new Subject<any>();

    constructor() {
        this.updateIBOsNavigation$ = this.updateIBOsNavigationSubject.asObservable();
    }

    updateIBOsNavigation(navigationData) {
        log.d('updateIBOsNavigation', JSON.stringify(navigationData));
        this.updateIBOsNavigationSubject.next(navigationData);
    }
}

IboDetailsGeneral composant

export class IboDetailsGeneral implements OnInit, OnDestroy {
    id: string;
    private sub: any;

    constructor(private route: ActivatedRoute, private iboService: IBOsService, private navigationService: NavigationElementsService) {
        this.sub = this.route.params.subscribe(params => {
            this.id = params['id'];

            console.log('CALLING updateIBOsNavigation FUNCTION');
            this.navigationService.updateIBOsNavigation(this.id);
        });
    }

    ngOnInit() {
        console.log('CALLING updateIBOsNavigation FUNCTION AGAIN');
        this.navigationService.updateIBOsNavigation('test');
    }

    ngOnDestroy() {
        this.sub.unsubscribe();
    }
}

Ce composant déclenche la méthode du service: updateIBOsNavigation.

IBOsNavigationElement composant

export class IBOsNavigationElement implements OnInit {
    private id: string;

    constructor(private navigationService: NavigationElementsService) {
        this.navigationService.updateIBOsNavigation$.subscribe((navigationData) => {
                log.d('I received this Navigation Data:', JSON.stringify(navigationData));
                this.id = navigationData;
            }
        );
    }

    ngOnInit() {
    }
}

Ce composant est inscrit, il doit être répertorié et recevoir les données ...

Trions DI: Tenez compte du fait que IboDetailsGeneral est dans une couche inférieure de la structure de l'application, donc IboDetailsGeneral est enfant of IBOsNavigationElement .

C'est pourquoi j'ajoute NavigationElementsService dans le module de IBOsNavigationElement:

NavigationModule est le module de IBOsNavigationElement

@NgModule({
    imports: [
        // A lot of stuff
    ],
    declarations: [
        // A lot of stuff
        IBOsNavigationElement
    ],
    exports: [
        // A lot of stuff
    ],
    providers: [
        NavigationElementsService
    ]
})

Console:

APPEL à la mise à jourIBOsNavigation FUNCTION

updateIBOsNavigation "95"

MISE À JOUR DE LA FONCTION IBONavigation

updateIBOsNavigation "test"

Les résultats de cette console m'indiquent que:

  • La méthode est appelée, donc aucune erreur de fournisseur. La communication avec le service est OK .

Mes tests:

  • J'ai essayé d'appeler une méthode aléatoire dans IBOsNavigationElement (l'auditeur), et la communication avec le service est bonne.
  • Le seul endroit où NavigationElementsService est ajouté à providers est dans NavigationModule, donc il n'y a qu'une seule instance du service, n'est-ce pas? Ensuite, le problème expliqué dans le lien suivant n'a pas lieu: L'abonnement observable Angular 2 ne se déclenche pas

Je suis vraiment désolé pour mon 'mur de texte' mais à ce stade, je suis un peu désespéré.

Toute aide est appréciée, merci!

Mise à jour 1:

Après la première réponse, j'ai essayé plusieurs choses ...

Utilisation de ReplaySubject:

@Injectable()
export class NavigationElementsService {
    public updateIBOsNavigation$ = new ReplaySubject();

    updateIBOsNavigation(navigationData) {
        log.d('updateIBOsNavigation', JSON.stringify(navigationData));
        this.updateIBOsNavigation$.next(navigationData);
    }
}

Résultat: lors de l'appel de updateIBOsNavigation(), subscribe() ne se déclenche toujours pas.

Utilisation de BehaviorSubject:

@Injectable()
export class NavigationElementsService {
    updateIBOsNavigationSubject = new BehaviorSubject<any>('');
     updateIBOsNavigation$ = this.updateIBOsNavigationSubject.asObservable();

    updateIBOsNavigation(navigationData) {
        log.d('updateIBOsNavigation', JSON.stringify(navigationData));
        this.updateIBOsNavigationSubject.next(navigationData);
    }
}

Résultat: Il entre subscribe() à l'initialisation mais quand j'appelle updateIBOsNavigation(), subscribe() ne se déclenche toujours pas.

Utilisation de BehaviorSubject v2:

J'ai essayé cette approche: behaviorSubject dans angular2, comment cela fonctionne et comment l'utiliser

@Injectable()
export class NavigationElementsService {
    public updateIBOsNavigation$: Subject<string> = new BehaviorSubject<string>(null);

    updateIBOsNavigation(navigationData) {
        log.d('updateIBOsNavigation', JSON.stringify(navigationData));
        this.updateIBOsNavigation$.next(navigationData);
    }
}

Résultat: identique à l'application précédente de BehaviorSubject.

Mise à jour 2:

Plus d'échantillons de tentatives désespérées après des recherches sur le Web ...

Utilisation de BehaviorSubject v3:

@Injectable()
export class NavigationElementsService {
    updateIBOsNavigation$: Observable<any>;
    updateIBOsNavigationSubject = <BehaviorSubject<any>> new BehaviorSubject([]);

    constructor() {
        this.updateIBOsNavigation$ = this.updateIBOsNavigationSubject.asObservable();
    }

    updateIBOsNavigation(navigationData) {
        log.d('updateIBOsNavigation()', JSON.stringify(navigationData));
        this.updateIBOsNavigationSubject.next(navigationData);
    }
}

Résultat: Identique aux précédentes tentatives de BehaviorSubject ... Le désespoir augmente ...

Mise à jour 3:

Au cas où, je voulais m'assurer que NavigationElementsService est un singleton:

export class NavigationModule {
    static forRoot() {
        return {
            ngModule: NavigationModule,
            providers: [NavigationElementsService]
        };
    }
}

Et lors de l'importation:

imports: [
        NavigationModule.forRoot()
       ]

Résultat: Même problème que d'habitude, subscribe() ne se déclenchant pas, mais au moins je sais qu'il existe une instance de NavigationElementsService.

16
SrAxi

Je pense que le problème est votre type Subject Avec Subject, aucun composant qui s'abonne après le déclenchement d'un événement ne recevra de valeur. Pour relire la valeur précédente aux abonnés en retard, utilisez BehaviorSubject ou ReplaySubject au lieu de Subject.

En savoir plus sur les différents types de sujets de http://reactivex.io/documentation/subject.html

Sujet du comportement

Lorsqu'un observateur s'abonne à un BehaviorSubject, il commence par émettre l'élément le plus récemment émis par l'observable source (ou une valeur de départ/valeur par défaut si aucun n'a encore été émis), puis continue d'émettre tout autre élément émis ultérieurement par l'observable source ( s).

ReplaySubject

ReplaySubject émet à tout observateur tous les éléments qui ont été émis par le ou les observables source, quel que soit le moment où l'observateur s'abonne.

Un BehaviorSubject nécessite une valeur par défaut lors de sa configuration. Vous devez donc le créer avec une sorte de valeur:

updateIBOsNavigationSubject = new BehaviorSubject<any>('');
updateIBOsNavigation$ = this.updateIBOsNavigationSubject.asObservable();

updateIBOsNavigation(navigationData) {
    log.d('updateIBOsNavigation', JSON.stringify(navigationData));
    this.updateIBOsNavigationSubject.next(navigationData);
}

Un ReplaySubject ne nécessite pas de valeur par défaut.

[~ # ~] modifier [~ # ~]

Lors de l'utilisation d'un service partagé, il est également important de s'assurer que le service est fourni UNIQUEMENT au niveau du module racine. Si plusieurs emplacements sont fournis, les composants peuvent ne pas obtenir la même instance. Même si le service est fourni au niveau racine, si un module entre le niveau racine et votre composant fournit le service, une nouvelle instance sera envoyée dans cette branche de l'arborescence Injection de dépendances.

J'espère que cela t'aides.

35
Tyler Jennings

Lorsque nous incluons un service dans les "fournisseurs", il est instancié et cet état est maintenu entre ses composants ainsi que ses composants enfants.

Et, si nous incluons le service dans le tableau de fournisseur des deux composants, il ne suit plus singleton. L'Etat sera indépendant et non partagé entre eux.

Donc, n'incluez votre service qu'au composant parent ou à votre composant racine.

J'ai moi aussi fait face à ce problème et résolu avec l'aide de cette solution ici, https://stackoverflow.com/a/38034298/5730167 .

5
lalith kumar