Angular ReactiveForms: produzindo uma matriz de valores de caixa de seleção?

104

Dada uma lista de caixas de seleção vinculadas ao mesmo formControlName, como posso produzir uma matriz de valores de caixa de seleção vinculados a formControl, em vez de simplesmente true/ false?

Exemplo:

<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 atualmente produz:

true or false

O que eu quero produzir:

['value-1', 'value-2', ...]
ReactingToAngularVues
fonte
Você achou alguma solução?
PC de
Esta é provavelmente a maneira mais elaborada de criar caixas de seleção em um formulário. Isso não é nada simples.
mwilson
8
Angular. Tudo o que estou tentando fazer é fazer com que um grupo de rádio-tapete se ligue na minha forma reativa. Não me lembro de ter lutado tanto com angular. Todos os artigos apontam para a mesma coisa. Simplesmente não consigo fazer funcionar. Todo o resto é super simples. Provavelmente estive olhando para ele por muito tempo. Ainda assim, parece uma complexidade demais para um valor de array em um formulário.
mwilson
3
Sim, foi terrível quando perguntei isso em 2016, e ainda é terrível em 2019.
ReactingToAngularVues
3
Não estou acrescentando muito a esta pergunta, mas queria que outras pessoas soubessem que sinto o mesmo. Essa por si só tem sido a parte mais difícil no aprendizado de formas reativas angulares. Eu sinto que não deveria ser tão difícil. Estou feliz em ver que não estou sozinho na luta, no entanto. Então, obrigado por postar a pergunta.
NorthStarCode

Respostas:

52

Com a ajuda de silentsod answer, escrevi uma solução para obter valores em vez de estados em meu formBuilder.

Eu uso um método para adicionar ou remover valores no formArray. Pode ser uma abordagem ruim, mas funciona!

component.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++;
    });
  }
}

Quando eu envio meu formulário, por exemplo, meu modelo se parece com:

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

Só falta uma coisa, uma função para preencher o formArray se o seu modelo já tiver verificado os valores.

Guymage
fonte
como faço para verificar se minha caixa de seleção está marcada quando carrego os dados após usar seu exemplo para entrar no banco de dados?
Devora
Nesta solução sempre o formulário é válido mesmo que a caixa de seleção não esteja
marcada
myChoices: new FormArray([], Validators.required)
Bikram Nath
48

Este é um bom lugar para usar o FormArray https://angular.io/docs/ts/latest/api/forms/index/FormArray-class.html

Para começar, vamos construir nosso conjunto de controles com um FormBuilderou com um novoFormArray

FormBuilder

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

novo FormArray

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

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

Fácil de fazer, mas então vamos mudar nosso modelo e deixar o mecanismo de modelagem lidar com como nos ligamos aos nossos controles:

template.html

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

Aqui, estamos iterando nosso conjunto de FormControlsem nosso myValues FormArraye para cada controle que estamos vinculando [formControl]a esse controle em vez de ao FormArraycontrole e <div>{{checkboxGroup.controls['myValues'].value}}</div>produz, true,false,trueao mesmo tempo que tornamos a sintaxe do modelo um pouco menos manual.

Você pode usar este exemplo: http://plnkr.co/edit/a9OdMAq2YIwQFo7gixbj?p=preview para bisbilhotar

Silentsod
fonte
1
provavelmente você deve remover id = "xxx", o id deve ser único certo?
PeiSong Xiong
1
para id pode ser usado o índice *ngFor="let control of checkboxGroup.controls['myValues'].controls ; let i=index""
Mirza
9
Isso é legal, mas produz uma matriz completamente genérica de caixas de seleção. Presumivelmente, você estaria carregando em um array ou qualquer outra coisa e associando cada caixa de seleção a algum outro valor. Como você adiciona, por exemplo, uma string de texto para uso em um rótulo de formulário para cada controle de formulário?
Askdesigners de
NM Acabei de mapeá-lo próximo a uma matriz externa: p
Askdesigners
@Askdesigners você pode postar sua solução com as caixas de seleção e os rótulos?
eddygeek
25

