How do I transfer data between a shared class and an angular 2 component?

1.2k views Asked by At

I am making calls to my back-end service using an independent TypeScript class. I am able to console.log the captured data when instantiating the instance of my shared class. However, I am having trouble accessing the local properties and methods of that class inside my angular component.

Here is my component:

import { Component, OnInit } from '@angular/core';
import { ProjectService } from '../shared/projectService';
import { HttpClient } from '@angular/common/http';

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

export class AboutComponent implements OnInit {

  projects: any;
  windowWidth: number;
  service: ProjectService;

  constructor(private httpClient: HttpClient) { 
    this.service = new ProjectService(this.httpClient);
    //  returns as undefined
    console.log(this.service.getAllProjects());   
  }

  ngOnInit() {
    this.windowWidth = window.innerWidth;
  }

}

Here is my shared class module:

import { HttpClient } from '@angular/common/http';

interface Project {
    demoURL: string,
    githubURL: string,
    imgFileName: string,
    name: string,
    stack: Array<string>
}

export class ProjectService {

    private configURL = `https://someURL.herokuapp.com/getAllProjects`;
    projects: any;
    constructor(private httpClient: HttpClient) {

        this.httpClient.get(this.configURL).subscribe(resp => {
            this.projects = resp;
        });
    }

    getAllProjects() {
        return this.projects;
    }
}

As you can see,

I want to populate my local variable projects inside my ng component using this.service.getAllProjects(). When I try to log the response from my shared class ProjectService the function response is undefined.

When I console.log inside ProjectService constructor after I initialize the class using new I can see that my class was able to capture the response.

Why is it doing this? Also, how do I fix it?

Thanks guys.

3

There are 3 answers

0
thtsau On BEST ANSWER

Okay, so what worked for me was using EventEmitter to emit the projects when the http request completed, as mentioned by user1986938.

This is what I did:

HttpService:

import { HttpClient } from '@angular/common/http';
import { Injectable, EventEmitter } from '@angular/core';
import { Project } from '../shared/project.interface';

@Injectable()
export class ProjectService {

    private configURL = `https://someURL.herokuapp.com/getAllProjects`;
    public httpResponse = new EventEmitter<Project[]>();
    public projects: Project[];

    constructor(private httpClient: HttpClient) {
        this.httpClient.get(this.configURL).subscribe((res: Project[]) => {
            this.projects = res;
            this.httpResponse.emit(this.getProjects());
        });
    }

    getProjects() {
        return this.projects.slice();
    }
}

Component:

import { Component, OnInit } from '@angular/core';
import { ProjectService } from '../shared/project.service';
import { Project } from '../shared/project.interface';

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

export class AboutComponent implements OnInit {

  projects: Project[];
  windowWidth: number;

  constructor(private projectService: ProjectService) { 
  }

  ngOnInit() {
    this.windowWidth = window.innerWidth;

    this.projectService.httpResponse.subscribe(([first, second]: [Project, Project]) => {
        console.log(first);
        console.log(second);
    });
  }

}
0
sasensi On

As pointed by user1986938, your problem is that your API call is asynchronous and you have to wait for the API response to be able to do something with your data.
In Angular, you can easily do that relying on RxJs library with which Angular is tightly coupled.

Here is a working example that you can easily adapt to your case.

You can see that in this example I use a ReplaySubject because it is able to provide value again to any new subscriber, even after your API call is completed.
Concretely, this allow you to query the list of all your projects from your ProjectService multiple times and possibly from different parts of your app, without having to do the real API call more than once and without having to care about anything else.

If your not familiar with ReactiveX concepts, I suggest you read this great documentation, and for your case, this part which is about Subjects.

component

export class AppComponent
{
    // this property allows passing projects to your template
    projects: string[];

    // you can directly inject your service into your component constructor
    constructor ( private projectService: ProjectService )
    {
        // here you subscribe to you projects query observable
        this.projectService.getAll().subscribe(projects =>
        {
            // now you can do what you want with your projects
            console.log('projects: ', projects);
            // here, we store projects in component property to display them in template
            this.projects = projects;
        });
    }
}

service

export class ProjectService
{
    // this property is used to store your API response
    private projectsSubject: ReplaySubject<any>;

    constructor ()
    {
        // create subject
        this.projectsSubject = new ReplaySubject();

        // do API call
        this.doFakeAPICall().subscribe(projects =>
        {
            // store response as subject value
            this.projectsSubject.next(projects);
        });
    }

    getAll (): Observable<any>
    {
        // return subject as observable
        return this.projectsSubject.asObservable();
    }

    // replace this be your real API call
    private doFakeAPICall (): Observable<any>
    {
        // here we simulate a response delay of one second
        return of([ 'project 1', 'project 2' ]).pipe(delay(1000));
    }
}
0
user1986938 On

You are not waiting for response to complete when instantiating ProjectService object. Projects property is not set yet. I would suggest you use behaviorSubject for projects property. You can read about subjects and behaviorSubject here.