Search code examples
angularangular-forms

FormArray without FormControls and just strings Angular 2


I have two string arrays on my backend that I have to fill with either a single string or multiple strings. However they are not key value pairs. I am trying to push a string into one of the arrays but an running into the fact that I do not and cannot specify a control: value pair.

my formArray looks like

collections: new FormArray([]),

my html to select the strings

              <md-select placeholder="Collection">
            <md-option (click)="addCollectionId('one')" value="Local">Local</md-option>
            <md-option (click)="addCollectionId('two')" value="Music">Music</md-option>
            <md-option (click)="addCollectionId('three')" value="Performing Arts">Performing Arts</md-option>
            <md-option (click)="addCollectionId('four')" value="Sports">Sports</md-option>
            <md-option (click)="addCollectionId('five')" value="Restaurants">Restaurants</md-option>
          </md-select>

and my logic to add the strings to the formArray looks like:

  addCollectionId(id: string) {
const control = <FormArray>this.createCardForm.controls['collections'];
control.push(id);

}

I am getting the error 'Argument of type 'string' is not assignable to parameter of type 'AbstractControl'.

Since I cannot push a control: value pair and only string/strings how can I push strings to the array while still staying in my overall form?

Any help/tips/suggestions would be much appreciated.


Solution

  • You can use Reactive Forms API to achieve this, also I recommend to use angular formBuilder:

        export class SelectOverviewExample {
          createCardForm: FormGroup;
      
          foods = [
            {value: 'steak-0', viewValue: 'Steak'},
            {value: 'pizza-1', viewValue: 'Pizza'},
            {value: 'tacos-2', viewValue: 'Tacos'}
          ];
      
          // inject form builder
          constructor(private fb: FormBuilder) {
            // add collections form array to your form
            this.createCardForm = this.fb.group({
              collections: this.fb.array([]),
            });
          }
      
          // function which pushed new value to collections array
          addCollectionId(val) {
            const collections = this.createCardForm.get('collections');
            // add only once
            if (!collections.value.includes(val)) {
              collections.push(this.fb.control(val));
            }
          }
        }
    

    this way all your selected values will be added to the form and will be available under createCardForm.value.collections array.

    here is HTML:

    <md-select placeholder="Favorite food">
      <md-option [disabled]="createCardForm.value.collections.includes(food.value)" 
                 *ngFor="let food of foods" 
                 [value]="food.value" 
                 (click)="addCollectionId(food.value)">
        {{ food.viewValue }}
      </md-option>
    </md-select>
    
    <pre>{{ createCardForm.value | json }}</pre>
    

    here is updated plunker forked from https://material.angular.io/components/select/overview

    UPDATE

    here is reactive form only solution, without call to addCollectionId() function. Add reactiveCollections field to the form group:

        constructor(private fb: FormBuilder) {
          this.createCardForm = this.fb.group({
            collections: this.fb.array([]),
            reactiveCollections: null
          });
        }
    

    Add form group and control names to the md-select:

    <form [formGroup]="createCardForm">
      <md-select placeholder="Favorite food" 
                 multiple="true" 
                 formControlName="reactiveCollections">
        <md-option *ngFor="let food of foods" [value]="food.value">
          {{ food.viewValue }}
        </md-option>
      </md-select>
    </form>
    

    Plunker is updated as well