简体   繁体   中英

Angular: how to subscribe a component to a data service?

I've read all the Angular documentation and 89 million blog posts and I just get more confused. I have a button in my header for the user to select their preferred language:

  <mat-menu>
    <button (click)="change_L1_Language('en-US')">English<img width="50" alt="Flag of the United States" src="../assets/images/flags/us-flag.svg"></button>
    <button (click)="change_L1_Language('es')">Español<img width="50" alt="Flag of Spain" src="../assets/images/flags/es-flag.svg"></button>
    <button (click)="change_L1_Language('zh')">中文<img width="50" alt="Flag of China" src="../assets/images/flags/ch-flag.svg">&nbsp; 中文</button>
    <button (click)="change_L1_Language('jp')">日本人<img width="50" alt="Flag of Japan" src="../assets/images/flags/jp-flag.svg" class="flag-border">&nbsp; 日本人</button>
  </mat-menu>

The button works great, the language in the header changes when the user clicks the button.

The component works fine:

import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { L1LanguageService } from '../l1-language.service';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.css'],
})

export class HeaderComponent implements OnInit {
  public L1_Language: string = 'en-US';

  constructor(
    private l1LanguageService: L1LanguageService,
  ) {}

  change_L1_Language(language: string) {
    this.l1LanguageService.changeL1Language(language);
  }
}

I made a service l1-language.service.ts that receives the data from the button:

import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';

@Injectable({
  providedIn: 'root'
})

export class L1LanguageService {
  changeL1Language(language): Observable<any>{
    console.log(language);
    return of(language)
  }
}

The service works too. When I click the button, the new language appears on the console:

es                     l1-language.service.ts:13

The only bit that I don't understand is what's in the angle brackets: Observable<any> . Any what?

Now I want to send the data to a component. Here's my component:

import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { L1LanguageService } from '../../l1-language.service';

@Component({
  selector: 'app-landing-computer',
  templateUrl: './landing-computer.component.html',
  styleUrls: ['./landing-computer.component.css']
})
export class LandingComputerComponent implements OnInit{
  L1_Language: string = 'en-US'; // English is the initial or default language
  observeLanguage: Observable<L1LanguageService>; // the service to be observed

  constructor(
    private l1LanguageService: L1LanguageService,
  ) {
    this.observeLanguage = l1LanguageService.changeL1Language(this.L1_Language) as Observable<L1LanguageService>;
  }

ngOnInit() {
    this.observeLanguage.subscribe(language => console.log(language));  // does nothing
    this.observeLanguage.changeL1Language.subscribe(language => console.log(language));  // "Cannot read property 'subscribe' of undefined"
    this.l1LanguageService.subscribe(language => console.log(language)); // "not a function"
    this.l1LanguageService.changeL1Language.subscribe(language => console.log(language)); // "not a function"
  }
}

This logs the default value en-US when the app loads, but nothing logs from the component when I click the button.

This looks right to me:

import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { L1LanguageService } from '../../l1-language.service';

Nothing wrong with this:

@Component({
  selector: 'app-landing-computer',
  templateUrl: './landing-computer.component.html',
  styleUrls: ['./landing-computer.component.css']
})

The third line I'm not so sure about:

export class LandingComputerComponent implements OnInit{
  L1_Language: string = 'en-US'; // English is the initial or default language
  observeLanguage: Observable<L1LanguageService>; // the service to be observed

Is the purpose of the third line to create an instance named observeLanguage of the class Observable that observes the class L1LanguageService ?

The constructor completely baffles me:

 constructor(
    private l1LanguageService: L1LanguageService,
  ) {
    this.observeLanguage = l1LanguageService.changeL1Language(this.L1_Language) as Observable<L1LanguageService>;
  }

Huh? What's the smelly dumpster fire in the curly brackets?

Every blog post has a different way to subscribe to a service so I put in four, none of which work:

ngOnInit() {
    this.observeLanguage.subscribe(language => console.log(language));  // does nothing
    this.observeLanguage.changeL1Language.subscribe(language => console.log(language));  // "Cannot read property 'subscribe' of undefined"
    this.l1LanguageService.subscribe(language => console.log(language)); // "not a function"
    this.l1LanguageService.changeL1Language.subscribe(language => console.log(language)); // "not a function"
  }

The first line doesn't throw an error but it doesn't do anything.

The second line throws "Cannot read property 'subscribe' of undefined".

The third and fourth lines throw "not a function".

Why are there some many moving parts to a simple service subscription?

The only bit that I don't understand is what's in the angle brackets: Observable. Any what?

This defines which type is 'returned' in the observable. So basically, in this Observable any value is accepted... To describe explicitly what type is in the observable, you could say Observable, cause your languages are sent as a string.

Code problems

  1. <button (click)="change_L1_Language('en-US')"> ==> you're using the method change_L1_Language which isn't defined in your component
  2. this.observeLanguage = l1LanguageService.changeL1Language(this.L1_Language) as Observable<L1LanguageService>; doesn't make any sense... You already have an observable in your service method which you can access via this.l1LanguageService.changeL1Language
  3. this.l1LanguageService.changeL1Language.subscribe(language => console.log(language)); ==> This one was very close, but you're subscribing to the definition of your method... you should call your function and subscribe to it (cause it returns an observabel)

Something like below code should work for you to console log the set value for language:

ngOnInit() {
    this.l1LanguageService.changeL1Language(this.L1_Language).subscribe(language => {
        console.log(language)
    });
}

UPDATE: BUT above code will only subscribe to the initially set value, because you're subscribing to a method which is called only once...

A complete solution for you would be to extend your L1LanguageService to something like below code (some untested pseudo code, so check Observable documentation for more info if needed). This way you can subscribe to the L1Language subject and you will be notified on every change of the subject

export class L1LanguageService {
  public L1Language : Subject;
  
  changeL1Language(language) : void {
    console.log(language);
    this.L1Language.next(language);
  }
}

In your ngOnInit you can subscribe to the subject like this this.L1LanguageService.L1Language.subscribe(...)

More info on https://rxjs-dev.firebaseapp.com/guide/subject

This should work for you:

export class LandingComputerComponent implements OnInit{
  L1_Language: string = 'en-US'; // English is the initial or default language

  constructor(
    private l1LanguageService: L1LanguageService
  ) { }

  ngOnInit() {
    this.l1LanguageService.changeL1Language(this.L1_Language).subscribe(language => {
      console.log(language);
    });
  }
}

Observable is for any type of data what You wnt observeI haven't seen anyone watching the service yet try this:

在此处输入图像描述

This is result:

在此处输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM