Solucionado (ver solução)
Solucionado
(ver solução)
8
respostas

Formulário com checkbox

Tenho um formulário com checkbox do bootstrap que suporta seleção múltipla. Como eu faço o binding(ngModel) dos valores dos checkbox num array do componente para que eu possa tratar os dados numa função?

8 respostas

Removi a resposta, eu dei uma resposta para o angular 1. Sua pergunta foi para o Angular 2!

Você nem precisa de ngModel, pode usar diretamente um FormGroup.

https://netbasal.com/handling-multiple-checkboxes-in-angular-forms-57eb8e846d21

Neste exemplo, ele cria uma lista de itens para ser exibidos como checkbox e implementa a lógica de seleção de cada um deles.

Não compreendi muito bem o código. Poderia me explicar parte por parte?

Component

export class App {
  user = {
    skills: [
      { name: 'JS',  selected: true, id: 12 },
      { name: 'CSS',  selected: false, id: 2 },
    ]
  }
  form;
  constructor(private fb: FormBuilder) {
     console.clear();

     this.form = this.fb.group({
       skills: this.buildSkills()
     });

     console.log(this.form.get("skills"))
  }

  get skills(): FormArray {
    return this.form.get('skills') as FormArray;
  };


  buildSkills() {
    const arr = this.user.skills.map(s => {
      return this.fb.control(s.selected);
      // return this.fb.group({
      //   selected: [s.selected],
      //   id: [s.id]
      // }
    })
    return this.fb.array(arr);
  }

  submit(value) {

    const f = Object.assign({}, value, {
      skills: value.skills.map((s, i) => {
      return {
        id: this.user.skills[i].id,
        selected: s
      }
    });
    })

     console.log(f);
  }


}
Template

<form [formGroup]="form" (submit)="submit(form.value)">
     <div *ngFor="let skill of skills.controls; let i=index">
       <input type="checkbox" [formControl]="skill"/>{{user.skills[i].name}}
     </div>
     <button type="submit">Submit</button>
   </form>

   {{ form.value | json }}

Há um objeto usuário com uma lista de skills.

 user = {
    skills: [
      { name: 'JS',  selected: true, id: 12 },
      { name: 'CSS',  selected: false, id: 2 },
    ]
  }

Veja que nessa lista de skills, há a propriedade selected que esta como true ou false. É ela que faz o elemento estar selecionado ou não. Nesse sentido, basta transpor para o seu problema sabendo que cada item da sua lista que alimentará o checkbox precisa ter uma propriedade para guardar a informação se ele esta marcado ou não. Padrão, nada de extraordinário aqui.

Cada item desse array precisa fazer parte de um formGroup. Você aprendeu formgroup no curso de Angular da Alura.

Há um método auxiliar:

buildSkills() {
    const arr = this.user.skills.map(s => {
      return this.fb.control(s.selected);
    })
    return this.fb.array(arr);
  }

O que esse método faz é aplicar um mapmuito basicão para converter cada item da lista de skill em um controle do formulário. O arr será uma lista de controles e não mais uma lista de skillss. Por fim, o this.fb.array(arr) (novidade aqui) converte todos os controllers que serão acessíveis através do seu formulário pelo nome skills. Lembrando que skills é a lista que constrói seus checkboxes.

Olhando o template:

<form [formGroup]="form" (submit)="submit(form.value)">
     <div *ngFor="let skill of skills.controls; let i=index">
       <input type="checkbox" [formControl]="skill"/>{{user.skills[i].name}}
     </div>
     <button type="submit">Submit</button>
   </form>

   {{ form.value | json }}

