Angular ReactiveForms: Tworzysz tablicę wartości pól wyboru?

104

Biorąc pod uwagę listę pól wyboru powiązanych z tym samym formControlName, w jaki sposób mogę utworzyć tablicę wartości pól wyboru powiązanych z formControl, a nie po prostu true/ false?

Przykład:

<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 obecnie produkuje:

true or false

Co chcę, żeby wyprodukował:

['value-1', 'value-2', ...]
ReactingToAngularVues
źródło
czy znalazłeś jakieś rozwiązanie?
PK
Jest to prawdopodobnie najbardziej skomplikowany sposób tworzenia pól wyboru w formie. Nie jest to wcale proste.
mwilson
8
Kątowy. Jedyne, co próbuję zrobić, to zmusić grupę radioaktywną mat do związania w mojej reaktywnej formie. Nie pamiętam, bym tak bardzo zmagał się z kątowością. Wszystkie artykuły wskazują na to samo. Po prostu nie mogę zmusić tego do pracy. Wszystko inne jest bardzo proste. Prawdopodobnie patrzyłem na to zbyt długo. Nadal wydaje się, że jest zbyt skomplikowana jak na wartość tablicy w formularzu.
mwilson
3
Tak, to było okropne, kiedy zapytałem o to w 2016 roku i nadal jest okropne w 2019.
ReactingToAngularVues
3
Nie dodam tony do tego pytania, ale chciałem, aby inni wiedzieli, że czuję to samo. Już samo to było najtrudniejszą częścią uczenia się kątowych form reaktywnych. Czuję, że to wcale nie powinno być takie trudne. Jednak cieszę się, że nie jestem sam w tej walce. Więc dziękuję za zadanie pytania.
NorthStarCode

Odpowiedzi:

51

Z pomocą cichej odpowiedzi napisałem rozwiązanie, aby uzyskać wartości zamiast stanów w moim formBuilder.

Używam metody do dodawania lub usuwania wartości w formArray. To może być złe podejście, ale działa!

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

Kiedy przesyłam formularz, na przykład mój model wygląda tak:

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

Brakuje tylko jednej rzeczy, funkcji do wypełnienia formularza formArray, jeśli Twój model ma już sprawdzone wartości.

Guymage
źródło
Jak sprawdzić, czy moje pole wyboru jest zaznaczone, gdy ładuję dane po użyciu Twojego przykładu, aby wejść do bazy danych?
Devora
W tym rozwiązaniu formularz jest zawsze ważny, nawet jeśli pole wyboru nie jest zaznaczone
Teja
myChoices: new FormArray([], Validators.required)
Bikram Nath
"Ale to działa!" tak zaczyna się dług technologiczny. To nie jest reaktywna forma robienia tego. Jedną z zalet używania formcontrols dla każdego pola wyboru jest to, że mogą zapamiętać swoje stany nawet po ponownym dodaniu ich do DOM.
MIWMIB
50

Oto dobre miejsce do korzystania z FormArray https://angular.io/docs/ts/latest/api/forms/index/FormArray-class.html

Na początek zbudujemy naszą tablicę kontrolek z FormBuilderrozszerzeniem lub nowym rozszerzeniemFormArray

FormBuilder

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

nowy FormArray

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

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

To dość łatwe, ale potem zmienimy nasz szablon i pozwolimy silnikowi szablonów obsłużyć sposób, w jaki łączymy się z naszymi kontrolkami:

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>

Tutaj iterujemy nasz zestaw FormControlsw naszym myValues FormArrayi dla każdej kontrolki wiążemy się [formControl]z tą kontrolką, a nie z FormArraykontrolką i <div>{{checkboxGroup.controls['myValues'].value}}</div>produkuje, true,false,truejednocześnie sprawiając, że składnia szablonu jest nieco mniej ręczna.

Możesz użyć tego przykładu: http://plnkr.co/edit/a9OdMAq2YIwQFo7gixbj?p=preview do przeglądania

silentsod
źródło
1
prawdopodobnie powinieneś usunąć id = "xxx", identyfikator powinien być unikalny, prawda?
PeiSong Xiong,
1
dla id można użyć indeksu *ngFor="let control of checkboxGroup.controls['myValues'].controls ; let i=index""
Mirza
9
To jest fajne, ale tworzy całkowicie ogólną tablicę pól wyboru. Prawdopodobnie ładowałbyś tablicę lub coś innego i kojarzył każde pole wyboru z jakąś inną wartością. Na przykład, jak dodać ciąg tekstowy do użycia w etykiecie formularza do każdej kontrolki formularza?
Askdesigners
NM Właśnie zmapowałem to obok tablicy zewnętrznej: p
Askdesigners
@Askdesigners Czy możesz opublikować swoje rozwiązanie, aby mieć pola wyboru i etykiety?
eddygeek
26

