htmlangularangular7angular2-components

Angular 7 components: how to detect a change by user interaction?


I have a component with several checkboxes, drop-downs, and a save button. Here is a simplified example component template:

<aside class="container">
  <div class="row">
    <input
      type="checkbox"
      id="all-users"
      [(ngModel)]="showAllUsers"
      (ngModelChange)="onChange($event)"
    />
    <label for="all-users">Show all users</label>
  </div>
  <div class="row">
    <ng-select
      [(ngModel)]="selectedUser"
      [clearable]="false"
      appendTo="body"
      (change)="onChange($event)"
    >
      <ng-option *ngFor="let user of activeUsers" [value]="user">{{ user }}</ng-option>
    </ng-select>
  </div>
  <div class="row">
    <button type="button" class="btn btn-primary" [disabled]="!dirty" (click)="onSave()">
      Save Changes
    </button>
  </div>
</aside>

I want to enable the Save Changes button only when the user made a change, either by unchecking the check-box or changing a selection in drop-down box.

Right now I have an event handler registered at each and every control in the component (the onChange function in the example above), and use a dirty flag to disable or enable the Save Changes button.

Here is the component.ts for the above template:

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-filter',
  templateUrl: './filter.component.html',
  styleUrls: ['./filter.component.css']
})
export class FilterComponent implements OnInit {
  dirty: boolean;
  showAllUsers: boolean;
  selectedUser: string;
  activeUsers: string[];

  ngOnInit() {
    this.dirty = false;
    this.showAllUsers = true;
    this.activeUsers = ['Thanos', 'Thor', 'Starlord'];
    this.selectedUser = 'Thor';
  }
  onChange(event) {
    console.log('Event is ' + event);
    this.dirty = true;
  }
  onSave() {
    console.log('Gonna save changes...');
    this.dirty = false;
  }
}

Registering the event handler to every control does not seem intuitive to me.

Is this the correct approach to figure out a change made by user or does angular provide a different way to achieve this?


Solution

  • I would highly recommand using both FormGroup and FormControl to achieve this behavior.

    Both exposes the dirty property, a read-only boolean.

    The dirty property is set to true when the user changes the value of the FormControl from the UI. In the case of the FormGroup, the dirty property is set to true as long as at least 1 of the FormControl in that group is dirty.

    As a side note, the property pristine is the opposite property. So you can use one or the other if it simplifies the condition.

    [disabled]="myFormGroup.pristine" might be easier to read than [disabled]="!myFormGroup.dirty".