Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
576 views
in Technique[技术] by (71.8m points)

angular - 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.

I currently have a Service that gets data from a json server:

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. 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. 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() {
    this.getUsers();
    console.log('ngOnit after getUsers() ' + this.users);
  }

Dev console screenshot:

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This is because this.getUsers() and then this.userService.getUsers().subscribe(...) only schedules a call to make a request to the server. 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.

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.

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.

I also changed function () to () =>. This way this works inside the following code block () => { ... }, otherwise it wouldn't.

Don't forget to add

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

otherwise these operators won't be recoginzed.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...