A diretiva *ngFor esta iterando no array de skills e montando um componente para cada um deles. Até ai não há novidade, você viu isso no curso. Mas há uma coisa diferente. Cada input que ele criará será associado a um controle lá do seu formGroup do modelo. Mas como ele sabe em qual deles ficará associado? Ele usa um truque para pegar o índice de cada elemento para poder referência dentro do form group. Pode ignorar a instrução `.

No componente, o método submit esta assim:

 submit(value) {

    const f = Object.assign({}, value, {
      skills: value.skills.map((s, i) => {
      return {
        id: this.user.skills[i].id,
        selected: s
      }
    });
    })

     console.log(f);
  }

Ele usa o Object.assign para criar um novo objeto baseado na lista de skills. Mas essa lista de skills são controles e não objetos JavaScript. Por isso ele faz um map para converter cada item para um objeto com id e a propriedade selected. É esse objeto f que será será enviado para o banco.

Uma solução mais simples se você não quiser algo tão genérico quanto a solução anterior pode fazer simplesmente:

<input type="checkbox" [checked]="opcao1" (change)="opcao1 = !opcao1" />

<input type="checkbox" [checked]="opcao2" (change)="opcao2 = !opcao2" />

<input type="checkbox" [checked]="opcao3" (change)="opcao3 = !opcao3" />

No seu componente:

// decorator omitido
export class MeuComponente {
         opcao1: boolean = false;
         opcao2: boolean = false;
         opcao3: boolean = false;
 }

Toda vez que voc6e marcar um checkbox, ele vai mudar a propriedade opcao1 do componente. Você só precisa iniciar a propriedade opcao1 como true oufalse lá no componente. Aprendemos a realizar esse tipo de data binding no curso, mas acho que você não quer essa solução pela sua pergunta. Ao submeter o formulário, você consultaria essas propriedades.

Oi Felipe, tentou a última solução? Ela é bem mais simples, talvez eu tenha entendido que você queria algo mais complexo. Eu uso essa forma que postei para você.

Aguardo seu feedback.

Eu estava tentando a primeira solução e não deu muito certo. Vou tentar a segunda e dou a resposta

solução!

Eu consegui montar um formulário usando radio buttons baseado em várias fontes .Eu estou conseguindo pegar a opção do radio button e do select mas não faço ideia de como pegar os valores da lisa de checkboxes

Template

<button type="button" class="btn btn-success btn-cabecalho" placement="bottom" ngbTooltip="Exportar" (click)="abrirModal(conteudo)">
  <i class="fa fa-upload" aria-hidden="true"></i>
</button>

<ng-template #conteudo let-c="close" let-d="dismiss">
  <div class="modal-header">
    <h4 class="modal-title">Exportar</h4>
    <button type="button" class="close" aria-label="Fechar" (click)="c('Cross click')">
      <span aria-hidden="true">&times;</span>
    </button>
  </div>
  <div class="modal-body container">
    <form novalidate id="formulario" name="formulario" [formGroup]="globalForm" (submit)="exportar($event)">
      <div class="radio">
        <input type="radio"  value="opTodasVersoes" formControlName="opcao" [(ngModel)]="opForm"><label>Todas as Versões</label>
      </div>
      <div class="radio row">
        <input type="radio"  value="opVersao" formControlName="opcao" [(ngModel)]="opForm"><label>Versão:</label>
        <!-- SELECT DE VERSÕES -->
        <div class="form-group" *ngIf="opForm == 'opVersao'">
          <select class="form-control" name="versao" formControlName="versao">
            <option value="null">Selecione uma versão</option>
            <option *ngFor="let versao of versaoLista" [ngValue]="versao">{{versao.versao}}</option>
          </select>
        </div>
      </div>
      <div class="radio">
        <input type="radio" value="opRegras" formControlName="opcao" [(ngModel)]="opForm"><label>Regras:</label>
        <!-- TABELA DE REGRAS -->
        <div class="container form-table" *ngIf="opForm == 'opRegras'">
          <input #regraBusca class="form-control" placeholder="Digite a regra a ser buscada" (keyup)="0">
            <div class="checkbox form-table-item" *ngFor="let regra of regraLista | filtroRegras: regraBusca.value">
              <input type="checkbox"><label>{{regra.regra}}</label>
            </div>
        </div>
      </div>
      <span [hidden]="!onSubmit" *ngIf="globalForm.get('opcao').invalid" class="form-control alert-danger">
          Selecione uma opção
      </span>
    </form>
  </div>
  <div class="modal-footer">
    <button class="btn btn-primary" type="submit" form="formulario">Exportar</button>
  </div>
</ng-template>
Componente

export class ExportarRegrasComponent {

  private modalRef: NgbModalRef;
  private onSubmit: boolean= false;
  private opForm: string; //Opcão no formulário para esconder ou mostrar o select ou a tabela
  private exportacao= new Exportacao();
  private globalForm: FormGroup;

  constructor(private modalSv: NgbModal, private formBuilder: FormBuilder) {
    this.globalForm= this.formBuilder.group({
      opcao: ['', Validators.required],
      versao: [''],
      regras: this.formBuilder.array([])
    });
  }

  abrirModal(conteudo){
    this.modalRef= this.modalSv.open(conteudo, {backdrop: "static", keyboard: false});
  }

  exportar(event) {
    event.preventDefault();
    this.onSubmit= true;
    if(this.globalForm.invalid){
      return;
    }
    this.exportacao.opcao= this.globalForm.get('opcao').value;
    this.exportacao.versao= this.globalForm.get('versao').value;
    this.exportacao.regras= this.globalForm.get('regras').value;
    console.log(this.exportacao);
    this.globalForm.reset();
    this.onSubmit= false;
  }

  buildRegras(){
    const regrasArray= this.regraLista.map(regra=>{
      return this.formBuilder.control(regra.selected)
    });
    return this.formBuilder.array(regrasArray);
  }

  versaoLista= [
    {versao: "versao 1", id: 1},
    {versao: "versao 2", id: 2},
    {versao: "versao 3", id: 3}
  ]

  regraLista= [
    {regra: "regra 1", id: 1, selected: false},
    {regra: "regra 2", id: 2, selected: false},
    {regra: "regra 3", id: 3, selected: false},
    {regra: "regra 4", id: 4, selected: false},
    {regra: "regra 5", id: 5, selected: false},
    {regra: "regra 6", id: 6, selected: false},
    {regra: "regra 7", id: 7, selected: false},
    {regra: "regra 8", id: 8, selected: false},
    {regra: "regra 9", id: 9, selected: false},
    {regra: "regra 10", id: 10, selected: false}
  ]

}

}
Model

export class Exportacao{
    opcao: string;
    versao: string= undefined;
    regras: Array<string>= undefined;
}