Как вернуть ответ от вызова Observable/http/async в angular2?
У меня есть служба, которая возвращает наблюдаемую информацию, которая отправляет http-запрос на мой сервер и получает данные. Я хочу использовать эти данные, но всегда получаю undefined
, В чем проблема?
Сервис:
@Injectable()
export class EventService {
constructor(private http: Http) { }
getEventList(): Observable<any>{
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.get("http://localhost:9999/events/get", options)
.map((res)=> res.json())
.catch((err)=> err)
}
}
Составная часть:
@Component({...})
export class EventComponent {
myEvents: any;
constructor( private es: EventService ) { }
ngOnInit(){
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
});
console.log(this.myEvents); //This prints undefined!
}
}
11 ответов
Причина:
Причина в том, что это undefined
является то, что вы делаете асинхронную операцию. Это означает, что потребуется некоторое время, чтобы завершить getEventList
метод (зависит в основном от скорости вашей сети).
Итак, давайте посмотрим на http-вызов.
this.es.getEventList()
После того, как вы на самом деле сделаете ("выстрелите") ваш http-запрос с subscribe
вы будете ждать ответа. Во время ожидания javascript выполнит строки под этим кодом, и если он встретит синхронные назначения / операции, он выполнит их немедленно.
Так что после подписки на getEventList()
и ждет ответа,
console.log(this.myEvents);
строка будет выполнена немедленно. И ценность этого undefined
до того, как ответ поступит с сервера (или к тому, что вы инициализировали в первую очередь).
Это похоже на выполнение:
ngOnInit(){
setTimeout(()=>{
this.myEvents = response;
}, 5000);
console.log(this.myEvents); //This prints undefined!
}
Решение:
Итак, как мы можем преодолеть эту проблему? Мы будем использовать функцию обратного вызова, которая является
subscribe
метод. Потому что, когда данные поступают с сервера, они будут внутриsubscribe
с ответом.
Итак, изменив код на:
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
console.log(this.myEvents); //<-- not undefined anymore
});
напечатает ответ.. через некоторое время.
Что ты должен делать:
Может быть много вещей, связанных с вашим ответом, кроме регистрации его; Вы должны сделать все эти операции внутри обратного вызова (внутри subscribe
функция), когда данные поступают.
Еще одна вещь, которую стоит упомянуть, это то, что если вы приехали из Promise
фон, then
обратный вызов соответствует subscribe
с наблюдаемыми.
Что вы не должны делать:
Вы не должны пытаться изменить асинхронную операцию на операцию синхронизации (не то, что вы можете). Одна из причин, по которой у нас есть асинхронные операции, состоит в том, чтобы не заставлять пользователей ждать завершения операции, пока они могут делать другие действия в этот период времени. Предположим, что для выполнения одной из ваших асинхронных операций требуется 3 минуты, если бы у нас не было асинхронных операций, интерфейс завис бы на 3 минуты.
Предлагаемое чтение:
Первоначальная благодарность этому ответу заключается в следующем: Как вернуть ответ от асинхронного вызова?
Но с выпуском angular2 мы познакомились с машинописью и наблюдаемыми, поэтому мы надеемся, что этот ответ охватывает основы обработки асинхронного запроса с помощью наблюдаемых.
Выполнение http-вызова в angular/javascript является асинхронной операцией. Поэтому, когда вы делаете http-вызов, он назначит новый поток, чтобы завершить этот вызов и начать выполнение следующей строки с другим потоком. Вот почему вы получаете неопределенное значение. так что внесите изменения, чтобы решить эту проблему
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
console.log(this.myEvents); //<-this become synchronous now
});
Вы можете использовать asyncPype, если вы используете myEvents только в шаблоне.
Вот пример с asyncPype и Angular4 HttpClient https://stackblitz.com/edit/angular-rhioqt?file=app%2Fevent.service.ts
Результат не определен, потому что угловой процесс асинхронен. Вы можете попробовать, как показано ниже:
async ngOnInit(){
const res = await this.es.getEventList();
console.log(JSON.stringify(res));
}
Observables ленивы, поэтому вы должны подписаться, чтобы получить значение. Вы правильно подписали его в своем коде, но одновременно зарегистрировали вывод за пределами блока "подписка". Вот почему это "неопределено".
ngOnInit() {
this.es.getEventList()
.subscribe((response) => {
this.myEvents = response;
});
console.log(this.myEvents); //Outside the subscribe block 'Undefined'
}
Так что, если вы зарегистрируете его внутри блока подписки, он будет правильно регистрировать ответ.
ngOnInit(){
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
console.log(this.myEvents); //Inside the subscribe block 'http response'
});
}
Здесь проблема в том, что вы инициализируете this.myEvents
в subscribe()
который является асинхронным блоком, пока вы делаете console.log()
только из subscribe()
блок. Так console.log()
звонить раньше this.myEvents
инициализируется.
Пожалуйста, переместите свой код console.log(), а также в subscribe(), и все готово.
ngOnInit(){
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
console.log(this.myEvents);
});
}
Для этого у вас есть 2 варианта:
Предположим, у нас есть служба, которая возвращает массив сведений о доставке:
getShippingPrices(): Observable<IShippingDetails[]> {
return this.http.get<IShippingDetails[]>('/assets/shipping.json');
}
1. Используйте асинхронный канал : простой способ, если вы просто хотите показать результат в шаблоне.
В классе компонента напрямую присвойте наблюдаемое переменной:
export class ShippingComponent implements OnInit {
shipOptions1 = this.cartService.getShippingPrices();
constructor(private cartService: CartService) {}
ngOnInit() {}
}
а затем используйте асинхронный канал в шаблоне:
<div *ngFor="let s of shipOptions1 |async">
<label>{{s.type}}</label>
</div>
Обратитесь: проверьте 4-й пункт в этом URL-адресе https://angular.io/start/start-data#configuring-the-shippingcomponent-to-use-cartservice
2. Используйте подписку : если вы хотите манипулировать ею или хотите выполнить некоторую бизнес-логику в ответе / из ответа.
export class ShippingComponent implements OnInit {
shipOptions2: IShippingDetails[] = [];
constructor(private cartService: CartService) {}
ngOnInit() {
this.cartService.getShippingPrices().subscribe(response => {
this.shipOptions2 = response;
//console.log(this.myEvents);
//All other code using shipOptions2
});
}
}
Также убедитесь, что вы сопоставили свой ответ с выводом json. В противном случае он вернет простой текст. Вы делаете это так:
getEventList(): Observable<any>{
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.get("http://localhost:9999/events/get", options)
.map((res)=>{ return res.json();}) <!-- add call to json here
.catch((err)=>{return err;})
}
Не определено, потому что значение здесь регистрируется до того, как какие-либо данные из службы будут установлены из указанного выше вызова службы подписки. Поэтому вам нужно дождаться завершения вызова ajax и установить данные из данных ответа.
getEventList(): Observable<any>{
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.get("http://localhost:9999/events/get", options)
.map((res)=> res.json())
.catch((err)=> err)
}
Здесь сделайте журнал консоли внутри метода подписки, который будет вести журнал, когда данные установлены в переменной myEvents.
ngOnInit(){
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
// This prints the value from the response
console.log(this.myEvents)
});
}
Вы можете просто попробовать этот метод
let headers = new Headers({'Accept': 'application/json'});
let options = new RequestOptions({headers: headers});
return this.http
.get(this.yourSearchUrlHere, options) // the URL which you have defined
.map((res) => {
res.json(); // using return res.json() will throw error
}
.catch(err) => {
console.error('error');
}
Вы можете использовать нижеприведенный подход для возврата значений из службы
Ниже представлена услуга:
export class AppServiceService {
constructor(private http: HttpClient) { }
getStudentDetails(): Observable<Student[]>{
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.get<Student[]>('http://localhost:8080/api/student',options);
}
}
В Component вы можете использовать метод подписки для загрузки данных, как показано ниже.
export class AppComponent {
title = 'angular-demo';
constructor(private appService: AppServiceService) { }
studentDetails: Student[];
ngOnInit(): void {
this.appService.getStudentDetails().subscribe(data => {
this.studentDetails=data;
},error=>{
console.log(error);
});
}
}
Для получения дополнительной информации см. URL-адрес ниже:
https://beginnersbug.com/call-http-rest-service-from-angular/