Hello
Hello

Reputation: 816

string interpolation not updating the value from service

I have simple component to test string interpolation. In html,

<p>{{value}}</p>

The ts file:

export class HelloComponent implements OnInit {
   value: any;
   constructor(private service: ApiService) {
        this.value = 'test';
   }
   ngOnInit() {
       this.getValue();
   }
   getValue = (): void => {
       this.value = this.service.getValue();
   }

Now since I injected the service so let's look at it.

@Injectable({
    providedIn: 'root'
})
export  class ApiService {
      url: string;
      value: any;
      constructor(private http: HttpClient) {
           this.url = 'http://localhost:8080/api/test';
      }
      getValue = () => {
          this.http.get(this.url, {responseType: 'text'}).pipe(take(1)).subscribe(
                  data => {
                      this.value = data;
                      console.log(data); // 'Hello World' is printed out.
                      return data;
                  }
          );
      }
 }

My question is that I can see the correct value in console log. But I can't get the value in the component. It is undefined value so on the screen it is nothing.

Upvotes: 2

Views: 1813

Answers (1)

Barremian
Barremian

Reputation: 31145

That is not how asynchronous data fetched using observables work. You could return the observable from the service and subscribe where the data is required.

Service

@Injectable({ providedIn: 'root' })
export class ApiService {
  url: string;

  constructor(private http: HttpClient) {
    this.url = 'http://localhost:8080/api/test';
  }

  getValue(): Observable<any> {
    return this.http.get(this.url, { responseType: 'text' })
  }
}

Component

export class HelloComponent implements OnInit {
   value: any;
 
   constructor(private service: ApiService) {
      this.value = 'test';
   }
 
   ngOnInit() {
      this.getValue();
   }
 
   getValue(): void {
      this.service.getValue().subscribe({
        next: data => this.value = data,
        error: error { }
      });
   }
}

Please go through this answer in it's entirety to understand why your method wasn't working.

Update: close open subscription

As mentioned in my comment, Angular HTTP observables complete after a single emission. So a take(1) would be redundant. You could however assign the subscription to a variable and call .unsubscribe() on it or use takeUntil().

I prefer takeUntil() operator since it could be used to close multiple subscriptions with a single push to an observable.

import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

export class HelloComponent implements OnInit, OnDestroy {
   value: any;
   close$ = new Subject<any>();
 
   constructor(private service: ApiService) {
      this.value = 'test';
   }
 
   ngOnInit() {
      this.getValue();

      this.getOtherValue();  // <-- illustration
   }
 
   getValue(): void {
      this.service.getValue().pipe(
        takeUntil(this.close$)    // <-- close stream when `close$` emits
      ).subscribe({
        next: data => this.value = data,
        error: error { }
      });
   }

   getOtherValue(): void {  // <-- illustration
      this.service.getOtherValue().pipe(
        takeUntil(this.close$)    // <-- close stream when `close$` emits
      ).subscribe();
   }

   ngOnDestroy() {
     this.close$.next();    // <-- close both open subscriptions
   }
}

You could refer this post for more concise ways to close open subscriptions.

Upvotes: 1

Related Questions