问题
I am migrating some code from Angular1 to Angular2 and having a few issues. I can open a json response, populate a template, and access data from template ng functions but have not been able to access the data directly from the component class. From what I have read and the error messages seen Angular2 http / observable does not seem to return a pure json object, so I suspect I need to remap it, but not sure how. I believe it should also be possible to drop back to promises using onPromise but have not managed to get that working. I have spent a lot of time googling for solutions, and have tried to implement most of them, but no luck. If anyone can advise on how to remap the response to a usable format or directly access data in the response it would be greatly appreciated.
Example http call from the service :-
getExam() {
return this._http.get('/json/exam.json')
.map(data => data.json());
}
Example subscribe :-
ngOnInit() {
this._examsService.getExam()
.subscribe(response => this.exam = response);
console.log(this.exam.length); //this fails
}
Example console log error :-
TypeError: Cannot read property 'length' of undefined in [null]
Example data structure (very simplified for testing) :-
{"title":"My Practice Exam",
"questions":[
{"question":"1+1 = ",
"answers":[
{"answer":"2","correct":"Y","selected":"N","iscorrect":""},
{"answer":"5","correct":"N","selected":"N","iscorrect":""}]},
{"question":"2+2 = ",
"answers":[
{"answer":"4","correct":"Y","selected":"N","iscorrect":""},
{"answer":"7","correct":"N","selected":"N","iscorrect":""}]},
{"question":"3+3 = ",
"answers":[
{"answer":"6","correct":"Y","selected":"N","iscorrect":""},
{"answer":"8","correct":"N","selected":"N","iscorrect":""}]}]}
In Angular1 I was able to directly access data from functions - e.g as follows, and would like to do similar in Angular2
if ($scope.myExams[0].questions[q].answers[a].correct == 'y') ...
回答1:
With this code
ngOnInit() {
this._examsService.getExam()
.subscribe(response => this.exam = response);
console.log(this.exam.length); //this fails
}
the first line sends the request this._examsService.getExam()
.subscribe(...)
and registers interest in the response, then console.log(this.exam.length)
is executed, but at this time respone => this.exam = response
hasn't been execute yet, because getExam()
is not yet done connecting to the server and receiving the response.
You need to stay in the chain of events to work with the data that is returned eventually, like:
ngOnInit() {
this._examsService.getExam()
.subscribe(response => {
this.exam = response;
console.log(this.exam.length); //this shoudn't fail anymore
});
}
I don't know if this solves your problem but your question doesn't provide enough information about your requirements for a more elaborate solution.
回答2:
I think that the following case is the normal behavior:
ngOnInit() {
this._examsService.getExam()
.subscribe(response => this.exam = response);
console.log(this.exam.length); //this fails
}
because you try to access the length
property on the exam
object that will set later and when the response will be there (in the subscribe
method).
That said, when an error is thrown within the observable, the map
operator isn't called. If you want to transform the error response, you can leverage the catch
operator, as described below:
this._examsService.getExam()
.subscribe(
// Success
response => this.exam = response,
// Failure
response => {
// Do something
});
and the corresponding service code:
getExam() {
return this.http.get('http://...')
.map(res = > res.json())
.catch(res => {
// If you want to extract the JSON error
// message from the response
return Observable.throw(res.json());
});
}
Otherwise you can also leverage the async
pipe to directly set the observable on the component and not do the subscribe:
this.exam = this._examsService.getExam();
and in the associated template
<ul>
<li *ngFor="#e of exam | async">{{e.name}}</li>
</ul>
Hope it helps you, Thierry
来源:https://stackoverflow.com/questions/34925882/angular2-http-observables-how-to-work-with-undefined-type