FacundoGFlores FacundoGFlores - 1 month ago 10
TypeScript Question

Performing advanced http requests in rxjs

I have the following objects:

class Question {
idQuestion: string;
question: string;
typeQuestion: string;
}

class Answer {
idAnswer: string;
idQuestion: string;
answer: string;
}

class Option {
idOption: string;
idQuestion: string;
option;
}


And I want to populate the following object:

class QuestionOptionsAnswer {
question: Question;
answer: Answer;
options: Option[];
}


For now, I have a service for each kind of object, so we can illustrate it in the following way:

questionService.getQuestions();
answerService.getAnswers();
optionService.getOptions();


To populate a
questionoptionsanswer
object I have doing nested requests:

questionService.getQuestions()
.subscribe(
answerService.getAnswers()
.subscribe(
optionService.getOptions()
.subscribe();
)
)


I can populate correctly the
questionoptionsanswer
object, but is is slow, so I think I am making a bad approach. The idea behind having a
questionoptionsanswer
is for rendering in a easy way the
html
with angular2 directives.

I read about
flatMap
,
switchMap
,
forkJoin
but I am not quite sure in how to use them.

Which could be a good approach to load this data?

questionoptionsanswer
will have a question object, an answer associated with it, and its possible options depending on the
typeQuestion
i.e: select, radio, multiple, etc.

Answer

So you need to call the first request and wait for its response and then call the two other requests (for options and the answer) at the same time.

Since I want to know when both responses are ready I'll use Observable.forkJoin() operator that emits a single array with all values of source Observables when they completed and then add data to the qoa variable that I'll just pass to the Observer when it subscribes.

Operators such concat() or concatMap() are good to make multiple HTTP calls in order but not very useful when you need to create multiple Observable to construct one large response you want to emit (QuestionOptionsAnswer in your case).

I also used Observable.of(...) to simulate HTTP requests. I don't know what's your usecase so you'll maybe don't use Observable.create() and use Subject instead:

function getQOA() {
    return Observable.create(observer => {

        Observable.of({ question_id: '1' }).subscribe(response => {
            var qoa = new QuestionOptionsAnswer();
            let question = new Question();
            question.idQuestion = response.question_id;

            qoa.question = question;

            let obs1 = Observable.of({ answer_id: '1', answer: 'bla' });
            let obs2 = Observable.of([{ option_id: '1', option: 'ble' }]);

            Observable.forkJoin(obs1, obs2).subscribe(responses => {
                let [answerResponse, optionsResponse] = responses;

                let answer = new Answer();
                answer.idAnswer = answerResponse.answer_id;
                answer.answer = answerResponse.answer;
                qoa.answer = answer;

                qoa.options = optionsResponse.map(o => {
                    let option = new Option();
                    option.idOption = o.option_id;
                    option.option = o.option;
                    return option;
                });

                observer.next(qoa);
            });
        });
    });
}

getQOA().subscribe(qoa => console.log(qoa));

Prints to console:

QuestionOptionsAnswer {
  question: Question { idQuestion: '1' },
  answer: Answer { idAnswer: '1', answer: 'bla' },
  options: [ Option { idOption: '1', option: 'ble' } ] }