Angular 6: use a service to get local json data

Viewed 12560

I have a movies.json that contain a list of movies and I want to create a MoviesServices to get the data where I want.

My MoviesServices:

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

@Injectable({
  providedIn: 'root'
})

export class MoviesService {

  movies: string[];

  constructor(private httpService: HttpClient) {
    this.getMovies();
  }

  getMovies() {
    this.httpService.get('../../assets/movies.json').subscribe(
      data => {
        this.movies = data as string[];
        console.log(this.movies); // My objects array
      },
      (err: HttpErrorResponse) => {
        console.log(err.message);
      }
    );
    console.log(this.movies); // Undefined
  }

}

Firstly, I have no idea why the first console.log() works and the second not, can you tell me why ?

Here is my component where I need to get the data:

import { Component, OnInit } from '@angular/core';
import { MoviesService } from '../services/movies/movies.service';

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

export class MoviesComponent implements OnInit {
  title = 'films-synopsys';
  movies;

  constructor(private myService: MoviesService) {}

  ngOnInit() {
    console.log(this.myService.movies); // Undefined
  }
}

Of course this is not working. Can you tell me how must I do ? I'm newbie angular

3 Answers

So basically you need to return an Observable from your service and then subscribe to it from your Component. You can then assign your response to the Component property movies

Try this:

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

@Injectable()
export class MoviesService {

  constructor(private httpService: HttpClient) { }

  getMovies() {
    return this.httpService.get('../../assets/movies.json');
  }

}

And in your Component:

import { Component } from '@angular/core';
import { MoviesService } from './movies.service';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  title = 'films-synopsys';
  movies;

  constructor(private myService: MoviesService) {}

  ngOnInit() {
    this.myService.getMovies()
      .subscribe(res => this.movies = res);
  }
}

Here's a Sample StackBlitz for your ref.

Change your method to return an Observable which you can subscribe to:

import { Observable } from 'rxjs/Observable';
...
getMovies(): Observable<string []> {
    this.httpService.get('../../assets/movies.json').subscribe(
      data => {
        this.movies = data as string[];
        return this.movies;
      },
      (err: HttpErrorResponse) => {
        console.log(err.message);
      }
    );
  }

In your calling code:

import { Subscription } from 'rxjs/Subscription';

this.myService.getMovies().subscribe(movies => {
  console.log(movies); // My objects array
}

The reason the first console log works is because you are doing it within an observable's subscription. Subscriptions have three states, Next, Error, Complete and so when you console log the first time, within the subscription next state you get the value that was pushed out from the event stream.

In your component the reason why it doesn't work is due to the fact that observables are lazy, and that you need to initialize the data by calling this.myService.getMovies() first to make the subscription happen.

A better way to do this would been to pass observables around and use async pipe in the html template.

Related