Jest to znacznie łatwiejsze w Angular 6 niż w poprzednich wersjach, nawet jeśli informacje o polu wyboru są wypełniane asynchronicznie z interfejsu API.

Pierwszą rzeczą, którą należy sobie uświadomić, jest to, że dzięki keyvaluerurze Angular 6 nie musimy już jej używać FormArray, a zamiast tego możemy zagnieździć FormGroup.

Najpierw przekaż FormBuilder do konstruktora

constructor(
    private _formBuilder: FormBuilder,
) { }

Następnie zainicjalizuj nasz formularz.

ngOnInit() {

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

}

Gdy nasze dane opcji pola wyboru są dostępne, iteruj je i możemy włożyć je bezpośrednio do zagnieżdżonego FormGroupjako nazwanego FormControl, bez konieczności polegania na tablicach wyszukiwania indeksowanych według liczb.

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

Na koniec w szablonie musimy tylko iterować keyvaluepola wyboru: bez dodatkowych let index = i, a pola wyboru będą automatycznie ułożone w kolejności alfabetycznej: znacznie czystsze.

<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
źródło
1
Bardzo przydatne również w przypadku prostej zakodowanej na stałe tablicy wartości pól wyboru. Następnie możesz dodać kontrolki formularza, używając podobnej pętli for od razu w ngOnInit (), a pola wyboru w formularzu będą dynamicznie odzwierciedlać tablicę wartości pól wyboru
Arjan
3
To wciąż fragmenty [klucz1 = prawda, klucz2 = fałsz, klucz3 = prawda]. Chcemy ['key1', 'key3']
f.khantsis
@ f.khantsis Możesz to zrobić w ten sposób: `const value = {klucz1: prawda, klucz2: fałsz, klucz3: prawda}; const list = Object.entries (wartość) .filter (([_, isSelected]) => isSelected) .map (([klucz]) => klucz); console.log (lista); `
zauni
2
Najlepsze rozwiązanie imho. Możesz umieścić zlecenie const checkboxes = ..poza foreach;)
Bernoulli IT
Co się dzieje, gdy klucz pozycji jest taki sam, jak inne pole w formularzu? Na przykład mam dwie różne tablice pól wyboru, każda z klawiszami „Mały”, „Średni” i „Duży”?
Newclique
9

Jeśli szukasz wartości pól wyboru w formacie JSON

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

Pełny przykład tutaj .

Przepraszam za używanie nazw krajów jako wartości pól wyboru zamiast tych w pytaniu. Dalsze wyjaśnienie -

Utwórz FormGroup dla formularza

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

Niech każde pole wyboru będzie FormGroup zbudowane z obiektu, którego jedyną właściwością jest wartość pola wyboru.

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

Tablica FormGroups dla pól wyboru służy do ustawiania kontrolki dla „krajów” w formularzu nadrzędnym.

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

W szablonie użyj potoku, aby uzyskać nazwę kontrolki pola wyboru

  <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>
kwas
źródło
7

Nie widzę tutaj rozwiązania, które całkowicie odpowiada na pytanie przy użyciu formularzy reaktywnych w pełnym zakresie, więc oto moje rozwiązanie na to samo.


Podsumowanie

Oto sedno szczegółowego wyjaśnienia wraz z przykładem StackBlitz.

  1. Użyj FormArraydla pól wyboru i zainicjuj formularz.
  2. To, co valueChangesobserwowalne, jest idealne, gdy chcesz, aby formularz wyświetlał coś, ale przechowywał coś innego w komponencie. Zamapuj wartości true/ falsena żądane wartości tutaj.
  3. Odfiltruj falsewartości w momencie przesyłania.
  4. Anuluj subskrypcję valueChangesobserwowalnych.

Przykład StackBlitz


Szczegółowe wyjaśnienie

Użyj FormArray, aby zdefiniować formularz

Jak już wspomniano w odpowiedzi oznaczonej jako poprawna. FormArrayjest dobrym rozwiązaniem w takich przypadkach, w których chciałbyś uzyskać dane w tablicy. Więc pierwszą rzeczą, którą musisz zrobić, jest utworzenie formularza.

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

Spowoduje to ustawienie początkowej wartości wszystkich pól wyboru na false.

Następnie musimy zarejestrować te zmienne formularza w szablonie i iterować po checkboxestablicy (NIE po FormArraydanych z pola wyboru), aby wyświetlić je w szablonie.

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

Wykorzystaj obserwowalną wartość valueChanges

