150 votes

Angular ReactiveForms : Produire un tableau de valeurs de cases à cocher ?

Étant donné une liste de cases à cocher liées à la même formControlName comment puis-je produire un tableau de valeurs de cases à cocher liées à l'élément de données de l'utilisateur ? formControl plutôt que de simplement true / false ?

Exemple :

<form [formGroup]="checkboxGroup">
    <input type="checkbox" id="checkbox-1" value="value-1" formControlName="myValues" />
    <input type="checkbox" id="checkbox-2" value="value-2" formControlName="myValues" />
    <input type="checkbox" id="checkbox-3" value="value-2" formControlName="myValues" />
</form>

checkboxGroup.controls['myValues'].value produit actuellement :

true or false

Ce que je veux qu'il produise :

['value-1', 'value-2', ...]

0 votes

Avez-vous trouvé une solution ?

0 votes

Il s'agit probablement de la façon la plus sophistiquée de faire des cases à cocher dans un formulaire. Ce n'est pas du tout simple.

0 votes

@mwilson vous plaignez vous spécifiquement de mon implémentation ou d'Angular ?

85voto

Guymage Points 485

Avec l'aide de la réponse de silentsod, j'ai écrit une solution pour obtenir des valeurs au lieu des états dans mon formBuilder.

J'utilise une méthode pour ajouter ou supprimer des valeurs dans le formArray. C'est peut-être une mauvaise approche, mais ça marche !

composant.html

<div *ngFor="let choice of checks; let i=index" class="col-md-2">
  <label>
    <input type="checkbox" [value]="choice.value" (change)="onCheckChange($event)">
    {{choice.description}}
  </label>
</div>

component.ts

// For example, an array of choices
public checks: Array<ChoiceClass> = [
  {description: 'descr1', value: 'value1'},
  {description: "descr2", value: 'value2'},
  {description: "descr3", value: 'value3'}
];

