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?
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?
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 map
muito 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
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">×</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;
}