É significativamente mais fácil fazer isso no Angular 6 do que nas versões anteriores, mesmo quando as informações da caixa de seleção são preenchidas de forma assíncrona a partir de uma API.

A primeira coisa a perceber é que, graças ao keyvaluetubo do Angular 6, não precisamos FormArraymais usar e, em vez disso, podemos aninhar um FormGroup.

Primeiro, passe FormBuilder para o construtor

constructor(
    private _formBuilder: FormBuilder,
) { }

Em seguida, inicialize nosso formulário.

ngOnInit() {

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

}

Quando nossos dados de opções de caixa de seleção estiverem disponíveis, itere-os e podemos colocá-los diretamente no aninhado FormGroupcomo um nome FormControl, sem ter que depender de matrizes de pesquisa indexadas por número.

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

Finalmente, no modelo, precisamos apenas iterar o keyvaluedas caixas de seleção: sem adicionais let index = i, e as caixas de seleção estarão automaticamente em ordem alfabética: muito mais limpo.

<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>
Danny Pritchard
fonte
1
Muito utilizável também no caso de uma simples matriz codificada de valores de caixa de seleção. Então você pode adicionar os controles do formulário usando um loop for semelhante imediatamente em ngOnInit (), e as caixas de seleção em seu formulário refletirão dinamicamente a matriz de valores das caixas de seleção
Arjan
3
Isso ainda extrai [key1 = true, key2 = false, key3 = true]. Queremos ['key1', 'key3']
f.khantsis
@ f.khantsis Você pode fazer assim: `const value = {key1: true, key2: false, key3: true}; const list = Object.entries (value) .filter (([_, isSelected]) => isSelected) .map (([key]) => key); console.log (lista); `
zauni
1
Melhor solução imho. Você pode colocar a atribuição de const checkboxes = ..fora do foreach;)
Bernoulli IT
O que acontece quando a chave do item é igual a outro campo do formulário? Por exemplo, eu tenho duas matrizes de caixa de seleção diferentes, cada uma com as chaves 'Small', 'Medium' e 'Large'?
Newclique
9

Se você estiver procurando por valores de caixa de seleção no formato JSON

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

Exemplo completo aqui .

Peço desculpas por usar os nomes dos países como valores da caixa de seleção em vez dos da pergunta. Explanação adicional -

Crie um FormGroup para o formulário

 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);}
 }

Deixe cada caixa de seleção ser um FormGroup construído a partir de um objeto cuja única propriedade é o valor da caixa de seleção.

 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);
  }

A matriz de FormGroups para as caixas de seleção é usada para definir o controle para os 'países' no formulário pai.

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

No modelo, use um tubo para obter o nome do controle de caixa de seleção

  <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>
ácido
fonte
6

TL; DR

  1. Eu prefiro usar FormGroup para preencher a lista de caixas de seleção
  2. Escreva um validador personalizado para verificar se pelo menos uma caixa de seleção foi selecionada
  3. Exemplo de trabalho https://stackblitz.com/edit/angular-validate-at-least-one-checkbox-was-selected

Isso também me surpreendeu algumas vezes, então tentei as abordagens FormArray e FormGroup.

Na maioria das vezes, a lista de caixas de seleção era preenchida no servidor e eu a recebia por meio da API. Mas às vezes você terá um conjunto estático de caixa de seleção com seu valor predefinido. Com cada caso de uso, o FormArray ou FormGroup correspondente será usado.

Basicamente, FormArrayé uma variante de FormGroup. A principal diferença é que seus dados são serializados como um array (em vez de serem serializados como um objeto no caso de FormGroup). Isso pode ser especialmente útil quando você não sabe quantos controles estarão presentes no grupo, como formulários dinâmicos.

Para simplificar, imagine que você tem um formulário de criação de produto simples com

  • Uma caixa de texto de nome de produto necessária.
  • Uma lista de categorias para selecionar, exigindo que pelo menos uma seja verificada. Suponha que a lista será recuperada do servidor.

Primeiro, configurei um formulário com apenas o nome de produto formControl. É um campo obrigatório.