Oto część, której nie widzę w żadnej udzielonej tutaj odpowiedzi. W sytuacjach takich jak ta, w których chcielibyśmy wyświetlić wspomniane dane, ale przechowywać je jako coś innego, valueChangesobserwowalne są bardzo pomocne. Korzystanie valueChangesmożemy obserwować zmiany w checkboxes, a następnie mapz true/ falsewartości otrzymanych od FormArraydo żądanych danych. Zwróć uwagę, że nie zmieni to wyboru pól wyboru, ponieważ każda prawdziwa wartość przekazana do pola wyboru oznaczy je jako zaznaczone i odwrotnie.

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

Zasadniczo mapuje FormArraywartości do oryginalnej checkboxestablicy i zwraca valuew przypadku, gdy pole wyboru jest oznaczone jako true, w przeciwnym razie zwraca false. Jest emitEvent: falseto ważne, ponieważ ustawienie FormArraywartości bez niego spowoduje valueChangeswyemitowanie zdarzenia tworzącego nieskończoną pętlę. Ustawiając emitEventna false, upewniamy się, że valueChangesobserwowalne nie wyemitują, gdy ustawimy tutaj wartość.

Odfiltruj fałszywe wartości

Nie możemy bezpośrednio filtrować falsewartości w FormArraypliku, ponieważ spowoduje to zepsucie szablonu, ponieważ są one powiązane z polami wyboru. Dlatego najlepszym możliwym rozwiązaniem jest odfiltrowanie falsewartości podczas przesyłania. W tym celu użyj operatora spreadu.

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
}

To w zasadzie odfiltrowuje fałszywe wartości z checkboxes.

Anuluj subskrypcję valueChanges

Na koniec nie zapomnij zrezygnować z subskrypcji valueChanges

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

Uwaga: Istnieje specjalny przypadek, w którym wartość nie może być ustawiona na FormArrayin valueChanges, tj. Jeśli wartość pola wyboru jest ustawiona na liczbę 0. Spowoduje to, że pole wyboru nie może być zaznaczone, ponieważ zaznaczenie pola wyboru ustawi FormControljako liczbę 0(wartość fałszywą), a zatem pozostawi niezaznaczone. Byłoby lepiej, aby nie używać liczby 0jako wartości, ale jeśli jest to wymagane, musisz warunkowo ustawić 0jakąś prawdziwą wartość, powiedzmy ciąg '0'lub po prostu zwykły, truea następnie przy przesyłaniu przekonwertować ją z powrotem na liczbę 0.

Przykład StackBlitz

StackBlitz ma również kod, gdy chcesz przekazać domyślne wartości do pól wyboru, aby zostały oznaczone jako zaznaczone w interfejsie użytkownika.

nash11
źródło
To nadal wymaga utrzymywania dwóch tablic i utrzymywania ich synchronizacji. Wciąż nie jest tak czysty, jak mam nadzieję. Być może uda nam się sprawić, by kontrolki formularza przechowywały złożone wartości zamiast dwóch tablic.
MIWMIB
1
Wartość złożona nie zadziałała, ponieważ wartość pola wyboru musi mieć wartość true lub false. Więc to rozwiązanie nadal wygląda najlepiej.
MIWMIB
6

TL; DR

  1. Wolę używać FormGroup, aby wypełnić listę pól wyboru
  2. Napisz niestandardowy walidator, aby sprawdzić, czy wybrano co najmniej jedno pole wyboru
  3. Przykład roboczy https://stackblitz.com/edit/angular-validate-at-least-one-checkbox-was-selected

To również mnie czasami uderzało, więc wypróbowałem zarówno podejście FormArray, jak i FormGroup.

W większości przypadków lista checkboxów znajdowała się na serwerze i otrzymywałem ją przez API. Ale czasami będziesz mieć statyczny zestaw pól wyboru ze wstępnie zdefiniowaną wartością. W każdym przypadku użycia zostanie użyty odpowiedni FormArray lub FormGroup.

Zasadniczo FormArrayjest wariantem FormGroup. Kluczową różnicą jest to, że jego dane są serializowane jako tablica (w przeciwieństwie do serializacji jako obiektu w przypadku FormGroup). Może to być szczególnie przydatne, gdy nie wiesz, ile kontrolek będzie obecnych w grupie, takich jak formularze dynamiczne.

Ze względu na prostotę wyobraź sobie, że masz prostą formę produktu za pomocą

  • Jedno wymagane pole tekstowe nazwy produktu.
  • Lista kategorii do wyboru, wymagająca sprawdzenia co najmniej jednej. Załóżmy, że lista zostanie pobrana z serwera.

Najpierw skonfigurowałem formularz z tylko nazwą produktu formControl. To jest wymagane pole.

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

