javascriptangularangularjscheckboxmutual-exclusion

How to create Angular mutually exclusive checkboxes and filter


I have 4 checkboxes that filter based on its name/identifierValue. When the page loads ALL should be selected. If I click any other box 'ALL' should deselect and I then filter based on the selection. Then, if I click ALL all of the other checkboxes should deselect but ALL identifiers should show.

Currently, I set the checkboxes to true or false via FormGroup in parent component. I then listen to form changes to hanlde the logic.

The problem is I have to uncheck ALL in order to select another box.

Then when I click ALL after selecting other boxes they deselect properly but all items in the table dont show as expected. After that I have to toggle all of the other boxes just to get the filtering back working again.

Here is the method I'm calling via ngOnInit by way of initializeSearchPage().

handleCheckboxSelection(values: any) {
 const allSelected = values.ALL;
 const checkBox1Selected = values.Checkbox1;
 const checkBox2Selected = values.Checkbox2;
 const checkBox3Selected = values.Checkbox3;

 if (allSelected) {
  // If "All" is selected, deselect others
  this.reportListFG.get('filterGroup')?.patchValue(
    {
      Checkbox1: false,
      Checkbox2: false,
      Checkbox3: false,
    },
    { emitEvent: false } // Prevent infinite loop
  );
  } else {
  // If "All" is not selected, deselect "All" if any other is selected
  if (checkBox1Selected || checkBox2Selected || checkBox3Selected) {
    this.reportListFG.get('filterGroup')?.patchValue(
      {
        ALL: false,
      },
      { emitEvent: false }
    );
  }
  
  

Here is my stackblitz


Solution

  • Updated

    You should compare the current and previous form value with these scenarios:

    1. When the "ALL" checkbox is updated and checked, disable other checkboxes.

    2. When the other checkbox is updated, uncheck the "ALL" checkbox.

    import { pairwise, startWith } from 'rxjs/operators';
    
    this.reportListFG
      .get('filterGroup')
      .valueChanges.pipe(
        startWith(this.reportListFG.get('filterGroup').value),
        pairwise()
      )
      .subscribe(([previousValues, values]) => {
        this.handleCheckboxSelection(previousValues, values);
      });
    
    handleCheckboxSelection(previousValues: any, values: any) {
      const allSelected = values.ALL;
      const checkBox1Selected = values.Checkbox1;
      const checkBox2Selected = values.Checkbox2;
      const checkBox3Selected = values.Checkbox3;
      const isAllSelectedChanged = previousValues?.ALL != values.ALL;
      const checkBox1SelectedChanged =
        previousValues.Checkbox1 != values.Checkbox1;
      const checkBox2SelectedChanged =
        previousValues.Checkbox2 != values.Checkbox2;
      const checkBox3SelectedChanged =
        previousValues.Checkbox3 != values.Checkbox3;
    
      if (isAllSelectedChanged && allSelected) {
        // If "All" is selected, deselect others
        this.reportListFG.get('filterGroup')?.patchValue(
          {
            Checkbox1: false,
            Checkbox2: false,
            Checkbox3: false,
          },
          { emitEvent: false } // Prevent infinite loop
        );
      } else if (
        checkBox1SelectedChanged ||
        checkBox2SelectedChanged ||
        checkBox3SelectedChanged
      ) {
        // If "All" is not selected, deselect "All" if any other is selected
        this.reportListFG.get('filterGroup')?.patchValue(
          {
            ALL: false,
            Checkbox1: checkBox1Selected,
            Checkbox2: checkBox2Selected,
            Checkbox3: checkBox3Selected,
          },
          { emitEvent: false }
        );
    
        // Alternate without update whole filterGroup object
        // this.reportListFG.get('filterGroup')
        //   .get("ALL")
        //   .setValue(false, { emitEvent: false });
      }
    }
    

    Demo @ StackBlitz