this.form = this.formBuilder.group({
    name: ["", Validators.required]
});

Como a categoria é renderizada dinamicamente, terei que adicionar esses dados ao formulário mais tarde, depois que os dados estiverem prontos.

this.getCategories().subscribe(categories => {
    this.form.addControl("categoriesFormArr", this.buildCategoryFormArr(categories));
    this.form.addControl("categoriesFormGroup", this.buildCategoryFormGroup(categories));
})

Existem duas abordagens para construir a lista de categorias.

1. Matriz de formulário

  buildCategoryFormArr(categories: ProductCategory[], selectedCategoryIds: string[] = []): FormArray {
    const controlArr = categories.map(category => {
      let isSelected = selectedCategoryIds.some(id => id === category.id);
      return this.formBuilder.control(isSelected);
    })
    return this.formBuilder.array(controlArr, atLeastOneCheckboxCheckedValidator())
  }
<div *ngFor="let control of categoriesFormArr?.controls; let i = index" class="checkbox">
  <label><input type="checkbox" [formControl]="control" />
    {{ categories[i]?.title }}
  </label>
</div>

Isso buildCategoryFormGroupme retornará um FormArray. Ele também leva uma lista de valores selecionados como um argumento. Se você quiser reutilizar o formulário para editar dados, pode ser útil. Para fins de criação de um novo formulário de produto, ainda não é aplicável.

Observe que, ao tentar acessar os valores de formArray. Será parecido [false, true, true]. Para obter uma lista de id selecionados, é necessário um pouco mais de trabalho para verificar a lista, mas com base no índice da matriz. Não parece bom para mim, mas funciona.

get categoriesFormArraySelectedIds(): string[] {
  return this.categories
  .filter((cat, catIdx) => this.categoriesFormArr.controls.some((control, controlIdx) => catIdx === controlIdx && control.value))
  .map(cat => cat.id);
}

É por isso que comecei a usar FormGrouppara esse assunto

2. Grupo de forma

A diferença do formGroup é que ele armazena os dados do formulário como o objeto, o que requer uma chave e um controle de formulário. Portanto, é uma boa ideia definir a chave como categoryId e então podemos recuperá-la mais tarde.

buildCategoryFormGroup(categories: ProductCategory[], selectedCategoryIds: string[] = []): FormGroup {
  let group = this.formBuilder.group({}, {
    validators: atLeastOneCheckboxCheckedValidator()
  });
  categories.forEach(category => {
    let isSelected = selectedCategoryIds.some(id => id === category.id);
    group.addControl(category.id, this.formBuilder.control(isSelected));
  })
  return group;
}
<div *ngFor="let item of categories; let i = index" class="checkbox">
  <label><input type="checkbox" [formControl]="categoriesFormGroup?.controls[item.id]" /> {{ categories[i]?.title }}
  </label>
</div>

O valor do grupo de formulários será semelhante a:

{
    "category1": false,
    "category2": true,
    "category3": true,
}

Mas, na maioria das vezes, queremos obter apenas a lista de categoryIds como ["category2", "category3"]. Eu também tenho que escrever um get para obter esses dados. Eu gosto mais dessa abordagem em comparação com formArray, porque eu poderia realmente pegar o valor do próprio formulário.

  get categoriesFormGroupSelectedIds(): string[] {
    let ids: string[] = [];
    for (var key in this.categoriesFormGroup.controls) {
      if (this.categoriesFormGroup.controls[key].value) {
        ids.push(key);
      }
      else {
        ids = ids.filter(id => id !== key);
      }
    }
    return ids;
  }

3. O validador personalizado para verificar se pelo menos uma caixa de seleção foi selecionada

Eu fiz o validador marcar pelo menos a caixa de seleção X selecionada, por padrão, ele marcará apenas uma caixa de seleção.

export function atLeastOneCheckboxCheckedValidator(minRequired = 1): ValidatorFn {
  return function validate(formGroup: FormGroup) {
    let checked = 0;

    Object.keys(formGroup.controls).forEach(key => {
      const control = formGroup.controls[key];

      if (control.value === true) {
        checked++;
      }
    });

    if (checked < minRequired) {
      return {
        requireCheckboxToBeChecked: true,
      };
    }

    return null;
  };
}
trungk18
fonte
6

