1

I am trying to subscribe to a service I am writing so I can update an mat-autocomplete input when a language dropdown is changed.

the stackoverflow question I found is;

Delegation: EventEmitter or Observable in Angular

However, when I implement this the subscription method is not firing? I have the following;

My app.component.ts;

import { Component } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { TranslationEmitterService } from '../../services/translation.emitter.service';

@Component({
    selector: 'app',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css'],
    providers: [ TranslationEmitterService ]
})
export class AppComponent {
    constructor(private translate: TranslateService, private translationService: TranslationEmitterService) {
        translate.addLangs(["en", "pt", "fr"]);
        translate.setDefaultLang('pt');

        let browserLang = translate.getBrowserLang();
        translate.use(browserLang.match(/en|pt|fr/) ? browserLang : 'en');
    }


}

My Component html;

<div class="dropdown">
    <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
        Select Language
    </button>
    <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
        <a class="dropdown-item" (click)="switchLanguage('pt')"><span class="flag-icon flag-icon-gr"></span> Portugese</a>
        <a class="dropdown-item" (click)="switchLanguage('en')"><span class="flag-icon flag-icon-gr"></span> English</a>
        <a class="dropdown-item" (click)="switchLanguage('fr')"><span class="flag-icon flag-icon-gr"></span> French</a>
    </div>
</div>

My Observable Service;

import { Injectable } from '@angular/core'
import { BehaviorSubject } from 'rxjs/BehaviorSubject';

@Injectable()
export class TranslationEmitterService {
    private _languageChangedSource = new BehaviorSubject<any>(0);
    languageChange$ = this._languageChangedSource.asObservable();


    changeLanguage(lang) {
        console.log("in change language " + lang);
        this._languageChangedSource.next(lang);
    }
}

my component for switching the language which calls the change;

import { Injectable, Inject, Component } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';

import { TranslationEmitterService } from '../../../services/translation.emitter.service';

@Component({
    selector: 'nav-menu',
    templateUrl: './navmenu.component.html',
    styleUrls: ['./navmenu.component.css']
})
export class NavMenuComponent {
    public isCollapsed = true;
    public status = false;

    constructor(private translate: TranslateService, private translationEmitterService: TranslationEmitterService) {

    }

    public switchLanguage(language: string) {
        console.log("switching language");
        this.translate.use(language);
        this.translationEmitterService.changeLanguage(language);
    }
}

and finally I subscribe to the event in my component;

ngOnInit() {

    this.translationSubscription = this.translationService.languageChange$.subscribe(

        // lang => this.refreshAutoCompleteItems(lang)
        x => console.log(x)
    );
}

A copy of the component is here pastebin

No when I trigger the switchLanguage method i get the output switching language and in change language fr output, but the component observing is not triggering?

Can anyone tell me what I am doing wrong here?

Matthew Flynn
  • 3,661
  • 7
  • 40
  • 98
  • Try subscribing to this.translationService._languageChangedSource directly. I don't think asObservable is needed. I have written code without that and it works. – Vinod Bhavnani Apr 06 '18 at 12:20
  • It is needed if you care about the encapsulation. You might only want to expose the observable stream but keep the emiting logic behind the service method's facade. – Tomasz Kula Apr 06 '18 at 12:34
  • Your code looks fine, does console shows any error? – Vikas Apr 06 '18 at 12:42
  • @Vikas no there are no erros I can see either, its just the `console.log(x)` is not firing. – Matthew Flynn Apr 06 '18 at 12:50
  • @Vikas also to add when the program runs initially it does fire the subscription as '0' is logged for the value of x? – Matthew Flynn Apr 06 '18 at 13:16
  • @MatthewFlynn your code seems I triggered switchLanguage function inside my component constructor and passed "test" as parameter I got all three console.logs – Suvethan Nantha Apr 06 '18 at 13:26
  • @MatthewFlynn check the demo I attached if you are still getting the error. – Suvethan Nantha Apr 06 '18 at 13:35
  • how are you invoking the switchLanguage() method? – Vikas Apr 06 '18 at 13:45
  • can you share your html – Vikas Apr 06 '18 at 13:45
  • @Vikas yes I have added more detail to my code including the dropdown html and also provided a pastbin link for the component (I have stripped out some of the bumpf in this though so if you cant see anything I can provide the whole component code) – Matthew Flynn Apr 06 '18 at 13:55
  • I think it could be down to this maybe? https://stackoverflow.com/questions/38033723/angular-2-observable-subscription-not-triggering – Matthew Flynn Apr 06 '18 at 13:57
  • again just to update, I updated the provider so it was injected at the app.comonent.ts level and added this to the question, but it is still not firing? – Matthew Flynn Apr 06 '18 at 14:38
  • @MatthewFlynn register your service in `(@NgModule.providers)` in app.module.ts – Vikas Apr 06 '18 at 17:23

3 Answers3

5

One possible cause of the problem is that each of the two components has its own instance of the service. When one component calls the changeLanguage service method, the other component does not receive the notification because it has subscribed to the other service instance.

To make sure that a single instance of the service is shared by the two components, it should be provided only once. This can be accomplished by providing the service at the module level only and by removing it from the providers list of the components:

@NgModule({
    providers: [
        TranslationEmitterService 
    ],
    ...
})
export class AppModule {
}
ConnorsFan
  • 70,558
  • 13
  • 122
  • 146
0

You need to map your service call.

this._languageChangedSource.next(lang).map(r=>r.json());

this.translationEmitterService.changeLanguage(language).map(r=>r.json());

Map is required in service when you are using subscribe in your component.

Also your service has to return an Observable. To which your component method can subscribe.

Dheeraj Kumar
  • 3,917
  • 8
  • 43
  • 80
  • but my service does return an observable? Also adding `map` to the `.next` throws an error `map does not exist on type void` even when I have the relevant import on the service. – Matthew Flynn Apr 06 '18 at 13:13
  • Thats because your service isnt returning anything. It should return Observable. – Dheeraj Kumar Apr 06 '18 at 13:19
  • I'm not fully understanding what you mean sorry? The service stream is observable and the element I am subscribing to. – Matthew Flynn Apr 06 '18 at 13:24
0

Check the Demo below

Working Demo

private _languageChangedSource = new BehaviorSubject<any>(0);

You have passed 0 as intial value when instantiating the BehaviorSubject. That's why you are getting intial value as 0.

Note When instantiating BehaviorSubject we need to give a default value. It's a must.

this.switchLanguage("test");

I passed test as the switchLanguage function parameter and I got below logs.

switching language
in change language test
test

I hope this will resolve your problem. If you have any problem let me know.

Suvethan Nantha
  • 2,404
  • 16
  • 28