Ponieważ kategoria jest renderowana dynamicznie, będę musiał dodać te dane do formularza później, gdy dane będą gotowe.

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

Istnieją dwa podejścia do tworzenia listy kategorii.

1. Form Array

  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>

To buildCategoryFormGroupzwróci mi FormArray. Pobiera również listę wybranych wartości jako argument, więc jeśli chcesz ponownie użyć formularza do edycji danych, może to być pomocne. W celu stworzenia nowego formularza produktu nie ma jeszcze zastosowania.

Zauważyłem, że podczas próby uzyskania dostępu do wartości formArray. Będzie to wyglądało [false, true, true]. Aby uzyskać listę wybranych identyfikatorów, sprawdzenie z listy wymagało nieco więcej pracy, ale w oparciu o indeks tablicy. Nie brzmi to dla mnie dobrze, ale działa.

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

Dlatego wymyśliłem używanie FormGroupw tym celu

2. Grupa formularzy

Różnica w formGroup polega na tym, że przechowuje dane formularza jako obiekt, który wymagał klucza i kontrolki formularza. Dlatego dobrym pomysłem jest ustawienie klucza jako categoryId, a później możemy go odzyskać.

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>

Wartość grupy formularzy będzie wyglądać następująco:

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

Ale najczęściej chcemy uzyskać tylko listę categoryIds jako ["category2", "category3"]. Muszę też napisać, żeby wziąć te dane. Takie podejście podoba mi się bardziej w porównaniu z formArray, ponieważ faktycznie mógłbym wziąć wartość z samego formularza.

  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. Własny walidator, aby zaznaczyć przynajmniej jedno pole wyboru

Zrobiłem walidator, aby sprawdzał co najmniej X checkbox jest zaznaczony, domyślnie będzie sprawdzał tylko z jednym checkboxem.

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
źródło
4

Po kliknięciu utwórz zdarzenie, a następnie ręcznie zmień wartość true na nazwę tego pola wyboru, a następnie nazwa lub true oceni to samo i możesz uzyskać wszystkie wartości zamiast listy prawda / fałsz. Dawny:

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

Spowoduje to przechwycenie zdarzenia po tym, jak zostało już zmienione na true lub false przez Angular Forms, jeśli to prawda, zmieniam nazwę na nazwę tego, co reprezentuje pole wyboru, co w razie potrzeby zostanie również ocenione na true, jeśli jest sprawdzane pod kątem prawda / fałsz jako dobrze.

kanada11
źródło
To zaprowadziło mnie na właściwą ścieżkę. Skończyło się na tym, że zrobiłem this.customForm.patchValue ({[event.target.id]: event.target.checked});
Demodave
4

Jeśli chcesz użyć formularza reaktywnego Angular ( https://angular.io/guide/reactive-forms ).

Możesz użyć jednej kontrolki formularza, aby zarządzać wartością wyjściową grupy pól wyboru.

składnik

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

Zarządza modelem / stanem danych wejściowych listy kontrolnej. Ten model umożliwia mapowanie bieżącego stanu do dowolnego formatu wartości, jakiego potrzebujesz.

Model:

{
   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 Form Control

Ta kontrolka przechowuje wartość, którą chciałbyś zapisać jako np

wartość wyjściowa: "value_1,value_2"

Zobacz demo na https://stackblitz.com/edit/angular-multi-checklist

Robert Prib
źródło
Z łatwością najlepsze rozwiązanie dla mnie. Dziękuję bardzo.
Newclique
2

Moje rozwiązanie - rozwiązałem to dla Angular 5 z Material View
Połączenie odbywa się przez

formArrayName = "powiadomienie"

(change) = "updateChkbxArray (n.id, $ event.checked, 'powiadomienie')"

W ten sposób może działać dla wielu tablic pól wyboru w jednym formularzu. Po prostu ustaw nazwę tablicy controls do połączenia za każdym razem.

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>

Na koniec otrzymujesz formularz z tablicą oryginalnych identyfikatorów rekordów do zapisania / aktualizacji. Widok interfejsu użytkownika

Odpowiednia część pliku json formularza

Chętnie przedstawię uwagi dotyczące ulepszeń.

Cwi Gregory Kaidanov
źródło
0

CZĘŚĆ WZORU: -

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

CZĘŚĆ KONTROLERA: -

        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
źródło
1
Cześć @EchoLogic .. Proszę o informację w przypadku jakichkolwiek pytań
Abhishek Srivastava
1
To nie jest użycie ReactiveForms, ale zwykłe formularze, więc nie odpowiada na pytanie
Guillaume
0

Dodaj moje 5 centów) Model mojego pytania

{
   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
źródło