initModelForm(): FormGroup{
  return this._fb.group({
    otherControls: [''],
    // The formArray, empty 
    myChoices: new FormArray([]),
  }
}

onCheckChange(event) {
  const formArray: FormArray = this.myForm.get('myChoices') as FormArray;

  /* Selected */
  if(event.target.checked){
    // Add a new control in the arrayForm
    formArray.push(new FormControl(event.target.value));
  }
  /* unselected */
  else{
    // find the unselected element
    let i: number = 0;

    formArray.controls.forEach((ctrl: FormControl) => {
      if(ctrl.value == event.target.value) {
        // Remove the unselected element from the arrayForm
        formArray.removeAt(i);
        return;
      }

      i++;
    });
  }
}

Lorsque je soumets mon formulaire, par exemple, mon modèle ressemble à ceci :

  otherControls : "foo",
  myChoices : ['value1', 'value2']

Il ne manque qu'une chose, une fonction pour remplir le formArray si votre modèle a déjà des valeurs vérifiées.

0 votes

comment puis-je vérifier si ma case à cocher est cochée lorsque je charge les données après avoir utilisé votre exemple pour entrer dans la base de données ?

0 votes

Dans cette solution, le formulaire est toujours valide même si la case à cocher n'est pas sélectionnée.

0 votes

myChoices: new FormArray([], Validators.required)

59voto

silentsod Points 5425

Voici un bon endroit pour utiliser le FormArray https://angular.io/docs/ts/latest/api/forms/index/FormArray-class.html

Pour commencer, nous allons construire notre tableau de contrôles soit avec un fichier de type FormBuilder ou la création d'un FormArray

FormBuilder

this.checkboxGroup = _fb.group({
  myValues: _fb.array([true, false, true])
});

nouveau FormArray

let checkboxArray = new FormArray([
  new FormControl(true),
  new FormControl(false),
  new FormControl(true)]);

this.checkboxGroup = _fb.group({
  myValues: checkboxArray
});

C'est assez facile à faire, mais nous allons ensuite modifier notre modèle et laisser le moteur de création de modèles gérer la manière dont nous nous lions à nos contrôles :

modèle.html

<form [formGroup]="checkboxGroup">
    <input *ngFor="let control of checkboxGroup.controls['myValues'].controls"
    type="checkbox" id="checkbox-1" value="value-1" [formControl]="control" />     
  </form>

Ici, nous itérons sur notre ensemble de FormControls dans notre myValues FormArray et pour chaque contrôle, on lie [formControl] à ce contrôle plutôt qu'au FormArray contrôle et <div>{{checkboxGroup.controls['myValues'].value}}</div> produit true,false,true tout en rendant la syntaxe de vos modèles un peu moins manuelle.

Vous pouvez utiliser cet exemple : http://plnkr.co/edit/a9OdMAq2YIwQFo7gixbj?p=preview pour fouiller

1 votes

Vous devriez probablement supprimer id="xxx", l'id doit être unique, n'est-ce pas ?

1 votes

pour id pourrait être utilisé indice *ngFor="let control of checkboxGroup.controls['myValues'].controls ; let i=index""

13 votes

C'est cool, mais cela produit un tableau complètement générique de cases à cocher. Vous devriez probablement charger un tableau ou autre chose, et associer chaque case à cocher à une autre valeur. Comment ajouter, par exemple, une chaîne de texte à utiliser dans une étiquette de formulaire à chaque contrôle de formulaire ?

42voto

Danny Pritchard Points 41

Il est beaucoup plus facile de faire cela dans Angular 6 que dans les versions précédentes, même lorsque les informations de la case à cocher sont alimentées de manière asynchrone à partir d'une API.

La première chose à savoir, c'est que grâce à l'introduction d'Angular 6 keyvalue nous n'avons pas besoin d'utiliser le tuyau FormArray et peuvent à la place imbriquer un FormGroup .

Tout d'abord, passez FormBuilder dans le constructeur

constructor(
    private _formBuilder: FormBuilder,
) { }

Puis initialiser notre formulaire.

ngOnInit() {

    this.form = this._formBuilder.group({
        'checkboxes': this._formBuilder.group({}),
    });

}

Lorsque les données relatives aux options de la case à cocher sont disponibles, elles sont itérées et nous pouvons les pousser directement dans l'élément imbriqué FormGroup comme un nom FormControl sans avoir à recourir à des tableaux de consultation indexés sur le nombre.

const checkboxes = <FormGroup>this.form.get('checkboxes');
options.forEach((option: any) => {
    checkboxes.addControl(option.title, new FormControl(true));
});

Enfin, dans le modèle, il nous suffit d'itérer les fichiers keyvalue des cases à cocher : aucune let index = i et les cases à cocher seront automatiquement classées par ordre alphabétique : beaucoup plus propre.

<form [formGroup]="form">

    <h3>Options</h3>

    <div formGroupName="checkboxes">

        <ul>
            <li *ngFor="let item of form.get('checkboxes').value | keyvalue">
                <label>
                    <input type="checkbox" [formControlName]="item.key" [value]="item.value" /> {{ item.key }}
                </label>
            </li>
        </ul>

    </div>

</form>

1 votes

Très utilisable également dans le cas d'un simple tableau de valeurs de cases à cocher codées en dur. Vous pouvez alors ajouter les contrôles du formulaire en utilisant une boucle for similaire immédiatement dans ngOnInit(), et les cases à cocher de votre formulaire refléteront dynamiquement le tableau de valeurs des cases à cocher.

6 votes

Cet alambic extrait [key1=true,key2=false,key3=true]. Nous voulons que ['key1', 'key3']

0 votes

@f.khantsis Vous pouvez le faire comme ça : ` const value = { key1 : true, key2 : false, key3 : true } ; const list = Object.entries(value).filter(([_, isSelected]) => isSelected).map(([key]) => key) ; console.log(list) ; `

10voto

aCiD Points 311

Si vous recherchez les valeurs des cases à cocher au format JSON

{ "name": "", "countries": [ { "US": true }, { "Germany": true }, { "France": true } ] }

Exemple complet ici .

Je m'excuse d'avoir utilisé les noms de pays comme valeurs de case à cocher au lieu de ceux de la question. Plus d'explications -

Créer un FormGroup pour le formulaire

 createForm() {

    //Form Group for a Hero Form
    this.heroForm = this.fb.group({
      name: '',
      countries: this.fb.array([])
    });

    let countries=['US','Germany','France'];

    this.setCountries(countries);}
 }

