Reputation:
Angular Material select dropdown API only emits a value. Both [(ngModel)]
and (selectionChange)
only emit single member , not the whole object data field.
For example eg, it only emits the food.value = 2, does not emit other class fields for a row list item, like foodName, foodDescription, foodWeight, etc, (need to emit all the corresponding members for a listId)
How do I emit the whole object for food, given an food.value ? Needs to work for Any Material Dropdown in the future created, not just specific cases. Does Material Angular have any specific options to allow this?
The answers below, only work for one particular example, trying to see larger picture.
https://material.angular.io/components/select/api
<mat-form-field>
<mat-label>Favorite food</mat-label>
<mat-select>
<mat-option *ngFor="let food of foods" [value]="food.value">
{{food.viewValue}}
</mat-option>
</mat-select>
</mat-form-field>
Class example, for 2, need foodCategory, Calories, and other fields, etc
export class SelectOverviewExample {
foods: Food[] = [
{value: '0', viewValue: 'Steak', foodCategory: 'Meat', Calories: 50},
{value: '1', viewValue: 'Pizza', foodCategory: 'Carbs', Calories: 100},
{value: '2', viewValue: 'Apple', foodCategory: 'Fruit', Calories: 25}
];
}
Does anyone have a better solution?
Otherwise, will to write this in the selectionChange
onOutput method. Seems like Angular Materials would have better option,
this.foods.find(x=>x.value =="2")
Note for solutions below; Need to have solution for any different classes, with many type of members, etc
Upvotes: 0
Views: 5240
Reputation: 66
There is a solution without using indexes or searching through the collection.
The issue is that the select component doesn't know how to compare two objects.
The component API allows to set a compareWith
function to accomplish this.
Example:
My component gets a Set/Array of DTOs passed to it:
<app-user-select [(selectedUsers)]="dock.authorizedUsers"></app-user-select>
The template is a simple select, but with compareWith
set:
<mat-form-field appearance="fill">
<mat-label>Users</mat-label>
<mat-select [(value)]="selectedUsers" (valueChange)="selectedUsersChange.emit(selectedUsers)" [compareWith]="deepCompare" multiple>
<mat-option *ngFor="let user of availableUsers" [value]="user">{{user.name}}</mat-option>
</mat-select>
</mat-form-field>
In the controller I define what the object equality is in the deepCompare
function. Equality in my model is the equality of the two fields identitySourceId
and id
:
export class UserSelectComponent implements OnInit {
@Input()
selectedUsers: Set<UserDto> = new Set<UserDto>();
@Output()
selectedUsersChange = new EventEmitter<Set<UserDto>>();
availableUsers: Array<UserDto>;
constructor(private userApiService: UserApiService) { }
ngOnInit(): void {
this.getAvailableUsers();
}
private getAvailableUsers() {
this.userApiService.getUsers().subscribe({
next: users => this.availableUsers = users
})
}
public deepCompare(optionValue: UserDto, selectionValue: UserDto): boolean {
return optionValue.identitySourceId == selectionValue.identitySourceId && optionValue.id == selectionValue.id;
}
}
The compareWith
function can of course do a deep equality check too.
This code loads the collection properly into the selected values and also emits the full collection to the parent.
Upvotes: 0
Reputation: 29
Your onSelectionChange($event) function can emit any object, it is not tightly coupled with the data structure you want to display. I had a similar problem with a list of locations, I modified your solution a bit:
Template:
<form formGroup="foodForm">
<mat-form-field>
<mat-label>Favorite food</mat-label>
<mat-select [formControl]="foodControl">
<mat-option *ngFor="let food of foods" [value]="food.value">
{{food.viewValue}}
</mat-option>
</mat-select>
</mat-form-field>
</form>
Controller:
// Food list may come from anywhere, even from inside the controller, or from
an API
foods: Food[] = [
{value: '0', viewValue: 'Steak', foodCategory: 'Meat', Calories: 50},
{value: '1', viewValue: 'Pizza', foodCategory: 'Carbs', Calories: 100},
{value: '2', viewValue: 'Apple', foodCategory: 'Fruit', Calories: 25}
];
export class SelectOverviewExample {
@Output() foodSelectionChange = new EventEmitter<any>();
// if food comes from a parent component @Input() foods;
foodForm: FormGroup;
foodControl = new FormControl();
constructor(private fb: FormBuilder) {
this.foodForm = fb.group({
food: this.locationControl
});
}
onFoodSelectionChange(event): void {
const food = this.foods.find(element => element.value === event.value);
this.foodSelectionChange.emit(food);
}
}
You can set the default value of your formControl in the OnInit lifecycle:
ngOnInit() {
this.foodForm.controls['food'].setValue(/any value/);
}
Upvotes: 1
Reputation: 5181
Select
ComponentThe Select
component from the Angular Material library does emit the whole object. Anything you define in the value
in the mat-option
input directive will be emitted when you the select the correspondent value. This is the usual way how it behave.
As side note: Be careful with the used brackets []
as they define that the set value is an object and not a string.
Don't use it like that
<mat-option value="option3">Option 3</mat-option>
Instead
<mat-option [value]="someObject">Option 3</mat-option>
Set the object as value to the mat-option components
See how the value is set using the row
object of the data
array
<mat-form-field>
<mat-label>Select a food option</mat-label>
<mat-select (selectionChange)="handleSelect($event)">
<mat-option *ngFor="let row of data" [value]="row">{{ row.name }}</mat-option>
</mat-select>
</mat-form-field>
handleSelect(food: any) {
// Here is the actual selected object
console.log(food.value);
this.selected = JSON.stringify(food.value);
}
For your reference: https://stackblitz.com/angular/annvxmlqlqe
Upvotes: 1
Reputation: 161
Here is a selection that returns the object
import { Component } from "@angular/core";
// import { Observable } from 'rxjs/Observable';
@Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
selected = "";
data = [
{ value: "0", viewValue: "Steak", foodCategory: "Meat", Calories: 50 },
{ value: "1", viewValue: "Pizza", foodCategory: "Carbs", Calories: 100 },
{ value: "2", viewValue: "Apple", foodCategory: "Fruit", Calories: 25 }
];
}
<h2>Select Food</h2>
<mat-form-field>
<mat-select [(value)]="selected">
<mat-option value="">Select favorite food</mat-option>
<mat-option *ngFor="let food of data" [value]="food">
{{food.viewValue}}
</mat-option>
</mat-select>
</mat-form-field>
<div>
<p>Your Favorite food is</p>
<ul>
<li>Food Name: {{selected.viewValue}}</li>
<li>Calories: {{selected.Calories}}</li>
<li>Category: {{selected.foodCategory}}</li>
</ul>
</div>
Look it up at: https://stackblitz.com/edit/angular-h4oivc
Upvotes: 0
Reputation:
try this one
<mat-form-field>
<mat-label>Favorite food</mat-label>
<mat-select [(value)]="selected">
<mat-option *ngFor="let food of data; let i = index" [value]="i">
{{food.viewValue}}
</mat-option>
</mat-select>
</mat-form-field>
Selected value: {{data[selected].foodCategory}}
https://stackblitz.com/edit/angular-thvpvz
Upvotes: 0
Reputation: 1037
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, FormControl, Validators } from '@angular/forms';
import { Observable } from 'rxjs/Observable';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
formGroup: FormGroup;
titleAlert: string = 'This field is required';
selected;
data = [
{value: '0', viewValue: 'Steak', foodCategory: 'Meat', Calories: 50},
{value: '1', viewValue: 'Pizza', foodCategory: 'Carbs', Calories: 100},
{value: '2', viewValue: 'Apple', foodCategory: 'Fruit', Calories: 25}
];;
constructor(private formBuilder: FormBuilder) { }
ngOnInit() {
this.selected = 0
}
}
<h4>Basic mat-select</h4>
<mat-form-field>
<mat-label>Favorite food</mat-label>
<mat-select [(value)]="selected">
<mat-option *ngFor="let food of data; let i = index" [value]="i">
{{food.viewValue}}
</mat-option>
</mat-select>
</mat-form-field>
<div>
Selected value: {{data[selected].foodCategory}}
</div>
Upvotes: 0
Reputation: 542
<mat-form-field>
<mat-label>Favorite food</mat-label>
<mat-select>
<mat-option *ngFor="let food of foods; let i = index" [value]="i" (onSelectionChange)="onValueChange(food)">
{{food.viewValue}}
</mat-option>
</mat-select>
</mat-form-field>
Upvotes: 1