0

I am new to Angular and Observables. I have a service that receives a URL I want to pass to my observable and render data every time a new URL is passed to it with NgFor. Any example would help. Please help nothing seems to work. Thanks in advance

SERVICE File

import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Event } from '../event/event.interface';
import { map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})

export class ClientService {

  eventUrl ;

  constructor(private http: HttpClient) {
  }

 receiveEvent(eventId){
    this.eventUrl = eventId;
   console.log(this.eventUrl)
 }

renderEvent(eventUrl): Observable<Event[]>{
  return this.http
  .get<Event[]>(eventUrl)
}

RECEIVING COMPONENT file

import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { ClientService } from '../service/clientService';
import { HttpParams, HttpClient } from '@angular/common/http';


@Component({
  selector: 'hero-review',
  templateUrl: './review.component.html',
  styleUrls: ['./review.component.css']
})
export class ReviewComponent implements OnInit {

public events = [];

  constructor(private _clientService: ClientService) { 
  }

  ngOnInit() {

    this._clientService.renderEvent('Need to be dynamic URL').subscribe(data => this.events = data);
  }
}

SENDING COMPONENT file

sendEvent(eventId){
  let reqUrl = "myEndpoint" + eventId;
  this._clientService.receiveEvent(reqUrl);
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Jamal
  • 21
  • 9
  • This looks fine syntactically. Could you provide a bit more information about the specific area you're struggling with, including examples of what you've tried so far. – Kurt Hamilton Feb 09 '20 at 17:08
  • Hi Kurt I updated the example. The sending file sends the url to the service. The service needs to pass it to the Observable making the GET request. Every time a URL is passed I would like Observable to render the requested data – Jamal Feb 09 '20 at 17:25
  • I still don't understand what isn't working here. Is the receiving component loaded at some point after the sending component has updated the url in the service? – Kurt Hamilton Feb 09 '20 at 17:43
  • The Observable is not receiving the dynamic URL when passed to the service. I'm not sure where it's failing at. Every time a URL is sent I would like it to be updated – Jamal Feb 09 '20 at 17:58
  • I trying your example but can't determine what Subject type is. Do in need to import that? – Jamal Feb 09 '20 at 18:30
  • Subject itself is fine in this case. Subscribers will only receive values that are emitted after they have subscribed. You could use ReplaySubject to ensure that subscribers receive the last emitted value (if any) upon subscription. It's down to your use case. – Kurt Hamilton Feb 09 '20 at 18:39
  • I've also added an example of how to unsubscribe from the subscription, as you'll want to do that to avoid memory leaks. – Kurt Hamilton Feb 09 '20 at 18:43

2 Answers2

1

I think I understand your problem.

The http.get(url) is a single-use observable. It's not a stream that can be triggered multiple times like subjects can. You will need to create your own subject and use that as a proxy.

export class ClientService {

 constructor(private http: HttpClient) {
 }

 private eventPublisher: Subject<Event[]> = new Subject<Event[]>();

 receiveEvent(eventId){
   // call http client here, and publish results to event publisher
   const eventUrl = eventId;
   this.http.get<Event[]>(eventUrl).subscribe(response => {
     this.eventPublisher.next(response);
   });
 }

 renderEvent(eventUrl): Observable<Event[]>{
  // do not subscribe to http client here, subscribe to event publisher
  return this.eventPublisher.asObservable();
 }

This is the pub-sub design pattern (publisher - subscriber). It might be useful to read up on other solutions to this design pattern using Angular, as this is a fairly trivial example.

Edit:

You will also want to unsubscribe in your component once you're done with the subscription to avoid memory leaks.

Something like this is a simple pattern:

export class MyComponent implements OnInit, OnDestroy() {
  constructor(private myService: MyService) { }

  private destroyed: Subject<void> = new Subject<void>();

  ngOnInit(): void {
    this.myService.myMethod().pipe(
      takeUntil(() => this.destroyed)
    ).subscribe(() => { 
      // do stuff
    });
  }

  ngOnDestroy(): void {
    this.destroyed.next(undefined);
    this.destroyed.complete();
  }
}

Although it does get repetitive if you need to start doing it a lot. I personally use this method: https://stackoverflow.com/a/45709120/5367916

Kurt Hamilton
  • 12,490
  • 1
  • 24
  • 40
0
// This sample using angular 9/10
// Component name:  list.component.ts

import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { NgZone} from '@angular/core';
import {HttpServiceService} from './../http-service.service';

@Component({
  selector: 'app-list',
  templateUrl: './list.component.html',
  styleUrls: ['./list.component.scss']
})
export class ListComponent implements OnInit {

  constructor(
     private http: HttpServiceService,
     private zone: NgZone
    ) { }
  brews: object;
  ngOnInit(): void {
      this.http.getPeople().subscribe(data => {
      this.brews = data;
      alert(JSON.stringify(this.brews));
      console.log(this.brews);
      });
  }
}
//--------------------------------

// This is my http service
// Component Name: http-service.service.ts

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

@Injectable({
  providedIn: 'root'
})
export class HttpServiceService {

  constructor() { }
  getPeople(): Observable<any>{
    const myNameArray = [{
      firstName: 'Algem',
      lastName: 'Mojedo',
      age: 63
    }];
    return of(myNameArray);
 }
}

//--------------------------
// should display in alert

localhost:4200 says
[{"firstName":"Algem","lastName":"Mojedo","age":"60"}]

Happy coding..