1

I'm learning Ang2. I ran the Hero tutorial successfully. to practice, I just added a link in the main page to a get a new component. there is a json file with a list of Radio Station. the following are my service, and the Component:

    import { Radio } from '../models/radio';

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

@Injectable()
export /**
 * RadioService
 */
    class RadioService {
    radios: Radio[] = [];
    len: Number = 0;
    http: Http;
    constructor(http: Http) {
        this.http = http;
    }
    getRadios() {
        console.log('Radios Service is being called');
        this.http.get('app/mock/FranceRadioJSON.json')
            .map((res: Response) => res.json())
            .subscribe((rad: Radio[]) => {      // Succes
                console.log('Radios Service Success');
                this.radios = rad;
                this.len = rad.length;
            }

            , err => {// Failure
                console.log('Radio Services Failure');
                console.error(err);
            }

            , // complete
            () => {
                console.log('Get Radios Complete');
                console.log('Records NB: ' + this.len);
                console.log('radios nb: ' + this.radios.length)
            }
            )
            ;
        return this.radios;

    }

and the Component is:

import { Component, OnInit } from '@angular/core';
import { RouteParams } from '@angular/router-deprecated';
import { Radio } from '../../models/radio';
import { RadioService } from '../../services/radio.service';

@Component({
    selector: 'radio'
    , templateUrl: 'app/html/radio.component.html'
    , providers: [RadioService]
})
export /**
 * RadioComponent
 */
    class RadioComponent {
    radios: Radio[] = [];
    constructor(
        private radioservice: RadioService) {
    }
    getRadios() {
        this.radios = this.radioservice.getRadios();
        console.log('radio component NB: ' + this.radios.length);
    }
    ngOnInit() {
        this.getRadios()


    }
}

the problem is the call of service is coming first, and no Radio is return while when the service is called with console.log I see that's is successful and get all records from JSON file. Any help would be greatly appreciated.

Frenchi In LA
  • 3,139
  • 3
  • 25
  • 41

2 Answers2

4

You can't get data this way from async calls (which is when you use Promise or Observable). Http returns and Observable. You need to subscribe() and in the callback you pass to subscribe(...) you get the data and can assign it to local properties.

And example how you can solve this:

In getRadios we don't call subscribe() and use map() instead. map() returns an Observable which allows the caller to get the data. subscribe() returns a Subscription and doesn't allow to get the response data, it only allows to cancel the subscription (which is also often handy, but not now ;-) ).

getRadios() {
    console.log('Radios Service is being called');
    return this.http.get('app/mock/FranceRadioJSON.json')
        .map((res: Response) => res.json())
        .map((rad: Radio[]) => {      // Succes
            console.log('Radios Service Success');
            this.radios = rad;
            this.len = rad.length;
        });
}

We subscribe in getRadios because here we want to get the data. When we subscribe, the map() calls in getRadios are executed as well. subscribe() makes the Observable actually do its work:

getRadios() {
    this.radios = this.radioservice.getRadios()
        .subscribe((radios) => {
           this.radios = radios;
        } , err => {// Failure
            console.log('Radio Services Failure');
            console.error(err);
        } , // complete
        () => {
            console.log('Get Radios Complete');
            console.log('Records NB: ' + this.len);
            console.log('radios nb: ' + this.radios.length)
        });

    // executed before the HTTP get call to the server
    // console.log('radio component NB: ' + this.radios.length);
}

Because data is not returned immediately but only when the callback passed to subscribe(...) is executed you can get errors for bindings like

<div>{{radios.xxx}}</div>

when radios is still null when Angular2 already resolves the bindings in the template. To avoid errors you can use the safe-navigation (Elvis) operator

<div>{{radios?.xxx}}</div>

Angular2 doesn't try to access .xxx while radios is null.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 1
    Thank you for your detailed explanation. I do see much better the process. – Frenchi In LA Jun 14 '16 at 17:40
  • How can I force the service to return Radio[], or should I convert the response in Component? – Frenchi In LA Jun 14 '16 at 18:09
  • You didn't provide the `Radio` class (or interface) and also not how the data looks like you get from the observable. Hard to tell without full information. http://stackoverflow.com/questions/22885995/how-do-i-initialize-a-typescript-object-with-a-json-object might be what you are looking for. – Günter Zöchbauer Jun 14 '16 at 18:14
  • let's say the Radio Class is: export class Radio { location: string; type: string; } and the jason files is a [] of this class – Frenchi In LA Jun 14 '16 at 18:25
  • instead of `.map((res: Response) => res.json())` do `.map((res: Response) => { result = res.json(); return result.map(r => new Radio(r.location, r.type);})` and in the `Radio` class add `constructor(private location, private type) {}` (you need to remove the `location` and `type` properties from the class because with `private xxx` in the constructor argument list they are already declared as class properties. – Günter Zöchbauer Jun 14 '16 at 18:52
1

The problem is that you are subscribing to your observable from within your service. Your service should return an Observable and then you Subscribe to it from your component.

So your service should be:

getRadios() : Observable<Radio[]> {
    return this.http.get('app/mock/FranceRadioJSON.json')
                    .map((res: Response) => <Radio[]>res.json())
}

And your component:

getRadios() {
    this.radios = this.radioservice.getRadios().subscribe(
        r => {
             this.radios = r;   
        }
        ...
    );
}
Morteza Manavi
  • 33,026
  • 6
  • 100
  • 83