Não vejo uma solução aqui que responda completamente à pergunta usando formas reativas em sua extensão máxima, então aqui está minha solução para o mesmo.


Resumo

Aqui está o cerne da explicação detalhada, juntamente com um exemplo StackBlitz.

  1. Use FormArraypara as caixas de seleção e inicialize o formulário.
  2. O valueChangesobservável é perfeito para quando você deseja que o formulário exiba algo, mas armazene outra coisa no componente. Mapeie os valores true/ falsepara os valores desejados aqui.
  3. Filtre os falsevalores no momento do envio.
  4. Cancele a assinatura do valueChangesobservável.

Exemplo StackBlitz


Explicação detalhada

Use FormArray para definir o formulário

Conforme já mencionado na resposta marcada como correta. FormArrayé o caminho a seguir em casos em que você prefere obter os dados em uma matriz. Portanto, a primeira coisa que você precisa fazer é criar o formulário.

checkboxGroup: FormGroup;
checkboxes = [{
    name: 'Value 1',
    value: 'value-1'
}, {
    name: 'Value 2',
    value: 'value-2'
}];

this.checkboxGroup = this.fb.group({
    checkboxes: this.fb.array(this.checkboxes.map(x => false))
});

Isso apenas definirá o valor inicial de todas as caixas de seleção como false.

Em seguida, precisamos registrar essas variáveis ​​de formulário no modelo e iterar sobre o checkboxesarray (NÃO os, FormArraymas os dados da caixa de seleção) para exibi-los no modelo.

<form [formGroup]="checkboxGroup">
    <ng-container *ngFor="let checkbox of checkboxes; let i = index" formArrayName="checkboxes">
        <input type="checkbox" [formControlName]="i" />{{checkbox.name}}
    </ng-container>
</form>

Faça uso do observável valueChanges

Esta é a parte que não vejo mencionada em nenhuma das respostas aqui. Em situações como essa, em que gostaríamos de exibir esses dados, mas armazená-los como outra coisa, o valueChangesobservável é muito útil. Usando valueChanges, podemos observar as mudanças no checkboxese, em seguida, mapas true/ falseos valores recebidos do FormArraycom os dados desejados. Observe que isso não mudará a seleção das caixas de seleção, pois qualquer valor verdadeiro passado para a caixa de seleção irá marcá-lo como marcado e vice-versa.

subscription: Subscription;

const checkboxControl = (this.checkboxGroup.controls.checkboxes as FormArray);
this.subscription = checkboxControl.valueChanges.subscribe(checkbox => {
    checkboxControl.setValue(
        checkboxControl.value.map((value, i) => value ? this.checkboxes[i].value : false),
        { emitEvent: false }
    );
});

Isso basicamente mapeia os FormArrayvalores para o checkboxesarray original e retorna o valuecaso a caixa de seleção esteja marcada como true, do contrário, ele retorna false. O emitEvent: falseé importante aqui, pois definir o FormArrayvalor sem ele fará com valueChangesque seja emitido um evento criando um loop infinito. Ao definir emitEventcomo false, estamos garantindo que o valueChangesobservável não emita quando definimos o valor aqui.

Filtre os valores falsos

Não podemos filtrar diretamente os falsevalores no FormArrayporque fazer isso bagunçará o modelo, pois eles estão vinculados às caixas de seleção. Portanto, a melhor solução possível é filtrar os falsevalores durante o envio. Use o operador de propagação para fazer isso.

submit() {
    const checkboxControl = (this.checkboxGroup.controls.checkboxes as FormArray);
    const formValue = {
        ...this.checkboxGroup.value,
        checkboxes: checkboxControl.value.filter(value => !!value)
    }
    // Submit formValue here instead of this.checkboxGroup.value as it contains the filtered data
}

Isso basicamente filtra os valores falsos do checkboxes.

Cancelar inscrição em valueChanges

