在Angular 2 v2.0.1中,onInit被调用两次。 (显然我在做一次调用时也做错了,但现在不是问题)
这是我的Plunker:http://plnkr.co/edit/SqAiY3j7ZDlFc8q3I212?p=preview
这是服务代码:
import {Injectable} from '@angular/core';
import {Http, Response} from '@angular/http';
import {Observable} from 'rxjs/Rx';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
@Injectable()
export class DemoService {
constructor(private http:Http) { }
// Uses http.get() to load a single JSON file
getData() {
return this.http.get('./src/data.json')
.map((res:Response) => res.json())
.do(data => console.log(data))
.subscribe(data => {
return <PageContent[]>data;
}, error => console.log("there was an error!"));
}
}
export class PageContent {
constructor(public _id: string,
public tag: string,
public title: string,
public body?:string,
public image?: string) {}
}
...以及使用它的简单组件。
//our root app component
import {Component, NgModule, OnInit } from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
import { DemoService, PageContent } from './service';
import { HttpModule } from '@angular/http';
@Component({
selector: 'my-app',
template: `
<div>
<h2>Hello {{name}}</h2>
</div>
<div *ngFor="let page of pages">
{{ page.title }}
</div>
`
})
export class App implements OnInit {
name:string;
pages: PageContent[] = [];
constructor(private _service: DemoService) {
this.name = 'Angular2'
this.loadData(); // <-- this is called once
}
ngOnInit() {
//this.loadData(); // <-- this is called twice
}
loadData(){
this.pages = this._service.getData();
console.log(this.pages);
}
}
@NgModule({
imports: [ BrowserModule, HttpModule ],
declarations: [ App ],
providers: [DemoService],
bootstrap: [ App ]
})
export class AppModule {}
免责声明:这是错误的,但是当从构造函数调用服务方法时,您可以看到它被提供一次,但是当它在ngOnInit钩子内时会被调用两次。
我的问题是,为什么从OnInit函数调用两次?
更新:所有答案的解决方案:
这是新的服务方法:
getData() {
return this.http.get('./src/data.json')
.map((res:Response) => res.json() as PageContent[]);
}
...这是新的组件方法:
loadData(){
this._service.getData()
.subscribe(data => this.pages = data);
}
答案 0 :(得分:10)
您的subscribe
应放在组件中而不是服务中。作为您的组件的原因是订阅到从服务返回的数据,稍后您可以根据需要取消订阅或添加更多控制(例如拒绝)。更改后,代码将如下所示。
在您的组件中:
ngOnInit() {
this.loadData();
}
loadData(){
this._service.getData().subscribe(data => this.pages = data);
}
在您的服务中:
getData() {
return this.http.get('./src/data.json')
.map((res:Response) => res.json());
}
答案 1 :(得分:3)
this._service.getData()
返回主题,而不是PageContent列表。您可以更改loadData
之类的内容:
loadData() {
this._service.getData().subscribe(data => this.pages = data);
console.log("Load data !");
}
并删除subscribe
方法的getData
部分(来自DemoService
)。我刚测试了这个,ngOnInit
被调用一次
答案 2 :(得分:1)
在英语中,当您订阅流(Observable
)时,订阅块内第一个函数内的代码将在该observable发出数据时执行。
如果您订阅两次,它将被调用两次,等等
由于您多次订阅,订阅块内的第一个函数(称为下一个函数)将被执行多次。
您应该只在ngOnInit
内订阅一个流。
如果要将数据发送到流上,可以使用RXJS主题,然后让主题随后使用RXJS flatmap发送到您订阅的流。
答案 3 :(得分:1)
好的我的第二个答案......看看这有帮助......
return subject.asObservable().flatMap((emit: any) => {
return this.http[method](url, emit, this.options)
.timeout(Config.http.timeout, new Error('timeout'))
// emit provides access to the data emitted within the callback
.map((response: any) => {
return {emit, response};
})
.map(httpResponseMapCallback)
.catch((err: any) => {
return Observable.from([err.message || `${err.status} ${err.statusText}`]);
});
}).publish().refCount();
其中subject是您要发出的RXJS主题(使用subject.next()方法)