Chaque case à cocher est un FormGroup construit à partir d'un objet dont la seule propriété est la valeur de la case à cocher.

 setCountries(countries:string[]) {

    //One Form Group for one country
    const countriesFGs = countries.map(country =>{
            let obj={};obj[country]=true;
            return this.fb.group(obj)
    });

    const countryFormArray = this.fb.array(countriesFGs);
    this.heroForm.setControl('countries', countryFormArray);
  }

Le tableau de FormGroups pour les cases à cocher est utilisé pour définir le contrôle pour les "pays" dans le formulaire parent.

  get countries(): FormArray {
      return this.heroForm.get('countries') as FormArray;
  };

Dans le modèle, utilisez un tube pour obtenir le nom du contrôle de la case à cocher.

  <div formArrayName="countries" class="well well-lg">
      <div *ngFor="let country of countries.controls; let i=index" [formGroupName]="i" >
          <div *ngFor="let key of country.controls | mapToKeys" >
              <input type="checkbox" formControlName="{{key.key}}">{{key.key}}
          </div>
      </div>
  </div>

0 votes

Vous devez copier le pipe mapToKeys du lien d'exemple ou comme ci-dessous si ce lien meurt. Vous devrez peut-être supprimer le deuxième paramètre (args) et déclarer le tube dans le fichier du module d'application : import {Pipe, PipeTransform} from '@angular/core' ; @Pipe({name : 'mapToKeys'}) export class MapToKeysPipe implements PipeTransform { transform(value) : any { let keys = [] ; for (let key in value) { keys.push({key : key, value : value[key]}) ; } return keys ; } } }

4voto

canada11 Points 207

Créez un événement lorsqu'elle est cliquée et changez manuellement la valeur de true au nom de ce que la case à cocher représente, alors le nom ou true sera évalué de la même manière et vous pourrez obtenir toutes les valeurs au lieu d'une liste de true/false. Ex :

composant.html

<form [formGroup]="customForm" (ngSubmit)="onSubmit()">
    <div class="form-group" *ngFor="let parameter of parameters"> <!--I iterate here to list all my checkboxes -->
        <label class="control-label" for="{{parameter.Title}}"> {{parameter.Title}} </label>
            <div class="checkbox">
              <input
                  type="checkbox"
                  id="{{parameter.Title}}"
                  formControlName="{{parameter.Title}}"
                  (change)="onCheckboxChange($event)"
                  > <!-- ^^THIS^^ is the important part -->
             </div>
      </div>
 </form>

component.ts

onCheckboxChange(event) {
    //We want to get back what the name of the checkbox represents, so I'm intercepting the event and
    //manually changing the value from true to the name of what is being checked.

    //check if the value is true first, if it is then change it to the name of the value
    //this way when it's set to false it will skip over this and make it false, thus unchecking
    //the box
    if(this.customForm.get(event.target.id).value) {
        this.customForm.patchValue({[event.target.id] : event.target.id}); //make sure to have the square brackets
    }
}

Cela permet d'attraper l'événement après qu'il ait déjà été changé en vrai ou faux par Angular Forms, si c'est vrai, je change le nom pour le nom de ce que la case à cocher représente, qui si nécessaire sera également évalué à vrai si elle est vérifiée pour vrai/faux aussi.

0 votes

Cela m'a mis sur la bonne voie, j'ai fini par faire ceci this.customForm.patchValue({[event.target.id] : event.target.checked})) ;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X