Por último, não se esqueça de cancelar a assinatura de valueChanges

ngOnDestroy() {
    this.subscription.unsubscribe();
}

Observação: há um caso especial em que um valor não pode ser definido como FormArrayin valueChanges, ou seja, se o valor da caixa de seleção estiver definido como o número 0. Isso fará com que pareça que a caixa de seleção não pode ser selecionada, pois marcá-la definirá o FormControlcomo o número 0(um valor falso) e, portanto, o manterá desmarcado. Seria preferível não usar o número 0como um valor, mas se for necessário, você deve definir condicionalmente 0para algum valor verdadeiro, por exemplo, string '0'ou simplesmente truee, ao enviar, convertê-lo de volta para o número 0.

Exemplo StackBlitz

O StackBlitz também tem código para quando você deseja passar valores padrão para as caixas de seleção, para que sejam marcados como marcados na IU.

nash11
fonte
4

Faça um evento quando for clicado e então altere manualmente o valor de true para o nome que a caixa de seleção representa, então o nome ou true terá o mesmo valor e você poderá obter todos os valores em vez de uma lista de true / false. Ex:

component.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
    }
}

Isso captura o evento depois que ele já foi alterado para verdadeiro ou falso pelo Angular Forms, se for verdadeiro eu altero o nome para o nome do que a caixa de seleção representa, que se necessário também será avaliada como verdadeira se estiver sendo marcada para verdadeiro / falso como bem.

canada11
fonte
Isso me colocou no caminho certo, acabei fazendo isso this.customForm.patchValue ({[event.target.id]: event.target.checked});
Demodave de
4

