In my Angular quiz app, I had the same logic: option.selected && option.correct
and option.selected && !option.correct
in both of my question component (MultipleAnswerComponent and SingleAnswerComponent) templates, so I moved out the logic and added two variables to the QuizService to share/reduce the duplicate code:
setOptions(optionSelected: boolean, optionCorrect: boolean): void {
this.isCorrectOption = optionSelected && optionCorrect;
this.isIncorrectOption = optionSelected && !optionCorrect;
}
And I also replaced the logic with the variables in the class binding like so: [class.is-correct]="isCorrectOption"
and [class.is-incorrect]="isIncorrectOption"
, but was getting red on all of the options, so I changed the background-color on the mat-checkbox/mat-radio-button to $light-gray !important and now it's gray. When I click on an option, the green or red background does not appear as expected.
EDIT: I am getting correct values for isCorrectOption and isIncorrectOption in QuizService and the values are passed back to the components. However now ALL of the options are displaying green/red when clicking on an option; I only need just the selected option to have a background-color. Not sure if isCorrectOption and isIncorrectOption are binding correctly to the class binding or if the logic that I'm using is incorrect...
I tried following the advice in the answer below, but was getting an error: couldn't read 'selected' of undefined. My current solution is pretty close to figuring it out but I still need some additional help to get the individual highlighting working properly. Please can you see my StackBlitz and help me to resolve this issue. Thank you.
in MultipleAnswerComponent/SingleAnswerComponent template:
<div class="options" *ngFor="let option of currentQuestion.options; index as i">
<div *ngIf="multipleAnswer" or *ngIf="!multipleAnswer">
<mat-checkbox or <mat-radio-button
(change)="setSelected(i)"
[class.is-correct]="isCorrectOption"
[class.is-incorrect]="isIncorrectOption">
in MultipleAnswer/SingleAnswer TypeScript files:
setSelected(optionIndex: number): void {
this.quizStarted = true;
this.isCorrectAnswerSelected = this.isCorrect(this.currentQuestion.options[optionIndex].correct, optionIndex);
this.answer.emit(optionIndex);
if (this.correctAnswers.length === 1) {
this.currentQuestion.options.forEach((option: Option) => option.selected = false);
}
this.currentQuestion.options[optionIndex].selected = true;
if (
optionIndex >= 0 &&
this.currentQuestion &&
this.currentQuestion.options &&
this.currentQuestion.options[optionIndex]["correct"]
) {
optionIndex = null;
this.optionCorrect = true;
this.timerService.stopTimer();
this.quizService.correctSound.play();
} else {
this.optionCorrect = false;
this.quizService.incorrectSound.play();
}
this.quizService.setOptions(true, this.optionCorrect);
this.isCorrectOption = this.quizService.isCorrectOption;
this.isIncorrectOption = this.quizService.isIncorrectOption;
this.alreadyAnswered = true;
}
Your problem is when you save the selected state in quiz service.
When you select one item in the quiz and call setOptions(optionSelected: boolean, optionCorrect: boolean)
, the value of optionSelected
is true (because of the current item). The problem is that the values of quiz service isCorrectOption
and isIncorrectOption
use the same instance of optionSelected
, so it will be true for all items.
What to do? Well, instead of using quiz service for this, use saved selected state in each Option instance.
In typescript you already do:
this.optionSelected = this.currentQuestion.options[optionIndex];
this.optionSelected.selected = true;
this.optionSelected.correct = false;
So use these values in the html:
[class.is-correct]="option.selected && option.correct"
(or create a function for this)
Of course, you can refactor this to use the quiz service fixing that issue.