Unable to set default value for dropdown in Angular Material - html

I am using mat-select within Angular Material as below:
<mat-form-field class="full-width">
<mat-select (selectionChange)="dropdownSelectedItem($event)">
<mat-option *ngFor="let item of itemList" [value]="item">
{{item.name}}
</mat-option>
</mat-select>
</mat-form-field>
The above dropdown is coming up fine but the dropdown has no default value.
In order to add a default value, I have added value="{{dropdownSelectedName}}" as below:
<mat-form-field class="full-width">
<mat-select (selectionChange)="dropdownSelectedItem($event)" value="{{dropdownSelectedName}}">
<mat-option *ngFor="let item of itemList" [value]="item">
{{item.name}}
</mat-option>
</mat-select>
</mat-form-field>
The above change is still not populating the default dropdown value.
I have also tried doing [(value)]="dropdownSelectedName"
When I do console.log(this.dropdownSelectedName) in the component, I can see that it has a string value
mydefaultvalue
ngOnInit() {
this.subs.sink = this.myService.listAll()
.subscribe(r => {
this.AllList = r;
});
this.dropdownSelectedName = "mydefaultvalue";
}

To set a default value to mat-select, you have to bind compareWith property, which will compare the values and will set the default value.
Example:
In your HTML:
<mat-form-field class="full-width">
<mat-select (selectionChange)="dropdownSelectedItem($event)" [(ngModel)]="dropdownSelectedName" [compareWith]="compareFn">
<mat-option *ngFor="let item of itemList" [value]="item">
{{item.name}}
</mat-option>
</mat-select>
</mat-form-field>
In your TS:
compareFn(obj1: any, obj2: any) {
return obj1 && obj2 ? obj1.id === obj2.id : obj1 === obj2;
}

the value of your mat-option is a object reference to a item Object.
therefore the value that you set on mat-select must be the exact same item object reference.
this means that in your compoenent your code should
this.dropdownSelectedName = [Insert Object Reference Here]
the alternative approach is to change your mat-option to set
<mat-option *ngFor="let item of itemList" [value]="item.name">
{{item.name}}
<mat-option>
with this change your "myDefaultValue" would match the value of one of the options, instead of being an object reference
Update
Here is a link that shows a working version of setting the value of the select when using object references.

Related

Angular Material : Display empty option(None) in Mat-Select on initial page load when value is null