Se você quiser usar uma forma reativa angular ( https://angular.io/guide/reactive-forms ).

Você pode usar um controle de formulário para gerenciar o valor de saída do grupo de caixas de seleção.

componente

import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
import { flow } from 'lodash';
import { flatMap, filter } from 'lodash/fp';

@Component({
  selector: 'multi-checkbox',
  templateUrl: './multi-checkbox.layout.html',
})
export class MultiChecboxComponent  {

  checklistState = [ 
      {
        label: 'Frodo Baggins',
        value: 'frodo_baggins',
        checked: false
      },
      {
        label: 'Samwise Gamgee',
        value: 'samwise_gamgee',
        checked: true,
      },
      {
        label: 'Merry Brandybuck',
        value: 'merry_brandybuck',
        checked: false
      }
    ];

  form = new FormGroup({
    checklist : new FormControl(this.flattenValues(this.checklistState)),
  });


  checklist = this.form.get('checklist');

  onChecklistChange(checked, checkbox) {
    checkbox.checked = checked;
    this.checklist.setValue(this.flattenValues(this.checklistState));
  }

  flattenValues(checkboxes) {
    const flattenedValues = flow([
      filter(checkbox => checkbox.checked),
      flatMap(checkbox => checkbox.value )
    ])(checkboxes)
    return flattenedValues.join(',');
  }
}

html

<form [formGroup]="form">
    <label *ngFor="let checkbox of checklistState" class="checkbox-control">
    <input type="checkbox" (change)="onChecklistChange($event.target.checked, checkbox)" [checked]="checkbox.checked" [value]="checkbox.value" /> {{ checkbox.label }}
  </label>
</form>

checklistState

Gerencia o modelo / estado das entradas da lista de verificação. Este modelo permite mapear o estado atual para qualquer formato de valor necessário.

Modelo:

{
   label: 'Value 1',
   value: 'value_1',
   checked: false
},
{
  label: 'Samwise Gamgee',
  value: 'samwise_gamgee',
  checked: true,
},
{
  label: 'Merry Brandybuck',
  value: 'merry_brandybuck',
  checked: false
}

checklist Controle de formulário

Este controle armazena o valor que gostaria de salvar como, por exemplo

saída de valor: "value_1,value_2"

Veja a demonstração em https://stackblitz.com/edit/angular-multi-checklist

Robert Prib
fonte
Facilmente a melhor solução para mim. Muito obrigado.
Newclique 01 de
2

Minha solução - resolvi para Angular 5 com Material View
A conexão é através do

formArrayName = "notificação"

(alterar) = "updateChkbxArray (n.id, $ event.checked, 'notificação')"

Dessa forma, ele pode funcionar para matrizes de várias caixas de seleção em um formulário. Basta definir o nome do array de controles para conectar a cada vez.

constructor(
  private fb: FormBuilder,
  private http: Http,
  private codeTableService: CodeTablesService) {

  this.codeTableService.getnotifications().subscribe(response => {
      this.notifications = response;
    })
    ...
}


createForm() {
  this.form = this.fb.group({
    notification: this.fb.array([])...
  });
}

ngOnInit() {
  this.createForm();
}

updateChkbxArray(id, isChecked, key) {
  const chkArray = < FormArray > this.form.get(key);
  if (isChecked) {
    chkArray.push(new FormControl(id));
  } else {
    let idx = chkArray.controls.findIndex(x => x.value == id);
    chkArray.removeAt(idx);
  }
}
<div class="col-md-12">
  <section class="checkbox-section text-center" *ngIf="notifications  && notifications.length > 0">
    <label class="example-margin">Notifications to send:</label>
    <p *ngFor="let n of notifications; let i = index" formArrayName="notification">
      <mat-checkbox class="checkbox-margin" (change)="updateChkbxArray(n.id, $event.checked, 'notification')" value="n.id">{{n.description}}</mat-checkbox>
    </p>
  </section>
</div>

No final você está conseguindo salvar o formulário com a matriz de id de registros originais para salvar / atualizar. A visão da IU

A parte relevat do json do formulário

Ficarei feliz em receber comentários para melhorias.

Tzvi Gregory Kaidanov
fonte
0

PARTE DO MODELO: -

    <div class="form-group">
         <label for="options">Options:</label>
         <div *ngFor="let option of options">
            <label>
                <input type="checkbox"
                   name="options"
                   value="{{option.value}}"
                   [(ngModel)]="option.checked"
                                />
                  {{option.name}}
                  </label>
              </div>
              <br/>
         <button (click)="getselectedOptions()"  >Get Selected Items</button>
     </div>

PARTE DO CONTROLADOR: -

        export class Angular2NgFor {

          constructor() {
             this.options = [
              {name:'OptionA', value:'first_opt', checked:true},
              {name:'OptionB', value:'second_opt', checked:false},
              {name:'OptionC', value:'third_opt', checked:true}
             ];


             this.getselectedOptions = function() {
               alert(this.options
                  .filter(opt => opt.checked)
                  .map(opt => opt.value));
                }
             }

        }
Abhishek Srivastava
fonte
1
Olá @EchoLogic .. Por favor, deixe-me saber em caso de qualquer dúvida
Abhishek Srivastava
1
Isso não está usando ReactiveForms, mas formulários regulares, então não responde à pergunta
Guillaume
0

Adicione meus 5 centavos) Meu modelo de pergunta

{
   name: "what_is_it",
   options:[
     {
      label: 'Option name',
      value: '1'
     },
     {
      label: 'Option name 2',
      value: '2'
     }
   ]
}

template.html

<div class="question"  formGroupName="{{ question.name }}">
<div *ngFor="let opt of question.options; index as i" class="question__answer" >
  <input 
    type="checkbox" id="{{question.name}}_{{i}}"
    [name]="question.name" class="hidden question__input" 
    [value]="opt.value" 
    [formControlName]="opt.label"
   >
  <label for="{{question.name}}_{{i}}" class="question__label question__label_checkbox">
      {{opt.label}}
  </label>
</div>

component.ts

 onSubmit() {
    let formModel = {};
    for (let key in this.form.value) {
      if (typeof this.form.value[key] !== 'object') { 
        formModel[key] = this.form.value[key]
      } else { //if formgroup item
        formModel[key] = '';
        for (let k in this.form.value[key]) {
          if (this.form.value[key][k])
            formModel[key] = formModel[key] + k + ';'; //create string with ';' separators like 'a;b;c'
        }
      }
    }
     console.log(formModel)
   }
Chemaxa
fonte