I have an API where data is structured like this:
{
"questions"
|
|
"question": "What is 2 2?"
"options"
|
|
"Option 1": 2
"Option 2": 6
"Option 3": 1
"question": "What is the capitol of Sweden?"
"Option 1": "Stockholm"
"Option 2": "South America"
"Option 3": "Oceania"
}
I want to display one question and it's alternatives at a time, then on click of a "Next"-button display the next question. The amount of questions and whatnot changes, so it has to be dynamically rendered.
I figured I can't do it with *ngFor as I only want to render the next one upon clicking the button so I'm a bit unsure how to do it.
Might be possible to figure out the length of the "questions"-array and render the next one by saving which question-index you're currently at and change to the next one by doing something like
<h2>${questions[i].question}</h2>
<p *ngFor="option of questions[i].options">${questions[i].options}</p>
But I'm unsure exactly how that would be implemented.
CodePudding user response:
In angular, you should try to identify the component that can be reused.
If you create a single component that is responsible for showing a question and re-use it when clicking on next or previous to show the next/previous question.
Eg:
Create a component: <Question [input]="currentQuestion" (output)="doSomethingOnSelction(currentQuestion, $event)">
Now you have a component that can show different questions based on input and give you a selection you can store somewhere.
You can update the current question based on the next and previous button events. On next you can assign currentQuestion the next question in iteration.
CodePudding user response:
You almost got it, i'd do something like this:
<h2>${currentQuestion.question}</h2>
<div *ngFor="option of currentQuestion.options">${option}
<button (click)="getNextQuestion()">Next</button>
</div>
In the typescript file:
interface Question = ... // define data structure
ngOnInit(): void {
this.questions = ... // get questions from API
}
private _questionIndex: number = 0;
currentQuestion: Question = this.questions[this._questionIndex];
getNextQuestion(): void {
// handle case where the index is bigger than the array
...
// else
this.currentQuestion = this.questions[ this._questionIndex];
}
It's also possible to use an Observable and Async Pipe for this:
$question: BehaviorSubject<Question> = new BehaviorSubject<Question>();
ngOnInit(): void {
this.questions = ... // fetch from api
}
ngAfterViewInit(): void {
this.$question.next(this.questions[this._questionIndex]);
}
getNextQuestion(): void {
this.$question.next(this.questions[ this._questionIndex]);
}
<h2>${($question | async).question}</h2>
<div *ngFor="option of ($question | async).options">${option}
<button (click)="getNextQuestion()">Next</button>
</div>
Also, i might add the the way your data is structured won't be feasible in javascript, because in an object, every key must be unique. you would have to map the data from the api into a valid JS object.
Also, I am unsure if you want to render one question at a time, or only render the next question if the last one has been answered. I assumed the former.
If the case is the latter, you can apply the Observable-style pattern in another ngFor
-directive, which iterates another array which holds all the questions already answerered the question currently being answered.