Need to display the empty (None) option in Material Select element when the item is null.
<mat-label>Status</mat-label>
<mat-select formControlName="statusId">
<mat-option>None</mat-option>
<mat-option *ngFor="let item of statuses" [value]="item.id">
{{ item.name }}
</mat-option>
</mat-select>
</mat-form-field>```
As seen in above snippet I need to display the "None" option selected by default when the page loads.
StackBlitz
So, you can add a empty value to the None option.
<mat-select [formControl]="foodControl" name="food">
<mat-option value="">None</mat-option>
<mat-option *ngFor="let food of foods" [value]="food.value">
{{food.viewValue}}
</mat-option>
</mat-select>
And, in the ts, you can initialize the control with empty value like this
foodControl = new FormControl('');
same thing can be done with ngModel as well. (included in the StackBlitz)

How to get value of mat-option selected?

I've been trying to find a way to pull the value of a selected mat-option with this html/ts pairing.
html
<mat-form-field appearance="outline" floatLabel="always">
<mat-label>TRA Type</mat-label>
<mat-select
placeholder="TRA Type"
id="traType"
name="traType"
data-qa="new-tra"
[(ngModel)]="formSelect"
required>
<mat-option id="Regular" name="Regular" value="Regular">Regular</mat-option>
<mat-option id="Unitary" name="Unitary" value="Unitary">Unitary</mat-option>
</mat-select>
<mat-hint>Required</mat-hint>
</mat-form-field>
ts
formSelect: string;
traType = <any>{};
When propping up the page locally, this.traType.value doesn't prop up the value of the selected mat-option. It comes up as undefined when the expected result would be either "Regular" or "Unitary."
this.traType.value doesn't show the selected value as you miss [formControl] Input binding in <mat-select> element.
Solution:
Add [formControl]="traType" in <mat-select> element.
<mat-select placeholder="TRA Type"
id="traType"
name="traType"
[formControl]="traType"
data-qa="new-tra"
required>
..
</mat-select>
Solution in StackBlitz
Note: You are recommended to remove [(ngModel)] binding if you use [formControl] as it has been deprecated in Angular v6.
Reference
Form field features (Sample in HTML)

How to display mat-select with a default value on load

Here is my markup:
<mat-form-field class="col-md-3" *ngIf="isShown">
<mat-select placeholder="Status" formControlName="batchStatus" [value]="selected">
<mat-option *ngFor="let Status of statusList"[value]="status.referenceDetailCode">
{{ status.referenceDetailValue }}
</mat-option>
</mat-select>
</mat-form-field>
ts code :
this.isShown = true;
this.selected = res.status;
With the above code, dropdown is not getting selected with the status that I'm setting in my typescript file.
It worked by doing this :
this.displayForm.controls.status.setValue(this.selected);

How to get index of selected option outside the select in Angular?

I have a simple <select> in Angular (with Material) as follows:
<mat-form-field>
<mat-label>Type</mat-label>
<mat-select placeholder="Type" formControlName="type" name="type" id="name">
<mat-option>None</mat-option>
<mat-option *ngFor="let t of types" [value]="t">
{{t}} <-- it is enum -->
</mat-option>
</mat-select>
</mat-form-field>
I would like to use index of selected type in the other part of code.
To be more precise: in *ngFor of another select. Therefore, I can't use documentById.
In addition, I don't want to install jQuery just for do this.
Is it possible?
As suggested by this Angular Material Documentation example, you can bind to [(value)] two ways:
<mat-form-field>
<mat-select [(value)]="selected">
<mat-option>None</mat-option>
<mat-option *ngFor="let t of types; let i = index" [value]="i">{{t}}</mat-option>
</mat-select>
</mat-form-field>
<p>You selected: {{selected}}</p>
selected will be a property on your class.
Here's a Working StackBlitz from the Angular Team for your reference.
You can simply set [(ngModel)] variable and use the variable to get your index
<mat-select placeholder="Type" [(ngModel)]="selected" formControlName="type" name="type" id="name">
<mat-option>None</mat-option>
<mat-option *ngFor="let t of types" [value]="t">
{{t}} <-- it is enum -->
</mat-option>
</mat-select>
and then in the component, use
this.index = this.types.findIndex(item => item === selected);
Per Sajeetharans answer, using ngModel together with reactive form is not recommended, and is also deprecated. Instead watch for the changes of the form control and find the index and store it in a variable for later use. Here's a sample:
myForm: FormGroup;
idx: number;
foods = ['Steak', 'pizza-1'];
constructor(private fb: FormBuilder) { }
ngOnInit() {
this.myForm = this.fb.group({
mySelect: ['']
});
this.myForm.get('mySelect').valueChanges.subscribe((value) => {
this.idx = this.foods.findIndex(val => val === value);
console.log(this.idx)
});
}

Angular - Building form based on user input

I'm building a web form that is supposed to be dynamic.
When a user selects an option from a list, the next form inputs are generated based on his input.
For instance:
<mat-form-field>
<mat-select placeholder="Type" [(ngModel)]="row.Type" (change)="TypeChosen(row.Type, row)">
<mat-option [value]="0">Treatment</mat-option>
<mat-option [value]="1">Travel</mat-option>
<mat-option [value]="2">Medication</mat-option>
<mat-option [value]="3">Equipment</mat-option>
</mat-select>
</mat-form-field>
If he selects Type 'Treatment', he gets another selection input with some options with a few other inputs, and if he selects a different Type, he gets different options and other inputs.
I understand that I need to dynamically generate HTML content, and maybe a dynamic component.
What is the best approach to do this in an easy way?
I'd suggest creating a component for each sub-form and then *ngIf them based on the selected option, like so:
<!-- component.html -->
<mat-form-field>
<mat-select placeholder="Type" [(ngModel)]="row.Type" (change)="onTypeChosen(row.Type, row)">
<mat-option [value]="0">Treatment</mat-option>
<mat-option [value]="1">Travel</mat-option>
<mat-option [value]="2">Medication</mat-option>
<mat-option [value]="3">Equipment</mat-option>
</mat-select>
</mat-form-field>
<my-treatment-component *ngIf="type === 0" [someInput]="'some value'"></my-treatment-component>
<my-travel-component *ngIf="type === 1" [somethingElse]="true"></my-travel-component>
<my-medication-component *ngIf="type === 2" (someOutput)="onMedicationOutput($event)"></my-medication-component>
<my-equipment-component *ngIf="type === 3"></my-equipment-component>
If you already have something holding your Type selection, you can bind that to the *ngIfs instead. If not, create a field in your controller class and hold the selected type in there.
// component.ts
public type: number | null = null;
public onTypeChosen(type: number, row): void {
this.type = type;
}
If your sub-forms have parts that are re-usable (or are basically the same, sans configuration), it's generally a good practice to extract the re-usable code into components in and of themselves and compose them together.
Hope this helps a little :-)
To Add the options dynamically, angular provide ( *ngFor ).
<mat-form-field>
<mat-select placeholder="Type" [(ngModel)]="row.Type" (change)="TypeChosen(row.Type, row)" *ngFor="let option of options; let i = index">
<mat-option (click)="updateOptions(option)" [value]="{{i}}">option.text</mat-option>
</mat-select>
</mat-form-field>
in your controller .ts
private options = [];
private initOptions(){
this.options = [
{text:'Treatment' , possibleOptionsRelates:[text:'possible1']},
{text:'Travel' , possibleOptionsRelates:[text:'possible12']},
{text:'Medication' , possibleOptionsRelates:[text:'possible13']}];
}
private updateOptions(option){
this.options.push(...option.possibleOptionsRelates);
}