我有一个angular2和typescript应用程序,我使用angular2的http方法从服务中的数据库加载数据。我在onInit()期间触发组件内的服务。这工作正常,我可以加载数据。问题是我还想使用从onInit()函数内的服务加载的数据。当我尝试这样做时,我收到类似于下面的错误:
Error: Uncaught (in promise): TypeError: Cannot read property 'user_id' of undefined
TypeError: Cannot read property 'user_id' of undefined
以下是调用服务的组件的简化代码
export class ProfileComponent implements OnInit {
public profile: StaffProfile[];
constructor(private userService: UserService) {}
ngOnInit() {
this.userService.fetchProfile();
this.profile = this.userService.getProfile();
//I just want to be able to do anything once the data is loaded
console.log(this.profile[0].user_id);
}
}
以下是服务的简化代码
@Injectable()
export class WorkforceUserService implements OnInit {
private Profile: Profile[];
constructor(private http: Http) {
this.Profile = [];
}
public getProfile(){
return this.Profile;
}
public fetchStaffProfile(){
return this.http.get('http://localhost:3000/api/staff/1')
.map((response: Response) => response.json())
.subscribe(
(data) => {
var user_id = data.user_id || null;
var loadedProfile = new Profile(user_id);
this.Profile.push(loadedProfile);
}
);
}
}
我想要的只是当来自服务器的数据到达或刚刚更新时,能够在我的组件中触发一个功能。请让我知道你对如何实现这一点的想法。
提前谢谢。
答案 0 :(得分:1)
因为你调用fetchStaffProfile是异步进程,然后你立即调用getProfile,返回值为空,只需更改为:fetch将返回observable / promise,然后在调用它时,你订阅它。
@Injectable()
export class WorkforceUserService {
constructor(private http: Http) {
}
public fetchStaffProfile(){
return this.http.get('http://localhost:3000/api/staff/1')
.map((response: Response) => response.json());
}
}
在组件中,例如
export class ProfileComponent implements OnInit {
public profile: StaffProfile;
constructor(private userService: UserService) {}
ngOnInit() {
this.userService.fetchStaffProfile()
.subsribe(res => {
// do some transform data
this.profile = res;
console.log(this.profile);
}
}
}
答案 1 :(得分:1)
只需订阅这样的结果:
ngOnInit() {
this.userService.fetchProfile().subscribe(() => {
this.profile = this.userService.getProfile();
console.log(this.profile[0].user_id);
});
}
答案 2 :(得分:1)
涉及同步和传播的经典场景异步世界。 ( TL; DR - 我建议的解决方案低于)
因此,这是ngOnInit()
运行时所期望的流程:
1. (Component) Ask the service to fetch the profile
2. (Service) Fetch the profile
3. (Service) Extract the user_id from the profile received and create new profile
4. (Service) Push the profile into this.Profile
5. (Component) Set this.profile as service's Profile
6. (Component) Print profile's first entry that was fetched and configured in the service.
您实际获得的流程是:
1 => 2 => 5 => 6 (fails, but hypothetically) => 4 => 5.
在同步世界中:
this.profile = this.userService.getProfile();
同时,在异步世界中:
this.Profile
。但是,在它发生之前,ngOnInit尝试访问未定义的第一个项元素的属性user_id
。
所以,在这种情况下你需要的是保持异步世界,在这个领域,rxjs提供了非常酷的well documented工具集来处理这种情况。
天真的解决方案 - fetch方法将返回一个Promise,而不是返回订阅,它将在ngOnInit中解析。
// WorkforceUserService
public fetchStaffProfile() {
return this.http.get('http://localhost:3000/api/staff/1')
.map((response: Response) => response.json())
.toPromise()
.then((data) => {
var user_id = data.user_id || null;
var loadedProfile = new Profile(user_id);
this.Profile.push(loadedProfile);
});
// trying to explain my point, don't forget to catch promise errors
}
// ProfileComponent
ngOnInit() {
this.userService.fetchProfile().then(() => {
// this lines are called when http call was done, as the promise was resolved
this.profile = this.userService.getProfile();
console.log(this.profile[0].user_id);
});
}
Rxjs样式解决方案 - 保留一个Subject类型的配置文件数组,该组件将订阅:
// WorkforceUserService
this.Profile = new Subject<Profile[]>(); // the subject, keep it private and do not subscribe directly
this.Profile$ = this.Profile.asObservable(); // expose an observable in order to enable subscribers.
public fetchStaffProfile(){
return this.http.get('http://localhost:3000/api/staff/1')
.map((response: Response) => response.json())
.subscribe(
(data) => {
var user_id = data.user_id || null;
var loadedProfile = new Profile(user_id);
this.Profile.next([loadedProfile]);
});
}
// ProfileComponent
export class ProfileComponent implements OnInit {
public profile: StaffProfile[];
constructor(private userService: UserService) {
// here you can subscribe to the Profile subject, and on each call to 'next' method on the subject, the provided code will be triggered
this.profile = this.userService.getProfile();
console.log(this.profile[0].user_id);
}
ngOnInit() {
// here, we'll ask the service to start process of fetching the data.
this.userService.fetchProfile();
}
}
无论您的问题是什么,可能会有所帮助:
this.Promise
=&gt;就{js命名约定而言this.promise
更好。var
是oldschool。请改用让或 const 。 see angular styleguide This article可能会对使用observables有一些启示,详细的例子和说明。