Асинхронные вызовы Angular2

#asynchronous #angular

#асинхронный #угловой

Вопрос:

Я столкнулся с классической проблемой http race, но я недостаточно знаком с Angular2 и Observable, чтобы разрешить мой случай.

Вот описание моих кирпичей :

 [ FormComponent ]
    |    |
   1|    |6
    |    |
[ MetadataService ] <----4 amp; 5----> [ TranslatorService ]
    |    |
   2|    |3
    |    |
 [ Backend ]
  
  1. Мой компонент формы запрашивает у MetadataService метаданные формы
  2. Metadataservice выполняет HTTP-вызов серверной части для извлечения этих метаданных
  3. Серверная часть возвращает метаданные в формате JSON
  4. MetadataService передает JSON в TranslatorService
  5. TranslatorService выполняет некоторые переводы в формате JSON и возвращает массив в MetadataService
  6. 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?