Search code examples
javascriptangularangularjstypescriptinput

Validation not working in HTML file when assigned to a variable in the ts file (Angular)


I am starting with Angular basics, I have the below code to assign a class to a button based on the button name which is received as a prop like below in component.ts file,

import { Component, Input } from '@angular/core';
import type { ButtonProps } from './reuseable-button-types';

@Component({
  selector: 'button',
  standalone: true,
  imports: [],
  templateUrl: './button.component.html',
  styleUrl: './button.component.css',
})
export class ReusableButtonComponent {
  @Input({ required: true })
  buttonProps: ButtonProps = {
    buttonName: '',
    handleClick: () => {
      //
    },
  };
}

button.component.html =>

<button
  (click)="buttonProps.handleClick()"
  class="reusable-button {{ buttonProps.buttonClass }}"
  type="button"
  [class.yes-button]="buttonProps.buttonName === 'Yes'"
  [class.no-button]="buttonProps.buttonName === 'No'"
>
  {{ buttonProps.buttonName }}
</button>

The above code works fine with 'yes-button' and 'no-button' class names getting assigned to the button when the button name is 'Yes' or 'no'. But when I assign the value to a variable inside the component.ts file classes are not getting assigned as expected. See the below code.

button.component.ts =>

import { Component, Input } from '@angular/core';
import type { ButtonProps } from './reuseable-button-types';

@Component({
  selector: 'button',
  standalone: true,
  imports: [],
  templateUrl: './button.component.html',
  styleUrl: './button.component.css',
})
export class ReusableButtonComponent {
  @Input({ required: true })
  buttonProps: ButtonProps = {
    buttonName: '',
    handleClick: () => {
      //
    },
  };

  isYesButton = this.buttonProps.buttonName === 'Yes';
  isNoButton = this.buttonProps.buttonName === 'No';
}

button.component.html =>

<button
  (click)="buttonProps.handleClick()"
  class="reusable-button {{ buttonProps.buttonClass }}"
  type="button"
  [class.yes-button]="isYesButton"
  [class.no-button]="isNoButton"
>
  {{ buttonProps.buttonName }}
</button>

Solution

  • When we write the class evaluation variables like below

    isYesButton = this.buttonProps.buttonName === 'Yes';
    isNoButton = this.buttonProps.buttonName === 'No';
    

    They are only evaluated once during component load and not after that, but you want this to be more dynamic, so I suggest you can go for getter method, which will be evaluated each time the property is accessed and you will get the dynamic behavior you are expecting!

      get isYesButton() {
        return this.buttonProps.buttonName === 'Yes';
      }
    
      get isNoButton() {
        return this.buttonProps.buttonName === 'No';
      }
    

    FULL CODE:

    CHILD:

    import { Component, Input } from '@angular/core';
    
    @Component({
      selector: 'app-child',
      standalone: true,
      imports: [],
      template: `
      <button
        (click)="handleButtonClick()"
        class="reusable-button {{ buttonProps.buttonClass }}"
        type="button"
        [class.yes-button]="isYesButton"
        [class.no-button]="isNoButton"
      >
        {{ buttonProps.buttonName }}
      </button>
      `,
    })
    export class ChildComponent {
      @Input({ required: true }) buttonProps: any = {
        buttonName: '',
        buttonClass: 'test',
        handleClick: () => {
          console.log('click');
        },
      };
    
      get isYesButton() {
        return this.buttonProps.buttonName === 'Yes';
      }
    
      get isNoButton() {
        return this.buttonProps.buttonName === 'No';
      }
    
      handleButtonClick() {}
    }
    

    MAIN

    import { Component } from '@angular/core';
    import { bootstrapApplication } from '@angular/platform-browser';
    import 'zone.js';
    import { ChildComponent } from './app/child/child.component';
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports: [ChildComponent],
      template: `
        <app-child [buttonProps]="buttonProps"/>
      `,
    })
    export class App {
      name = 'Angular';
      buttonProps = {
        buttonName: 'Yes',
        handleClick: () => {
          console.log('click');
        },
      };
    
      ngOnInit() {
        setInterval(() => {
          this.buttonProps.buttonName =
            this.buttonProps.buttonName === 'Yes' ? 'No' : 'Yes';
        }, 2000);
      }
    }
    
    bootstrapApplication(App);
    

    Stackblitz Demo