FormArray without FormControls and just strings Angular 2

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


The purpose of a FormArray is to contain a set of FormControls or FormGroups so that you can dynamically add input elements to a HTML form. I don't think that is what you are intending to do and that a normal array may suit your purpose.

This is from the docs:

Tracks the value and validity state of an array of FormControl, FormGroup or FormArray instances. A FormArray aggregates the values of each child FormControl into an array. It calculates its status by reducing the statuses of its children.

For example, if one of the controls in a FormArray is invalid, the entire array becomes invalid. FormArray is one of the three fundamental building blocks used to define forms in Angular, along with FormControl and FormGroup.

I assume you already have a data model that holds all of your data. Here is mine for example:

/* Defines the product entity */
export interface IProduct {
    id: number;
    productName: string;
    productCode: string;
    tags?: string[];
    releaseDate: string;
    price: number;
    description: string;
    starRating: number;
    imageUrl: string;
}

Notice that it has an array of tags.

Your addCollectionId method could then update your data model directly.

Before saving, you can do something like this:

let p = Object.assign({}, this.product, this.productForm.value);

It creates a new object, assigns it to the values from my product instance (which would have my tag array properties set) and then overwrites any properties from my form. So it basically updates my local product model properties with any form properties.

Then the save code would save p in this example.

Make sense?


export class SelectOverviewExample {
  createCardForm: FormGroup;

  // inject form builder
  constructor(private fb: FormBuilder) {
    // add collections form array to your form
    this.createCardForm = this.fb.group({
      collections: this.fb.array([]),
    });
  }

  // get method which return the formArray as control
  get collections(): FormArray {
    return this.createCardForm.get('collections') as FormArray;
  };

  // function which pushed new value to collections array
  addCollectionId(val) {
    // Instead of this use get property
    //const collections = this.createCardForm.get('collections');

    // add only once
    if (!this.collections.value.includes(val)) {
      this.collections.push(this.fb.control(val));
    }
  }
}

I also got "Property 'push' does not exist on type 'AbstractControl'" error and I found the solution for this, only by removing const and added get method for collection.