Reputation: 258
I am attempting to populate a select form field from an HTTP call. I get the data but cannot seem to set the value correctly utilizing a FormArray.
I have tried adjusting my object calls.references and even utilized array notation. I can get it to dump to the console, but not populate the select form field. So I am thinking that something is incorrect in my Angular code, but I have been unsuccessful in finding an example of this online. All of the documentation/tutorials I have come across do not have selects in the form.
public ngOnInit () {
// Reactive form fields
this.timecardForm = this.fb.group( {
payBeginningDate: [ '2019-03-19', [ Validators.required ] ],
payEndingDate: [ '2019-03-26', [ Validators.required ] ],
payCategoriesTracked: this.fb.array( [ this.buildPayCategoriesTracked() ] ), // This is the one on which I am working
overtimeCategories: '1',
workHoursTracked: this.fb.array( [ this.buildWorkTracked() ] ),
earnings: [ { value: null, disabled: true }, [ Validators.required ] ],
totalHours: [ { value: null, disabled: true }, [ Validators.required ] ],
totalEarnings: [ { value: null, disabled: true }, [ Validators.required ] ]
} );
... // There is some more unrelated code
// Dynamically build payCategories row
buildPayCategoriesTracked (): FormGroup {
console.log( 'buildPayCategoriesTracked: ', this.timeEntry.payCategories ); // This prints to the console successfully
return this.fb.group( {
payCategories: [ this.timeEntry.payCategories, [ Validators.required ] ]
} );
}
<!-- The HTML in question... -->
<select formControlName="{{i}}"
id="{{ 'payCategoriesTracked' + i }}"
class="form-control"
(change)="onRecordUpdated(timeCard, timecardDet)"
[ngClass]="{'is-invalid': payCategoriesMessage }">
<option value=null
disabled
selected
hidden>--Select--</option>
<option *ngFor="let payCategory of payCategories"
[ngValue]="payCategoryDescription.payCategoryId">{{payCategoryDescription}}</option>
</select>
I simply want my payCategories to populate my select form field.An example of one of the items returned is:
{payCategoryId: 9, description: "DUE FROM LIBRAR", payType: "Hourly", isActive: true}
So I want the select value to be the id and the description to display in the options tag.
Update I have changed my HTML as follows...
<div *ngFor="let payCategoriesTracked of payCategoriesTracked.controls;let i=index">
<mat-form-field>
<mat-label>Pay Category</mat-label>
<mat-select formControlName="i"
(change)="onRecordUpdated(timeCard, timecardDet)"
[ngClass]="{'is-invalid': payCategoriesMessage }">
<mat-option *ngFor="let payCategory of payCategories"
[ngValue]="payCategory.value">
{{payCategory.description}}
</mat-option>
</mat-select>
</mat-form-field>
Solution I figured it out...woot! Here is my solution...
<mat-form-field>
<mat-label>Pay Category</mat-label>
<select matNativeControl
formControlName="payCategory">
<option *ngFor="let payCategory of payCategories"
[ngValue]="payCategory">
{{payCategory.description}}
</option>
</select>
</mat-form-field>
// This is the FormControl, which is within a formgroup...
payCategory: new FormControl( this.buildPayCategories() )
// Pull in payCategories for select list from service
buildPayCategories () {
this.payCategories = this.timeEntry.payCategories;
return this.payCategories;
}
Upvotes: 0
Views: 3931
Reputation: 57919
Clay, you need choose if you want a FormArray of FormGroup or a FormArray of FormControls.
A FormArray of FormGroups
myForm=new FormGroup({
myArray:new FormArray([
new FormGroup({
prop1:new FormControl('',Validators.required),
prop2:new FormControl('',Validators.required)
})
])
})
<!--your form--->
<form [formGroup]="myForm">
<!--a div with formArrayName--->
<div formArrayName="myArray">
<!--a div that iterate over "controls" and has [formGroupName]-->
<div *ngFor="let group of myForm.get('myArray').controls;
let i=index" [formGroupName]="i">
<!--your input using formControlName-->
<input formControlName="prop1">
<input formControlName="prop2"/>
</div>
</div>
</form>
{{myForm?.value|json}}
//Some like { "myArray": [ { "prop1": "", "prop2": "" } ] }
A FormArray of FormControls
myForm2 = new FormGroup({
myArray: new FormArray([
new FormControl('', Validators.required),
new FormControl('', Validators.required)
])
})
<!--your form--->
<form [formGroup]="myForm2">
<!--a div with formArrayName--->
<div formArrayName="myArray">
<!--a div that iterate over "controls"-->
<div *ngFor="let group of myForm2.get('myArray').controls;let i=index">
<!--a unique element using [formControlName]-->
<input [formControlName]="i">
</div>
</div>
</form>
{{myForm2?.value|json}}
//will be like { "myArray": [ "", "" ] }
NOTE: I use directy new FormGroup and new FormArray , not FormBuilder. This avoid the need of inject the FormBuilder and, if you want, change the "change detection", but in sintax it's not much the difference
NOTE2: there are more differents ways to refered to a control in an array but I think this are the more clerest.
TIPs for make a form
<form *ngIf="form" [formGroup]="myForm"></form>
Update About select. A select it's a input, only need give the formControlName to the "select". Well a select need an array of objects to show the options. If our array of object has two properties, "value" and "text" generally we use some like:
//If our arrayForm is an ArrayForm of FormGroups
<select formControlName="prop1">
<options *ngFor="let item of options" [value]="item.value">
{{item.text}}
</option>
</select>
//If our arrayForm is a Array of FormControls
<select [formControlName]="i">
<options *ngFor="let item of options" [value]="item.value">
{{item.text}}
</option>
</select>
First, take account there NO [selected] anywhere: it's not necesary. If we can selected a value we'll give value to the FormControl.
When we are using ReactiveForm, usually we subscribe to changeValue propertie of the control in spite of use (change), and we must be carefully: In options we can use [value] or [ngValue]. but the value can be a simple variable (a string or a number) or a complex object. If we are not using dropdop in cascate, the normal it's use only a simple variable.
Update I made changes and I think I am close because I see the data dumped to the screen via {{timecardForm?.value|json}}
I am receiving the control.registerOnChange is not a function error, which leads me to believe I have something referenced incorrectly. Here is my updated code...
<div class="col-2">
<div formArrayName="payCategoriesTracked">
<div *ngFor="let payCategory of payCategoriesTracked.controls; let i=index">
<mat-form-field>
<mat-label>Pay Category</mat-label>
<mat-select [formControlName]="i"
(change)="onRecordUpdated(timeCard, timecardDet)"
[ngClass]="{'is-invalid': payCategoriesMessage }">
<mat-option *ngFor="let payCategory of payCategories"
[ngValue]="payCategory">
{{payCategory.description}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
The data dump is as follows...
"payCategoriesTracked": [ { "payCategories": [ { "payCategoryId": 1, "description": "Converted Data ",...etc.
Upvotes: 0
Reputation: 8065
Use the 'this.timeEntry.payCategories' in the template to build the options, using ngFor
<option *ngFor="let payCategory of timeEntry.payCategories" [value]="payCategory.payCategoryId">{{ payCategory.description }}</option>
When building the Reactive Form, the first parameter in the FormControl should be the value from the select, in other words, the selected option. Example:
return this.fb.group( {
payCategories: [ 2, [ Validators.required ] ]
} );
Will match the option with value equals 2.
Upvotes: 1