Первый шаг, если ваша функция getQuestion преобразуется в Observable.
Почему это необходимо?Потому что вам нужно вызвать this.http.get (element.optionsUrl).Это асинхронно (все возвращаемые http.get наблюдаемые).И вам нужно дождаться окончания звонка, чтобы получить данные.Преимущество наблюдаемого в том, что внутри «функции подписки» у вас есть данные.
Поэтому мы должны думать, что «службы возвращают наблюдаемые, компонент подписывается на службы».
Ну,пусть вопрос.Основная проблема в том, что нам нужно несколько звонков на http.get.Как мы знаем, все вызовы http являются асинхронными, так как можно быть уверенным, что у нас есть все данные (помните, что у нас есть только данные в функцию подписки. Поскольку мы не хотим иметь несколько подписок, лучше всего иметьв нашем сервисе нет подписки, нам нужно использовать forkJoin. ForkJoin нужен массив вызовов и вернуть массив результатов.
Итак, сначала нужно создать массив наблюдаемых, а затем мы возвращаем этот массив наблюдаемых.Подождите минутку! Мы не хотим возвращать массив с опциями, нам нужны наблюдаемые вопросы. Для этого, несмотря на возврат массива наблюдаемых, мы возвращаем объект, который использует этот массив наблюдаемых.пример внизу ответа
getQuestions():Observable<any[]> { //See that return an Observable
let questions: any = [];
//First we create an array of observables
let observables:Observable<any[]>[]=[];
this.jsonData.forEach(element => {
if (element.elementType === 'dropdown') {
observables.push(this.http.get(element.optionsUrl))
}
}
//if only want return a forkjoin of observables we make
//return forkJoin(observables)
//But we want return an Observable of questions, so we use pipe(map)) to transform the response
return forkJoin(observables).pipe(map(res=>
{ //here we have and array like-yes is an array of array-
//with so many element as "dowpdown" we have in question
// res=[
// [{ "key": 'average', "value": 'Average' },...],
// [{ "key": 'car', "value": 'dog },...],
// ],
//as we have yet all the options, we can fullfit our questions
let index=0;
this.jsonData.forEach((element) => { //see that have two argument, the
//element and the "index"
if (element.elementType === 'textbox') {
questions.push(new TextboxQuestion(element));
} else if (element.elementType === 'dropdown') {
//here we give value to element.options
element.option=res[index];
questions.push(new DropdownQuestion(element));
index++;
}
})
return question
}))
}
ПРИМЕЧАНИЕ: как преобразовать функцию, которая возвращает значение в наблюдаемом, используя «of»: Простой пример
import { of} from 'rxjs';
getData():any
{
let data={property:"valor"}
return data;
}
getObservableData():Observable<any>
{
let data={property:"observable"}
return of(data);
}
getHttpData():Observable<any>
{
return this.httpClient.get("myUrl");
}
//A component can be call this functions as
let data=myService.getData();
console.log(data)
//See that the call to a getHttpData is equal than the call to getObservableData
//It is the reason becaouse we can "simulate" a httpClient.get call using "of"
myService.getObservableData().subscribe(res=>{
console.log(res);
}
myService.getHttpData().subscribe(res=>{
console.log(res);
}
ПРИМЕЧАНИЕ2: использование forkJoin иmap
getData()
{
let observables:Observables[];
observables.push(of({property:"observable"});
observables.push(of({property:"observable2"});
return (forkJoin(observables).pipe(map(res=>{
//in res we have [{property:"observable"},{property:"observable2"}]
res.forEach((x,index)=>x.newProperty=i)
//in res we have [{property:"observable",newProperty:0},
// {property:"observable2",newProperty:1}]
}))
}
Обновление Есть другой способ сделать вещи. Я думаю, что лучше, если есть функция, которая возвращает заполненные "вопросы".
//You have
jsonData:any=....
//So you can have a function that return an observable
jsonData:any=...
getJsonData()
{
return of(this.jsonData)
}
//Well, what about to have a function thah return a fullFilled Data?
getFullFilledData()
{
let observables:Observables[]=[];
this.jsonData.forEach(element => {
if (element.elementType === 'dropdown') {
observables.push(this.http.get(element.optionsUrl))
}
})
return forkJoin(observables).pipe(map(res=>
let index = 0;
this.jsonData.forEach((element) => {
if (element.elementType === 'dropdown') {
element.options = res[index];
index++;
}
})
return this.jsonData
}))
}
Таким образом, вам не нужно менять компонент.Если вы вызываете getFullfilledData, у вас есть (в подписке) данные
см. stackblitz