简体   繁体   English

Angular2 - OnInit:从服务的订阅函数返回的值没有分配给组件字段

[英]Angular2 - OnInit : Values returned from Service' subscribe function does not get assigned to Component field/s

I'm trying out Angular2 and have been following their tutorials.我正在尝试 Angular2 并一直在关注他们的教程。

I currently have a Service that gets data from a json server:我目前有一个从 json 服务器获取数据的服务:

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

import { Observable } from 'rxjs/Observable';

import { User } from './user';

@Injectable()
export class UserService {
  constructor(private http: Http) {}

  private usersUrl = 'http://localhost:3000/users';

  getUsers(): Observable<User[]> {
    return this.http.get(this.usersUrl) //the request won't go out until something subscribes to the observable
                    .map(this.extractData)
                    .catch(this.handleError); // pass an error message back to the component for presentation to the user but only if we can say something the user can understand and act upon
  }

  private extractData(response: Response) {
    if (response.status < 200 || response.status >= 300) {
       throw new Error('Bad response status: ' + response.status);
    }
    let body = response.json(); // parse JSON string into JavaScript objects

    return body.data || { };
  }

  private handleError (error: any) {
    // In a real world app, we might send the error to remote logging infrastructure before returning/sending the error
   let errMsg = error.message || 'Server error'; // transform the error into a user-friendly message

   return Observable.throw(errMsg); // returns the message in a new, failed observable
  }

}

And my Component:还有我的组件:

import { Component, OnInit } from '@angular/core';

import { User } from '../common/user/user';
import { UserService } from '../common/user/user.service';

@Component({
  selector: 'app-nav',
  templateUrl: '/app/nav/nav.component.html',
  styleUrls: ['/app/nav/nav.component.css']
})
export class NavComponent implements OnInit {
  errorMsg: string;
  users: User[];

  constructor(private userService: UserService) { }

  getUsers() {
    this.userService
    .getUsers()
    .subscribe(
      function(users) {
        console.log('users ' + users);
        this.users = users;
        console.log('this.users ' + this.users);
      }, function(error) {
        console.log('error ' + error);
      });
    // users => this.users = users,
    // error => this.errorMsg = <any>error);
  }

  ngOnInit() {
    this.getUsers();
    console.log('ngOnit after getUsers() ' + this.users);
  }
}

My problem is, the data returned from the subscribed function is not being passed/assigned to my Component property 'users' after the getUsers() invocation completes.我的问题是,在 getUsers() 调用完成后,从订阅函数返回的数据没有传递/分配给我的组件属性“用户”。 I do know that the data is returned to the component from the service method call because I'm able to log the data within the userService().getUsers method.我确实知道数据从服务方法调用返回到组件,因为我能够在 userService().getUsers 方法中记录数据。 The weird thing is, the console.log invocation on my ngOnInit is being printed on my dev console first before the console.logs within my getUsers method, although I invoke the getUsers first:奇怪的是,我的 ngOnInit 上的 console.log 调用首先在我的 getUsers 方法中的 console.logs 之前打印在我的开发控制台上,尽管我首先调用了 getUsers:

  ngOnInit() {
    this.getUsers();
    console.log('ngOnit after getUsers() ' + this.users);
  }

Dev console screenshot:开发控制台截图:

This is because this.getUsers() and then this.userService.getUsers().subscribe(...) only schedules a call to make a request to the server.这是因为this.userService.getUsers().subscribe(...) this.getUsers()this.userService.getUsers().subscribe(...)只安排调用向服务器发出请求。 Eventually when the response from the server arrives ( console.log('ngOnit after getUsers() ' + this.users); was already executed before the call to the server was even made) then the function you passed to subscribe() is executed.最终当来自服务器的响应到达时( console.log('ngOnit after getUsers() ' + this.users);在调用服务器之前已经执行了)然后你传递给subscribe()的函数被执行.

This should do what you want:这应该做你想做的:

  getUsers() {
    return this.userService
    .getUsers()
    .map(
      (users) => {
        console.log('users ' + users);
        this.users = users;
        console.log('this.users ' + this.users);
      })
     .catch((error) => {
        console.log('error ' + error);
        throw error;
      });
    // users => this.users = users,
    // error => this.errorMsg = <any>error);
  }

  ngOnInit() {
    this.getUsers().subscribe(_ => {;
      console.log('ngOnit after getUsers() ' + this.users);
    });
  }

In getUsers() I use map() instead of subscribe, so we can subscribe later in order to be able to get code executed when the response arrived.getUsers()我使用map()而不是 subscribe,因此我们可以稍后订阅,以便能够在响应到达时执行代码。

Then in ngOnInit() we use the subscribe() ( subscribe() is necessary, otherwise would http.get() would never be executed) and pass the code we want to be executed when the response arrives.然后在ngOnInit()我们使用subscribe()subscribe()是必要的,否则http.get()将永远不会被执行)并在响应到达时传递我们想要执行的代码。

I also changed function () to () => .我还将function ()更改为() => This way this works inside the following code block () => { ... } , otherwise it wouldn't.这样this可以在下面的代码块() => { ... } ,否则就不会。

Don't forget to add不要忘记添加

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';

otherwise these operators won't be recoginzed.否则这些运算符将不会被识别。

for me this is works like magic i was need to get data from api to initialize datatable component对我来说,这就像魔术一样,我需要从 api 获取数据来初始化数据表组件

1- on your http service create normal get request like this : 1- 在您的 http 服务上创建正常的 get 请求,如下所示:

  public get(url: string):Observable<any> {
    return this.http.get<any>( url );
   }

2- on your component : 2- 在您的组件上:

constructor(private HttpSvc: HttpService) {}

  ngOnInit() {
    this.render();
  }    
render() {
        let options = this.options || {};
          this.HttpSvc.Get('your api url').subscribe(data => {
            this.Tableset = data;
            options = {
              data: this.Tableset.data,
              columns: this.Tableset.columns,
              dom: 'Bfrtip',
              buttons: this.Tableset.buttons,
              autoWidth: false,
              pageLength: 2,
              retrieve: true,
              responsive: true
            };
          }, err => {console.log(err); },
          () => {
            this.renderDetails(options); //<-- can call any other method after call end
          });
    }

here the full example link这里是完整的示例链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM