web-dev-qa-db-fra.com

Angular 5 Angular Case à cocher avec 3 états (cochée, non cochée, indéterminée)

Je suis nouveau sur Angular et Angular, je travaille maintenant comme support dans certains projets. Il y a une grille avec des filtres et une case à cocher, qui vérifie si l'utilisateur dans la grille est actif, inactif ou non choisi. Ce serait plus simple avec seulement deux options (actif, inactif) mais bon, je dois faire 3 états pour cela:

  1. 1er clic - Vérifié pour actif
  2. 2ème clic - Décoché pour inactif
  3. 3ème clic - Indéterminé pour non choisi

Voici un exemple de case à cocher officiel Angular Documentation: https://stackblitz.com/angular/rxdmnbxmkgk?file=app%2Fcheckbox-configurable-example.html

Comment le faire de la manière la plus simple?

6
TomasThall

TL; DR

Voici un composant prêt qui utilise les solutions présentées ci-dessous:

import { Component, forwardRef, OnInit } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { MAT_CHECKBOX_CLICK_ACTION } from '@angular/material';

@Component({
  selector: 'app-tri-state-checkbox',
  templateUrl: './tri-state-checkbox.component.html',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => TriStateCheckboxComponent),
      multi: true,
    },
    { provide: MAT_CHECKBOX_CLICK_ACTION, useValue: 'noop' },
  ],
})
export class TriStateCheckboxComponent implements ControlValueAccessor {

  tape = [null, true, false];

  value: boolean;

  disabled: boolean;

  private onChange: (val: boolean) => void;
  private onTouched: () => void;

  writeValue(value: boolean) {
    this.value = value;
  }

  setDisabledState(disabled: boolean) {
    this.disabled = disabled;
  }

  next() {
    this.onChange(this.value = this.tape[(this.tape.indexOf(this.value) + 1) % this.tape.length]);
    this.onTouched();
  }

  registerOnChange(fn: any) {
    this.onChange = fn;
  }

  registerOnTouched(fn: any) {
    this.onTouched = fn;
  }

}

et modèle:

<mat-checkbox [ngModel]="value" (click)="next()" [disabled]="disabled" [indeterminate]="value === false" [color]="value === false ? 'warn' : 'accent'">
  <ng-content></ng-content>
</mat-checkbox>

Usage:

<app-tri-state-checkbox [(ngModel)]="done">is done</app-tri-state-checkbox>
<app-tri-state-checkbox formControlName="done">is done</app-tri-state-checkbox>

Réponse originale

Solution 1:stackblitz

La première solution consiste à fournir MAT_CHECKBOX_CLICK_ACTION en tant que noop et à vous amuser avec l'événement click, mais ce n'est pas très pratique, car vous pouvez avoir plusieurs cases à cocher sur la même page et toutes ne sont pas des cases à trois états.

Composant:

import { MAT_CHECKBOX_CLICK_ACTION } from '@angular/material';

@Component({
  selector: 'material-app',
  templateUrl: 'app.component.html',
  providers: [
    // provide on THIS component level only
    { provide: MAT_CHECKBOX_CLICK_ACTION, useValue: 'noop' }
  ],
})
export class AppComponent {
  tape = [null, true, false];

  done = null;

  doneControl = new FormControl(false);
}

où les valeurs de la bande (une séquence de valeurs basculées) sont:

  • null pour case à cocher vide
  • true pour fait
  • false pour ne fera pas

Ensuite, utilisez-le dans le fichier modèle:

<mat-checkbox [ngModel]="done" 
              [indeterminate]="done === false" 
              (click)="done = tape[(tape.indexOf(done) + 1) % tape.length]"
              [color]="done === false ? 'warn' : 'accent'">
    Tri-state mat-checkbox with ngModel
</mat-checkbox>

<br>

<mat-checkbox [formControl]="doneControl" 
              [indeterminate]="doneControl.value === false" 
              (click)="doneControl.setValue(tape[(tape.indexOf(doneControl.value) + 1) % tape.length])"
              [color]="doneControl.value === false ? 'warn' : 'accent'">
    Tri-state mat-checkbox with formControl
</mat-checkbox>

Solution 2:stackblitz

Une autre solution à laquelle je suis arrivé est de changer le gestionnaire ngModelChange pour réagir aux valeurs précédentes du modèle. Cela fonctionne avec ngModel mais ne fonctionne pas avec formControlformControlName. Qu'est-ce qui a changé par rapport à la première solution:

  • non MAT_CHECKBOX_CLICK_ACTION est écrasé
  • l'événement click est changé en ngModelChange

Composant:

@Component({
  selector: 'material-app',
  templateUrl: 'app.component.html'
})
export class AppComponent {
  tape = [null, true, false];

  done = null;

  doneControl = new FormControl(false);
}

Modèle:

<mat-checkbox [ngModel]="done" 
              [indeterminate]="done === false" 
              (ngModelChange)="done = tape[(tape.indexOf(done) + 1) % tape.length]"
              [color]="done === false ? 'warn' : 'accent'">
    Tri-state mat-checkbox with ngModel
</mat-checkbox>

<br>

<mat-checkbox [formControl]="doneControl" 
              [indeterminate]="doneControl.value === false" 
              (ngModelChange)="doneControl.setValue(tape[(tape.indexOf(doneControl.value) + 1) % tape.length])"
              [color]="doneControl.value === false ? 'warn' : 'accent'">
    Tri-state mat-checkbox with formControl
</mat-checkbox>

Ici, le modèle est défini avec _/one-way binding, le mode indéterminé est strictement lié à la valeur false et la couleur est également modifiée en fonction de la valeur.

6
smnbbrv

Si vous avez besoin d'un exemple de travail, vous pouvez également cloner le projet material2 project here , puis:

cd material2
npm i
npm run demo-app

Ouvrez l'application de démonstration et accédez au composant case à cocher. 

0
Liviu Ilea

Une façon de faire est de définir MAT_CHECKBOX_CLICK_ACTION sur 'noop' et vous devrez ensuite définir les valeurs cochées avec (clic). N'oubliez pas de lier à la fois [ngModel] et [indéterminé].

providers: [
    {provide: MAT_CHECKBOX_CLICK_ACTION, useValue: 'noop'}
]

Regardez ceci: https://github.com/angular/material2/blob/master/src/lib/checkbox/checkbox.md

0
rcunha