Angular 4 Data in Service, passing to component(s)

Chris Rutherford picture Chris Rutherford · Aug 2, 2017 · Viewed 8k times · Source

Have some data in a service, and was working perfectly when I had data in an object sitting on the service, but now that I've hooked up a database connection, the data never makes it to the component.

I want the service to subscribe to the data coming back from the database and have defined the call like this:

  public setPerson(ac: string): void{
    console.log(ac);
    this.generatePerson(ac).subscribe((data) => {
        // this.mapPersonFromInput(data[0]);
        console.dir(data);
    });

  }

The mapPersonFrominput() function is a holdover from mocked data. it is essentially the same as extractData further below, but from a static object in the code.

generatePerson looks like this:

public generatePerson(id: string):Observable<Person>{
    var datRetUrl: string = '/api/'
    var fullUrl: string = datRetUrl + id;
    return this.http.get(fullUrl)
                .map(this.extractData)
                .catch(this.handleError);
  }

extractData simply assigns values from the input object to the service's object structure, and handleerror simply logs the error to the console.

I call the service to initialize the data object from the component before the component loads by calling this function from a navigation component:

  passCodeToService():void{
    this.psn.setPerson(this.accessCode);
    this.route.navigate(['/name']);
  }

and in the actual component that should get the data, I'm using ngOnInit, but I think I should be using ngOnChanges to initialize the component. Here's the code that I'm currently using, but haven't had luck fixing just yet.

ngOnInit() {
  this.name =this.psn.getName();
  console.log(this.name);
}

getName() simply returns the object that I'm storing in the service.

  public getName(): Name{
    return this.servicePerson.name;
  }

Answer

Lansana Camara picture Lansana Camara · Aug 2, 2017

You should not use ngOnChanges here, it is not meant for what you are trying to do.

According to your question, this is what you are trying to achieve:

  • Fetch data from DB
  • Your component should be able to get a piece of that data asynchronously

You can achieve this with the code you have. All you need to do is add a little more code and leverage RxJS. More specifically:

  • Create a person subject in your person service
  • When data comes back from your DB, do personSubject.next(dataFromDB) to add to the person stream.
  • Create a function that will return the person subject as an observable, and then you can subscribe to that observable from your component

With this approach, every time data comes from your DB, that data will be added to a person stream, and anything subscribing to that stream (your component) will get the data.

Quick example (since I don't have your full code):

import { ReplaySubject } from 'rxjs/ReplaySubject';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class PersonService {
    // The person subject
    personStream: ReplaySubject<Person> = new ReplaySubject();

    // The person observable
    person$(): Observable<Person> {
        return this.personStream.asObservable();
    }

    // Get person from DB and add to stream
    getDataFromDB() {
        this.http.get(url).subscribe(response => {
            this.personStream.next(response.person);
        });
    }
}

@Component({...})
export class MyComponent implements OnInit {
    person: Person;

    constructor(private personService: PersonService) {}

    ngOnInit() {
        // Subscribe to person observable. Any time person data changes, this will be called.
        this.personService.person$().subscribe(person => this.person = person);

        // If you return the `this.http.get(url)` from `getDataFromDB`, you can just do this instead...
        this.personService.getDataFromDB().subscribe(person => this.person = person);
    }
}

But this is all overkill, because in reality all you need to do is subscribe to the getDataFromDB function in your component, since that itself can return an observable of Person type.