Angular 10: converti i valori dell'array FormControl in stringhe separate da virgole

Oct 07 2020

MODIFICA 1:

Modifico la mia domanda per chiarire la mia necessità.

Ho un FormGroupche contiene FormControl.

Tra questi FormControl, ce n'è uno che riceve un array di valori.

Quello che sto cercando è se esiste una soluzione per fare in modo FormControlche riceva valori di matrice invece di ricevere valori in un valore di stringa separato da virgole (senza parentesi []).

Cosa ottengo: (valori array)

Cosa voglio: (valori stringa separati da virgola senza brackets[])

Collegamento della forcella: qui

  • Grazie in anticipo per il vostro aiuto

Domanda originale:

Vorrei convertire i dati ricevuti da a FormArrayin stringhe con virgole.

Sono riuscito a farlo in Console.log, ma non so come inviare i dati convertiti al file FormGroup.

Il mio file TS:

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

Mio 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>

Quando seguo questi passaggi, ottengo null"Accessoire" e valori di array su "Accessoires".

Quello che sto cercando è prendere i valori scelti da un elenco di caselle di controllo e convertirli in stringhe con virgole. Non voglio inviarli sotto forma di array.

Grazie in anticipo per le tue soluzioni.

MODIFICA 1:

Ho usato la prima soluzione Option di @akash e funziona. Ho solo un piccolo problema. Quando ottengo i valori selezionati su un input, e voglio inviarlo a formControl... ottengo il modulo di matrice con parentesi ... Quello che voglio è il formato di stringa con virgole. ecco il link del mio problema:https://stackblitz.com/edit/reactive-form-string-values

Risposte

4 Eliseo Oct 13 2020 at 18:18
<mat-select 
      [value]="toppings.value?toppings?.value.split(','):null" 
      (selectionChange)="toppings.setValue($event.value.join(','))"
       multiple>
...
</mat-select>

Ricorda FormControlun'esistenza anche se non hai input. Bene, devi riscrivere tutto il tuo codice per ricevere una stringa, alcuni come questo stackblitz biforcuto

3 Akash Oct 09 2020 at 18:18

Aggiornamento 2 :

Per popolare il tuo modulo dai valori db, inizializza il controllo toppingscome sotto (vedi questo fork ) -

toppingsValue: string = 'Extra cheese, Mushroom';
this.demandeDevis = this.formBuilder.group({
      toppings: [this.toppingsValue.split(',').map(a => a.trim())]
});

Aggiornamento 1 :

Penso di aver frainteso quello che stai cercando di ottenere. Si prega di capire come funziona FormGroup angolare . FormGroupha il campo dei controlli che è ciò che passi come oggetto sotto. Questo oggetto è un valore-chiave dove la chiave è il nome del controllo (guarnizioni) e il valore è il controllo (ad es. FormControl, FormArray o FormGroup) -

this.demandeDevis = this.formBuilder.group({
      toppings: this.toppings 
});

Quindi, quando lo fai this.demandeDevis.value, ottieni l'oggetto con le stesse chiavi (guarnizioni) e valori nel controllo corrispondente. È possibile utilizzare il codice seguente per ottenere i valori separati da virgole per toppings. Non sono sicuro che sia quello che stai cercando perché è ovvio (vedi questo fork ) -

validateForm(){
    const toppingsValue = this.demandeDevis.controls['toppings'].value;
    console.log(toppingsValue.toString()); 
}

Risposta originale :

Se sei aperto a diversi modi per l'aspetto della tua interfaccia utente, usa angular material

Opzione 1 :

Usa mat-selectcomponente. Con l'esempio seguente, non è necessario gestire la selezione / deselezione delle opzioni. Si prega di vedere questo esempio su 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>

Opzione 2 :

Se non desideri utilizzare il menu a discesa, utilizza mat-selection-list, guarda questo esempio . L'esempio seguente utilizza il riferimento a mat-selection-listper ottenere l'elenco delle opzioni selezionate nel componente:

<mat-selection-list #shoes (selectionChange)=selectionChange()>
  <mat-list-option [value]="shoe" *ngFor="let shoe of typesOfShoes">
    {{shoe}}
  </mat-list-option>
</mat-selection-list>
{{selectedValuesG}}
2 mr.vea Oct 14 2020 at 02:02

Puoi fare qualcosa di simile qui

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

Dovrai dividerlo di nuovo per popolare l'array di condimenti, ma non sono sicuro se ne hai bisogno.

1 OwenKelvin Oct 11 2020 at 14:02

La mia soluzione sarà quella di utilizzare Observablee trasformare i dati quando richiesto.

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

Nell'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 }}

Ora puoi accedere all'elenco come una stringa separata utilizzando toppingsValue

Vedi esempio su Stackblit

1 OwenKelvin Oct 15 2020 at 01:02

Dovrai creare un controllo personalizzato

Vedi Soluzione funzionante

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>