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.
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
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 FormControl
s or FormGroup
s 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With