javascriptangulartypescriptcomponents

Angular Component is not updating after state change


I'm coming from React and I'm liking Angular 18 so far. However I'm struggling with trying to understand how components work in angular, specifically updating the DOM from a child component.

I have a parent component that has 2 children, one is angular component and the other is just a div. Both should theoretically do the same thing. What i want do is 'tab' from certain views. So from the parent component I can tab to either view. I do this with a simple function goBack function that changes the currentTab variable. this works in the regular div element just fine, but in the angular component when I pass the Input (and log the result from the parent component), it shows that the variable or state has changed, but the view has not changed. To render the different views I'm using *ngIf. I've noticed similar issues with angular components not behaving as expected and I'm wondering why.

Here is a little snippit to help further elaborate my issue.

Parent Component.html


<div class="container"> <div \*ngIf="currentTab === 'chose-options'" class="button-container"> <button (click)="choseGameOption('new-game')" value="new-game" type="button" class="button" > <p>New Game</p> </button> <button (click)="choseGameOption('saved-game')" value="saved-game" type="button" class="button" > Saved Game </button> </div>

<div \*ngIf="currentTab === 'new-game'"> <app-jeopardy-game-board \[goBack\]="goBack"></app-jeopardy-game-board> <button (click)="goBack()">go back</button> </div>

<div \*ngIf="currentTab === 'saved-game'"> <p>Choose saved game</p> <button (click)="goBack()">back</button> </div> </div>

Parent component.ts

export class JeopardyComponent {
  @ViewChild(AddJeopardyQuestionComponent)
  dialog!: AddJeopardyQuestionComponent;
  currentTab: string = 'chose-options';
  private cd: ChangeDetectorRef = inject(ChangeDetectorRef);

  choseGameOption(gameOption: string) {
    this.currentTab = gameOption;
    this.cd.detectChanges();
  }
  goBack() {
    this.currentTab = 'chose-options';
    console.log(this.currentTab);
  }
}

Child component.html:


// ... misc. table data (no other logic)

<button (click)="onBackClick()">
        <mat-icon>keyboard_arrow_left</mat-icon>
      </button>

Child component.ts


import { CommonModule } from '@angular/common';
import { Component, Input, Output } from '@angular/core';
import { MatIconModule } from '@angular/material/icon';


@Component({
  selector: 'app-jeopardy-game-board',
  standalone: true,
  imports: [MatIconModule, CommonModule],
  templateUrl: './jeopardy-game-board.component.html',
  styleUrl: './jeopardy-game-board.component.scss',
})
export class JeopardyGameBoardComponent {
  @Input() goBack!: () => void;

  // @Output() viewEvent: EventEmitter = new EventEmitter();

  onBackClick() {
    this.goBack();
    // this.viewEvent.emit();
  }
}

I've tried using a a ChangeDetectorRef, ViewChild(), Outputting EventEmmiters, nothing seems to be updating the UI the way it is for the regular div element Sorry if my terminology is off, I'm still very new to angular


Solution

  • I think you're doing it in the opposite way. Your child component is supposed to emit, with an Output, an event to say that its go back button has been clicked to inform its parent. Then the parent is supposed to react on this emitted event to do its stuff.

    child component ts

    import { CommonModule } from '@angular/common';
    import { Component, Input, Output } from '@angular/core';
    import { MatIconModule } from '@angular/material/icon';
    
    
    @Component({
      selector: 'app-jeopardy-game-board',
      standalone: true,
      imports: [MatIconModule, CommonModule],
      templateUrl: './jeopardy-game-board.component.html',
      styleUrl: './jeopardy-game-board.component.scss',
    })
    export class JeopardyGameBoardComponent {
      @Output() backClick: EventEmitter = new EventEmitter();
    
      onBackClick() {
        this.backClick.emit();
      }
    }
    

    child component html:

    <button (click)="onBackClick()">
       <mat-icon>keyboard_arrow_left</mat-icon>
    </button>
    

    parent component ts

    export class JeopardyComponent {
      // other stuff
    
      goBack() {
        this.currentTab = 'chose-options';
        console.log(this.currentTab);
      }
    }
    

    parent component html

    <div *ngIf="currentTab === 'new-game'"> 
      <app-jeopardy-game-board (backClick)="goBack()"/> 
    </div>
    
    <div *ngIf="currentTab === 'saved-game'"> 
      <p>Choose saved game</p> 
      <button (click)="goBack()">back</button>
    </div>