Reputation: 277
I need to bind template data variables from a service. Service uses HTTP to retrieve data in JSON format. I get the data right, but because request is so asynchronous return of Service is always undefined.
How can I sell asynchronous data into the template? Without the use of callback?
AppComponent:
import {Component} from 'angular2/core';
import {RouteConfig, Router, ROUTER_DIRECTIVES} from 'angular2/router';
import {SystemComponent} from "./system.component";
import {MenuProvider} from "./providers/menuprovider";
import {Menu} from "./entity/menu";
import {Http, Headers, HTTP_PROVIDERS} from 'angular2/http';
@Component({
selector: 'app',
templateUrl: '/templates/layout',
directives: [ROUTER_DIRECTIVES]
})
@RouteConfig([
{
path: '/',
redirectTo: ['/System'],
useAsDefault: true
},
{
path: '/-1/...',
name: 'System',
component: SystemComponent
}
])
export class AppComponent {
menusItems:Array<Menu>;
constructor(router: Router, menuProvider:MenuProvider){
this.menusItems = menuProvider.getMenuItems(1);
router.navigate(['/System']);
}
}
MenuProvider:
import {Menu} from "../entity/menu";
import {IndexedCollection} from "../collections/IndexedCollection";
import {Injectable} from 'angular2/core'
import {Http, Headers, HTTP_PROVIDERS} from 'angular2/http';
@Injectable()
export class MenuProvider{
_menuItems:Array<Menu>;
private _http:Http;
constructor(http:Http){
this._http = http;
}
getMenuItems(shopId:number){
this.loadMenuForShopId(shopId);
return this._menuItems;
}
private loadMenuForShopId(shopId:number){
var base = this;
this._http.get('/services/menu/list/' + shopId)
.subscribe(
function(res){
res = res.json();
for(index in res['menuItems']){
var menu = res['menuItems'][index];
base._menuItems.push(new Menu(menu['url'], menu['name']));
}
}
);
}
}
MenuEntity:
export class Menu{
private _url;
private _name;
constructor(url:string,name:string){
this._url = url;
this._name = name;
}
get url() {
return this._url;
}
get name() {
return this._name;
}
}
Template:
<ul>
<li *ngFor="#menu of menusItems">
<a>{{menu.getName()}}</a>
</li>
</ul>
Upvotes: 1
Views: 353
Reputation: 41264
Take a look at Observable; it's your new best friend (;
You can make "cold" observable (just describe what it should do):
getMenuItems(shopId:number){
return this._http
.get('/services/menu/list/' + shopId)
.map(res => res.json())
.map(res => {
let menu = res.menuItems;
return new Menu(menu.url, menu.name)
})
}
... and let angular subscribe to it using AsyncPipe:
<ul>
<li *ngFor="#menu of menusItems |async">
<a>{{menu.getName()}}</a>
</li>
</ul>
AsyncPipe works on Observables, Promises and Events...
transform(obj: Observable<any>| Promise<any>| EventEmitter<any>, args?: any[]) : any
Upvotes: 2
Reputation: 775
Please don't use a synchronous HTTP request. This can block your browser from running any code until after the request has completed.
The best solution, in my opinion, is to keep the request asynchronous but to show a message to the user, such as "Loading..." while you retrieve the data.
You can use a ngIf
directive along with a variable that will become true after the request is completed.
Upvotes: 1