Search code examples
cssangularcss-selectorslogicng-class

how to display background-color only on selected option in quiz


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;
}

Solution

  • 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.