1

I have a simple Rails5 API and I'm trying to get data from the API in my Angular application.

First of, if I visit: http://localhost:3000/movies I get this result:

[{"id":2,"title":"Batman","created_by":"1","created_at":"2017-06-17T07:19:35.000Z","updated_at":"2017-06-17T07:19:35.000Z"}]

So the Rails side works.

In my app.component.ts I import:

import { AppService }         from "./app.service";
import { Movie }              from './movie';

movie.ts

export class Movie{
  id: number;
  title: string;
}

app.service.ts

import { Injectable } from '@angular/core'; import { Http, Response } from '@angular/http';

import { Observable }           from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';

import { Movie }                from './movie';

@Injectable()
export class AppService{

  private moviesUrl = 'http://localhost:3000/movies';

  constructor(private http: Http){

  }

  getMovies(): Observable<Movie[]>{
    return this.http.get(this.moviesUrl)
      .map(this.extractData)
      .catch(this.handleError);
  }

  private extractData(res: Response){
    let body = res.json();
    return body.data || {};
  }

  private handleError (error: Response | any) {
    // In a real world app, you might use a remote logging infrastructure
    let errMsg: string;
    if (error instanceof Response) {
      const body = error.json() || '';
      const err = body.error || JSON.stringify(body);
      errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
    } else {
      errMsg = error.message ? error.message : error.toString();
    }
    console.error(errMsg);
    return Observable.throw(errMsg);
  }

}

And finally my app.component.ts

import { Component }          from '@angular/core';
import { AppService }         from "./app.service";
import { Movie }              from './movie';

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

export class AppComponent {
  title = 'app works!';
  errorMessage: string;
  movies: Movie[];
  mode = 'Observable';

  constructor(private  appService: AppService){}

  ngOnInit(){
    this.getMovies();
    console.log(this.movies);
  }

  getMovies(){
    this.appService.getMovies()
      .subscribe(
        movies => this.movies = movies,
        error => this.errorMessage = <any>error);
  }
}

When I visit http://localhost:3000/movies in my browser I get the following in my Puma log:

Started GET "/movies" for 127.0.0.1 at 2017-06-17 10:35:00 +0200 Processing by MoviesController#index as HTML Movie Load (1.0ms) SELECT movies.* FROM movies Completed 200 OK in 6ms (Views: 4.0ms | ActiveRecord: 1.0ms)

When I visit http://localhost:4200/ where my Angular application is I get

Started GET "/movies" for 127.0.0.1 at 2017-06-17 10:37:59 +0200 Processing by MoviesController#index as HTML Movie Load (0.5ms) SELECT movies.* FROM movies Completed 200 OK in 4ms (Views: 2.8ms | ActiveRecord: 0.5ms)

So the Angular application does the request to the API and the API returns the result but that result does not show up in the Angular component.

Jota.Toledo
  • 27,293
  • 11
  • 59
  • 73
Peter Boomsma
  • 8,851
  • 16
  • 93
  • 185
  • 1
    You are not getting any data in `console.log(this.movies)` called in the `ngOnInit()`, right ? – Abrar Jun 17 '17 at 08:43
  • 4
    Possible duplicate of [How do I return the response from an Observable/http/async call in angular2?](https://stackoverflow.com/questions/43055706/how-do-i-return-the-response-from-an-observable-http-async-call-in-angular2) – eko Jun 17 '17 at 08:45
  • 1
    @Abrar correct. So yes, maybe the http.get does receive data but doesn't Angular first do the `this.getMovies` function and when that is complete the `console.log` in ngOnInit? – Peter Boomsma Jun 17 '17 at 08:47
  • the console.log method will be executed independent of the http request is completed or not. You should move that inside of the subscribe method, in the success case – Jota.Toledo Jun 17 '17 at 08:49
  • @PeterBoomsma, take a look at the answer I gave below – Abrar Jun 17 '17 at 09:07

1 Answers1

0

In Angular we use Observable, which you import from rxjs/Observable, to handle asynchronous code.

Notice here that when you call this.appService.getMovies() which returns an Observable<Movie[]>, this is essentially an asynchronous operation. In the AppComponent you subscribe to it and get the value Asynchrously.

Notice the AppComponent code -

 export class AppComponent {
  title = 'app works!';
  errorMessage: string;
  movies: Movie[];
  mode = 'Observable';

  constructor(private  appService: AppService){}

  ngOnInit(){
    this.getMovies();
    console.log(this.movies); <--- the value is not set to this.movies yet since data is loading asynchronously 
  }

  getMovies(){
    this.appService.getMovies()
      .subscribe(
        (movies) => {
              this.movies = movies;
              console.log(this.movies);  <--- the value will be set here properly
        },

        (error) => {
              this.errorMessage = <any>error);
        }
  }
}

Hope this helps.

EXTRA

For more reading on Observable

Abrar
  • 6,874
  • 9
  • 28
  • 41