#asynchronous #angular
#асинхронный #угловой
Вопрос:
Я столкнулся с классической проблемой http race, но я недостаточно знаком с Angular2 и Observable, чтобы разрешить мой случай.
Вот описание моих кирпичей :
[ FormComponent ]
| |
1| |6
| |
[ MetadataService ] <----4 amp; 5----> [ TranslatorService ]
| |
2| |3
| |
[ Backend ]
- Мой компонент формы запрашивает у MetadataService метаданные формы
- Metadataservice выполняет HTTP-вызов серверной части для извлечения этих метаданных
- Серверная часть возвращает метаданные в формате JSON
- MetadataService передает JSON в TranslatorService
- TranslatorService выполняет некоторые переводы в формате JSON и возвращает массив в MetadataService
- MetadataService возвращает массив в FormComponent.
Архитектура кажется простой, но что-то не так в том, как я это реализую, поскольку в итоге, на мой взгляд, свойство метаданных «не определено», и поэтому * ngFor выдает ошибку Моей первой идеей было подписаться на наблюдаемое в компоненте, но я не знаю, как справиться с этими наблюдаемыми, поскольку у меня есть два сервиса.
На мой взгляд, проблема возникает с 4-го шага :
MetadataService
getFormMetaData(folderId: string, context: string){
let body = JSON.stringify({
folderId: folderId,
context: context
});
return this.http.post(this.uri, body, options)
.map( (res) => {
let body = res.json();
if (body.data){
return this.translatorService.getTranslatedElements(body.data);
}
}
);
}
Он должен возвращать наблюдаемое, но метод map() возвращает значение TranslatorService, которое является массивом…
Как мне следует поступить?
Редактировать : Вот мой FormComponent :
FormComponent
@Component({
template: `
<dynamic-form controls="formMetadata | async"></dynamic-form>
`,
providers: [MetadataService]
})
export class CustomerFormComponent {
formMetadata: any;
constructor(private metadataService: MetadataService) { }
ngOnInit() {
this.formMetadata = this.formMetadataService.getFormMetadata('FR75P00000012', 'UserInformation');
}
}
И мой TranslatorService :
TranslatorService
getTranslatedElements(metadata: any) {
debugger; // It never breaks here
let elements = [];
metadata.forEach( (field) => {
...
elements.push(field);
} )
return elements;
}
Обновить :
Хорошо, я немного продвинулся. Похоже, проблема возникает из моего асинхронного канала, который я использую в шаблоне FormComponent для передачи данных дочернему компоненту.
Должен ли этот дочерний компонент обрабатывать такого рода асинхронные данные также определенным образом?
Решение, как тратить часы на отладку: забудьте поместить свои свойства, связанные с данными, в квадратные скобки.
<dynamic-form controls="formMetadata"></dynamic-form>
должно быть
<dynamic-form [controls]="formMetadata"></dynamic-form>
Это объясняет, почему DynamicFormComponent получил строку в качестве входных данных…
Решение 2 На самом деле, у меня все еще была проблема с созданием экземпляра дочернего компонента еще до того, как функция subscribe загрузила данные. Я смог решить эту проблему, выполнив <dynamic-form [controls]="formMetadata" *ngIf="formMetadata"></dynamic-form>
Я не мог использовать асинхронный канал, он не работал. Я не знаю, когда асинхронный запуск в родительском шаблоне, но, похоже, это происходит после конструктора дочернего компонента () и noOnInit ()
Комментарии:
1. Как вы вызываете
MetadataService
в своем компоненте?2. @rinukkusu да, я делаю. Компонент вызывает MetadataService, а MetadataService вызывает TranslatorService, который принимает JSON в качестве параметра, выбирает несколько строк и возвращает массив объектов
3. Я спрашиваю «как». Не могли бы вы, пожалуйста, добавить это к своему вопросу? 🙂
4. @rinukkusu Извините, я слишком быстро прочитал ваш комментарий :). Я отредактировал и добавил некоторую информацию внизу
5. Правильно ли выглядит ответ от сервера? Не могли бы вы добавить
console.log(body)
послеres.json()
Ответ №1:
Как описано в http://reactivex.io/documentation/operators/map.html
Оператор Map применяет выбранную вами функцию к каждому элементу, генерируемому исходным наблюдаемым объектом, и возвращает наблюдаемый объект, который генерирует результаты этих функциональных приложений.
Таким образом, MetadataService должен возвращать наблюдаемое значение того, что возвращает this.translatorService.getTranslatedElements(body.data);
или наблюдаемое значение undefined в случае body.data пусто.
Комментарии:
1. Спасибо, это звучит довольно ясно в теории, но на практике я немного запутался. Могу ли я заставить MetadataService возвращать наблюдаемое значение
this.translatorService.getTranslatedElements(body.data);
?2.
.map
просто изменяет содержимоеObservable
.return
Внутри.map
возвращает только значение для лямбда-выражения, а не весь метод, который вызывает.post
.3. ДА. Импортируйте Observable и попытайтесь вернуть Observable.of(this.translatorService.getTranslatedElements(body.data));
4. Я думаю, @rinukkusu прав, фактически у меня уже есть наблюдаемый объект, возвращаемый моими
http.post()
элементами управления, так что все в порядке.map just modifies the content of the Observable. In my component, I bind this cold Observable directly to my view so that the async pipe subscribe. Still I have a
. forEach не является функцией (…)`5. Объявлен ли ‘formMetadata’ как наблюдаемый в FormComponent?