Reputation: 288
I modify my question to clarify my need.
I have a FormGroup
that contains FormControl
.
Among these FormControl
, there is one that receives an array values.
What I'm looking for is if there is a solution to make the FormControl
that receives an array values instead receive values in a comma separated string value (without the brackets []
).
brackets[]
)Link of the fork : Here
I would like to convert the data received from a FormArray
to Strings with commas.
I managed to do this in Console.log, but I don't know how to send the converted data to the FormGroup
.
My TS file :
accessoire: string;
this.demandeDevis = this.formBuilder.group({
accessoires: new FormArray([]),
accessoire: this.accessoire,
});
onCheckboxChange(event) {
const checkAcs: FormArray = this.demandeDevis.get(
'accessoires'
) as FormArray;
if (event.target.checked) {
checkAcs.push(new FormControl(event.target.value));
console.log(checkAcs.value.toString());
this.accessoire = checkAcs.value.toString();
} else {
let i: number = 0;
checkAcs.controls.forEach((item: FormControl) => {
if (item.value == event.target.value) {
checkAcs.removeAt(i);
return;
}
i++;
});
}
}
My HTML
:
<ul class="list-group list-group-flush">
<li class="list-group-item p-3">
Ventilateur cabine
<label class="switch">
<input type="checkbox" value="Ventilateur cabine" class="primary"
(change)="onCheckboxChange($event)" />
<span class="slider"></span>
</label>
</li>
<li class="list-group-item p-3">
Manoeuvre pompier
<label class="switch">
<input type="checkbox" class="primary" value="Manoeuvre pompier"
(change)="onCheckboxChange($event)" />
<span class="slider"></span>
</label>
</li>
<li class="list-group-item p-3">
Afficheur à tous les étages
<label class="switch">
<input type="checkbox" class="primary" value="Afficheur à tous les étages"
(change)="onCheckboxChange($event)" />
<span class="slider"></span>
</label>
</li>
<li class="list-group-item p-3">
Gong
<label class="switch">
<input type="checkbox" class="primary" value="Gong"
(change)="onCheckboxChange($event)" />
<span class="slider"></span>
</label>
</li>
<li class="list-group-item p-3">
Système de secours automatique
<label class="switch">
<input type="checkbox" class="primary" value="Système de secours automatique"
(change)="onCheckboxChange($event)" />
<span class="slider"></span>
</label>
</li>
</ul>
When I follow these steps, I get null
on " Accessoire " and array values on " Accessoires ".
What I'm looking for is to take the values chosen from a checkbox list and convert them to strings with commas. I don't want to send them in Arrays form.
Thank you in advance for your solutions.
EDIT 1 :
I used the first Option of @akash solution and it's working. I have just a little problem. When i get the values selected on an input
, and I want to send it to a formControl
... I get the array form with brackets ... What I want is string form with commas. here's the link of my problem:
https://stackblitz.com/edit/reactive-form-string-values
Upvotes: 2
Views: 9768
Reputation: 15083
You will need to create a custom control
app-string-select.ts
import { Component, forwardRef, OnInit } from "@angular/core";
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
import { Observable, of } from "rxjs";
@Component({
selector: "app-string-select",
templateUrl: "./string-select.component.html",
styleUrls: ["./string-select.component.css"],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => StringSelectComponent),
multi: true
}
]
})
export class StringSelectComponent implements ControlValueAccessor {
value: string[] = [];
allToppings$: Observable<string[]> = of([
"Extra cheese",
"Mushroom",
"Onion",
"Pepperoni",
"Sausage",
"Tomato"
]);
onChange: any = () => {};
onTouched: any = () => {};
constructor() {}
writeValue(value: string): void {
this.value = value.split(",");
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
emitChange() {
this.onChange(this.value.join(","));
}
}
app-string-select.html
<mat-form-field appearance="fill">
<mat-label>Toppings</mat-label>
<mat-select [(ngModel)]="value" (ngModelChange)='emitChange()' multiple>
<mat-option *ngFor="let topping of allToppings$ | async" [value]="topping">{{topping}}</mat-option>
</mat-select>
</mat-form-field>
select-multiple-example.ts
import {ChangeDetectionStrategy, Component} from '@angular/core';
import {FormControl, FormGroup, FormBuilder} from '@angular/forms';
import {Observable, of } from 'rxjs';
import {tap } from 'rxjs/operators';
/** @title Select with multiple selection */
@Component({
selector: 'select-multiple-example',
templateUrl: 'select-multiple-example.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SelectMultipleExample {
demandeDevis: FormGroup = this.formBuilder.group({ toppings: ['']});
initialToppings$: Observable<string> = of('Onion,Pepperoni,Tomato').pipe(
tap(toppings => this.toppings.patchValue(toppings))
)
get toppings(): FormControl {
return this.demandeDevis.get('toppings') as FormControl
};
constructor(private formBuilder: FormBuilder) {}
validateForm(){
const valuesForm = this.toppings.value;
console.log(valuesForm);
}
}
select-multiple-example.html
<form [formGroup]="demandeDevis" *ngIf="initialToppings$ | async">
<app-string-select formControlName='toppings'></app-string-select>
<div>
<button (click)="validateForm()" >
Validate
</button>
</div>
</form>
Upvotes: 1
Reputation: 454
You can do something like this here
ngOnInit() {
this.initDemandeDevis();
this.toppings.valueChanges.subscribe(value => {
this.demandeDevis.patchValue({
toppingsToString: value.length > 0 ? value.join(",") : ""
});
});
}
initDemandeDevis() {
this.demandeDevis = this.formBuilder.group({
toppingsToString: "",
accessoires: null
});
}
You will have to split it back up to populate the toppings array, but I am not sure if you need it.
Upvotes: 2
Reputation: 15083
My solution will be to use Observable
and transform the data whenever required.
// We define an Observable list of toppings
allToppings$: Observable<string[]> = of (['Extra cheese', 'Mushroom', 'Onion', 'Pepperoni', 'Sausage', 'Tomato']);
// We define our FormGroup
demandeDevis: FormGroup = this.formBuilder.group({
toppings: [[]]
});
// Lets assume an initial topping in the form of string. Since MatSelect returns array, we will convert this to array and assign it to toppings selected
initialToppings$: Observable<string> = of('Onion, Pepperoni, Tomato');
initialToppingListArray$: Observable<string[]> = this.initialToppings$.pipe(
map(toppingList => toppingList.split(',')),
tap(toppings => this.toppings.patchValue(toppings))
);
// Next we define toppings and toppingsValue to extract toppings and toppings as a string from the form group
get toppings(): FormControl {
return this.demandeDevis.get('toppings') as FormControl
};
get toppingsValue(): string {
return this.toppings.value.join(',')
}
// Finally I will combine allToppings$ and initialToppingListArray$ to use one async pipe in the HTML
v$ = forkJoin([this.allToppings$, this.initialToppingListArray$]).pipe(
map(([allToppings]) => ({ allToppings}))
)
In the HTML
<form [formGroup]="demandeDevis" *ngIf='v$ | async as v'>
<mat-form-field appearance="fill">
<mat-label>Toppings</mat-label>
<mat-select formControlName="toppings" multiple>
<mat-option *ngFor="let topping of v.allToppings" [value]="topping">{{topping}}</mat-option>
</mat-select>
</mat-form-field>
<button (click)="validateForm()" >
Validate (console.log)
</button>
</form>
TOPPINGS VALUE: {{ toppingsValue }}
Now you can access the list as a string separated by using toppingsValue
Upvotes: 1
Reputation: 57939
<mat-select
[value]="toppings.value?toppings?.value.split(','):null"
(selectionChange)="toppings.setValue($event.value.join(','))"
multiple>
...
</mat-select>
Remember a FormControl
exist even you has no input. Well, you need rewrite all your code to received an string, some like this forked stackblitz
Upvotes: 6
Reputation: 4602
Update 2:
To populate your form from db values, just initialize the control toppings
like below (see this fork) -
toppingsValue: string = 'Extra cheese, Mushroom';
this.demandeDevis = this.formBuilder.group({
toppings: [this.toppingsValue.split(',').map(a => a.trim())]
});
Update 1:
I think I misunderstood what you're trying to achieve. Please understand how angular FormGroup works. FormGroup
has controls field which is what you pass as an object below. This object is a key-value where key is your control name (toppings) and value is your control (eg. FormControl, FormArray or FormGroup) -
this.demandeDevis = this.formBuilder.group({
toppings: this.toppings
});
So when you do this.demandeDevis.value
, you get the object with same keys (toppings) and values in the corresponding control. You can use below code to get the comma separated values for toppings
. Not sure if this is what you're looking for because this is obvious (see this fork) -
validateForm(){
const toppingsValue = this.demandeDevis.controls['toppings'].value;
console.log(toppingsValue.toString());
}
Original answer:
If you are open to different ways for how you'd like your UI to look like, use angular material
Option 1:
Use mat-select
component. With below example, you don't need to handle selection/deselection of options. Please see this example on StackBlitz -
<mat-form-field appearance="fill">
<mat-label>Toppings</mat-label>
<mat-select [formControl]="toppings" multiple>
<mat-option *ngFor="let topping of toppingList" [value]="topping">{{topping}}</mat-option>
</mat-select>
</mat-form-field>
<div>
{{toppings.value}}
</div>
Option 2:
If you don't want to use dropdown, use mat-selection-list
, see this example. Below example uses reference to mat-selection-list
to get the list of selected options in the component -
<mat-selection-list #shoes (selectionChange)=selectionChange()>
<mat-list-option [value]="shoe" *ngFor="let shoe of typesOfShoes">
{{shoe}}
</mat-list-option>
</mat-selection-list>
{{selectedValuesG}}
Upvotes: 3