I'm trying to preselect multiple options in a mat-select. So far I'm not able to achieve this.
Here is the HTML file:
<mat-dialog-content [formGroup]="form">
<mat-form-field>
<mat-select placeholder="participants" formControlName="participants" multiple>
<mat-option *ngFor="let participant of participants" [value]="participant">{{participant}}</mat-option>
</mat-select>
</mat-form-field>
</mat-dialog-content>
Here is the controller:
export class EventViewModalComponent implements OnInit {
form: FormGroup;
calendarEvent: CalendarEvent;
participants = [];
constructor(private fb: FormBuilder,
private participantService: ParticipantService,
#Inject(MAT_DIALOG_DATA) event: CalendarEvent)
{
this.form = fb.group({
title: [event.title, Validators.required],
location: [event.meta.location, Validators.required],
description: [event.meta.description, Validators.required],
start: [event.start, Validators.required],
end: [event.end, Validators.required],
participants: [this.participants, Validators.required]
});
this.calendarEvent = event;
}
ngOnInit() {
this.participantService.getAll().subscribe(data =>{
for(let i = 0; i < data['length']; i++){
this.participants.push(data[i]['name']);
}
})
}
}
This currently selects all the values per default. Here's a screenshot of how it looks currently:
What I'm trying to achieve is having only Cindy and Jim preselected like this:
How can I achieve this? I've read through several SO questions and wasn't successful. Any help is appreciated
With Angular6+ there is a deprecation warning when you have both a formControl and [(ngModel)]
It looks like you're using ngModel on the same form field as
formControl.
Support for using the ngModel input property and ngModelChange event with
reactive form directives has been deprecated in Angular v6 and will be removed
in Angular v7.
Option 1 using [ngModel] without FormControl
As the first answer suggests, split [ngModel] and (ngModelChange) like this.
<mat-select
[(ngModel)]="selectedFoods"
(ngModelChange)="selectedFoods" name="selectedFoods"
placeholder="Favorite food" multiple>
<mat-option *ngFor="let food of allfoods" [value]="food.value">
{{food.viewValue}}
</mat-option>
</mat-select>
And for the ts.
allfoods: Food[] = [
{value: 'steak-0', viewValue: 'Steak'},
{value: 'pizza-1', viewValue: 'Pizza'},
{value: 'tacos-2', viewValue: 'Tacos'},
{value: 'pasta-3', viewValue: 'Pasta'}
];
selectedFoods = [
'steak-0', 'pasta-3'
];
https://stackblitz.com/edit/angular-mat-select-with-ngmodel?embed=1&file=app/select-overview-example.ts
Option 2 using [formControl] without [ngModel]
<mat-form-field>
<mat-select [formControl]="foodForm" placeholder="Favorite food" multiple>
<mat-option *ngFor="let food of allfoods" [value]="food.value">
{{food.viewValue}}
</mat-option>
</mat-select>
</mat-form-field>
In this case, the selection is initialised in the FormControl's constructor.
allfoods: Food[] = [
{value: 'steak-0', viewValue: 'Steak'},
{value: 'pizza-1', viewValue: 'Pizza'},
{value: 'tacos-2', viewValue: 'Tacos'},
{value: 'pasta-3', viewValue: 'Pasta'}
];
myselectedFoods = ['pasta-3', 'steak-0'];
foodForm: FormControl = new FormControl(this.myselectedFoods);
https://stackblitz.com/edit/angular-mat-select-multi-with-formcontrol
I believe that the easiest way is to use [(ngModel)] directive (or a one-way binding, I'll cover it shortly later) to manipulate the value of mat-select: you can bind a variable, which contains elements you want to preselect, something like this:
<mat-select placeholder="participants" formControlName="participants" multiple [(ngModel)]="selection">
<mat-option *ngFor="let participant of participants" [value]="participant">{{participant}}</mat-option>
</mat-select>
And then you can just filter your initial array of items and get only those, which you want to select by default (it should be stored in selection class property in provided case).
I have created a STACKBLITZ to demonstrate, that it's possible. In this example, I filter items at even indexes, and finally we get "1'st, 3'rd, 5'th, ..." items selected.
Notice, that you can split [(ngModel)] into 2 separate directives if you want to use only a one-way binding: [ngModel] or (ngModelChange). For more info, take a look at angular template syntax guide.
Related
I'm using Angular material to make a reactive form that look like this:
Form
With this code I make a dynamically generated mat select and an input, but I don't know how to get the value from all of these.
HTML:
<div *ngFor="let number of [].constructor(cantConsumibles)">
<mat-select placeholder="Selecciona un consumible" class="form-control"
formControlName="consumibles">
<mat-option *ngFor="let consumible of consumibles" [value]="consumible">
{{consumible.CodConsumible}}
</mat-option>
</mat-select>
<input matInput type="number" formControlName="consumibles" placeholder="Cantidad">
</div>
<div align="end">
<button mat-button (click)="agregarConsumible(true)"><mat-icon>add</mat-icon></button>
<button mat-button (click)="agregarConsumible(false)"><mat-icon>remove</mat-icon></button>
</div>
TS:
cantConsumibles: number = 0;
agregarConsumible(flag: boolean): void {
(flag) ? this.cantConsumibles++ : this.cantConsumibles--;
}
Any ideas? Thanks in advance
The select API documents say that we could register a change with #Output() selectionChange: EventEmitter<C> https://material.angular.io/components/select/api
So let's add that method on our select to know, what was selected.
<mat-select placeholder="Favorite food" (selectionChange)="onFoodSelection($event)">
<mat-option *ngFor="let food of foods" [value]="food.value">
{{ food.viewValue }}
</mat-option>
</mat-select>
We then console.log the event value but also save the event value to a variable for later use.
#Component({
selector: 'material-app',
templateUrl: 'app.component.html'
})
export class AppComponent {
output: any;
foods = [
{value: 'product10', viewValue: 'Steak'},
{value: 'product20', viewValue: 'Pizza'},
{value: 'product30', viewValue: 'Tacos'},
{value: 'product40', viewValue: 'Lasagne'},
];
onFoodSelection(event:any) {
this.output = event.value;
console.log(event.value)
}
}
Follow same principal on other elemens (select, input, checkbox etc.) getting the event on change in template and doing something with in in typescript code part.
Here is a small example for you: https://stackblitz.com/edit/material-select-change-event-binding-tta13p?file=app%2Fapp.component.ts
What might be wrong with this code? I can't see that valueChanges is getting fired.
ngOnInit() {
this.tagsSubscription = this.service.tags$.subscribe(...);
this.createForm();
this.service.getSupportedTags(new TagId('sometag'));
this.languageSelectorForm.get('tagsFilterCtrl').valueChanges
.pipe(takeUntil(this._onDestroy))
.subscribe(() => {
console.log("received value chnage from filter control ");// I don't see the print and the mat-select object's value is `object Object`
this.filterTags();
});
}
createForm() {
this.languageSelectorForm = this.fb.group({
tags: [null, Validators.required],
tagsFilterCtrl:[null] //filter input shows here
});
}
The filter control is being used as
<div id="mat-select-div">
<mat-select id="language-selector" placeholder="Tags" class="selectpicker" formControlName="tags" [ngClass]="validateField(languageSelectorForm,'tags')" #singleSelect>
<mat-option *ngIf="newSearch">Please select</mat-option>
<mat-option>
<ngx-mat-select-search formControlName="tagsFilterCtrl"></ngx-mat-select-search>
</mat-option>
<mat-option *ngFor="let tag of filteredTags | async" [value]="tag">{{tag.subject}}</mat-option>
</mat-select>
</div>
The problem was not with valuechanges. There was some other logic error. However, it is strange that I don't see the print in the valueChanges observable.
I am attempting to build a web application that has a dropdown menu of industries.
This is what my current list looks like:
<mat-select placeholder="Industry" [(ngModel)]="selectedIndustry" >
<mat-option [value]="0">None</mat-option>
<mat-option [value]="1">Aerospace</mat-option>
<mat-option [value]="2">Banking</mat-option>
<mat-option [value]="3">Chemicals</mat-option>
<mat-option [value]="4">Defence</mat-option>
<mat-option [value]="5">Education</mat-option>
<mat-option [value]="6">Finacne</mat-option>
<mat-option [value]="7">Gaming</mat-option>
<mat-option [value]="8">Government</mat-option>
<mat-option [value]="9">Healthcare</mat-option>
<mat-option [value]="10">Insurance</mat-option>
<mat-option [value]="11">Media</mat-option>
<mat-option [value]="12">Mining</mat-option>
<mat-option [value]="13">Resources</mat-option>
<mat-option [value]="14">Retail</mat-option>
<mat-option [value]="15">Telecommunications</mat-option>
<mat-option [value]="16">Utilities</mat-option>
</mat-select>
<button mat-button (click)="loadJSON()">Create</button>
I also have a typescript file of JSON objects that I would like to connect to this list. This is the contents of the typescript file:
export class Industry {
id: Number;
name: String;
industry = [
{id: 1, name: "Aerospace"},
{id: 2, name: "Banking"},
{id: 3, name: "CapitalMarkets"},
{id: 4, name: "Chemicals"},
{id: 5, name: "Defence"},
{id: 6, name: "Education"},
{id: 7, name: "Gaming"},
{id: 8, name: "Government"},
{id: 9, name: "Healthcare"},
{id: 10, name: "Insurance"},
{id: 11, name: "Media"},
{id: 12, name: "Mining"},
{id: 13, name: "Resources"},
{id: 14, name: "Retail"},
{id: 15, name: "Telecommunications"},
{id: 16, name: "Utilities"}
]
}
Currently the dropdown list functions but like expected but when I clike the 'create button' . nothing happens. I cant figure out how to bind each of the dropdown option to the corresponding JSON object. I have seen the use of this below, which I have tried but I cant get it to work for me:
<mat-option *ngFor="let i of Industry.industry" [value]=i>{{i.name}}</mat-option>
Ideally I need to be able to select an object (either linked to the list by value of name or something) and have the json load when create is clicked.
I'm just starting out so any help is appreciated. As . I have a beginner level knowledge could you please explain your answers. Thanks in advance!
Assuming that you have some data that you're going to get from a REST API, you can call the API in the loadJSON method:
import { Component } from "#angular/core";
import { Observable } from "rxjs";
import { DataService } from "./data.service";
/** #title Select with 2-way value binding */
#Component({
selector: "select-value-binding-example",
templateUrl: "select-value-binding-example.html",
styleUrls: ["select-value-binding-example.css"]
})
export class SelectValueBindingExample {
selectedIndustry;
industries = [
{ id: 1, name: "Aerospace" },
...
];
data$: Observable<Array<any>>;
constructor(private dataService: DataService) {}
loadJSON() {
this.data$ = this.dataService.getDataById(this.selectedIndustry);
}
}
I've created a DataService that has a getDataById method. This takes an id as an argument and then returns an Observable<Array<any>>. I've then assigned it to the data$ property on the Component class.
I can unwrap the data$ property in my Component Template using the async pipe. Something like this:
<mat-form-field>
<mat-label>Select an option</mat-label>
<mat-select placeholder="Industry" [(ngModel)]="selectedIndustry">
<mat-option disabled>None</mat-option>
<mat-option
*ngFor="let industry of industries"
[value]="industry.id">
{{ industry.name }}
</mat-option>
</mat-select>
</mat-form-field>
<button mat-button (click)="loadJSON()">Create</button>
<p>You selected: {{selected}}</p>
<pre *ngIf="(data$ | async) as data">
{{ data | json }}
</pre>
Also, instead of hard-coding every single item in the industries array, I can use the *ngFor structural directive to loop through the items in the array and render the mat-option
Hope this helps and clears your doubts.
Here's a Working Sample StackBlitz for your ref.
I two mat-select, in the first one I select the type of Customer Individual or Organizational Customer.
If User selects Ind Customer I show another mat-select.
However, the issue is in second mat-select the dropdown options I want to disable certain input fields. How can I achieve that?
HTML Code to select Type of Customer
<mat-form-field>
<mat-label>Select Customer Type</mat-label>
<mat-select (onSelectionChange)="getCustType($event)">
<mat-option *ngFor="let obj of custType" (click)="getCustType(obj)"
[value]="obj" > {{ obj.viewValue }}</mat-option>
</mat-select>
</mat-form-field>
Typescript code:
custType: any[] = [{ value: 'indCust', viewValue: 'IndividualCustomer' }, { value: 'orgCust', viewValue: 'Organizational Customer' }];
Second Dropdown HTML CODE :
<mat-form-field class="col-sm-3">
<mat-label>Select Option to Edit</mat-label>
<mat-select (onSelectionChange)="getoptiontoedit($event)" >
<mat-option *ngFor="let obj of optiontoeditlist" (click)="getoptiontoedit(obj)" [value]="obj"> {{ obj.viewValue }}</mat-option>
</mat-select>
</mat-form-field>
Typescript Code for Second Dropdown:
optiontoeditlist: any[] = [
{ value: 'address', viewValue: 'Address' },
{ value: 'agentRelationship', viewValue: 'Agent Relationship' },
{ value: 'agreementRelationship', viewValue: 'Agreement Relationship' },
{ value: 'organizationCustomer', viewValue: 'Organization Customer' },
{ value: 'complaint', viewValue: 'Complaint' },
{ value: 'contact', viewValue: 'Contact' },
{ value: 'identification', viewValue: 'Identification' },
{ value: 'individualCustomer', viewValue: 'Individual Customer'}
];
I want to disable/hide individualCustomer option from the second dropdown if a user selects Organization Customer in the first dropdown similarly I want to disable/hide OrganazationalCustomer from the second dropdown if a user selects Individual Customer in the first dropdown.
You can implement a pipe for your second options list like below
#Pipe({ name: 'optfilter' })
export class OptionsFilterPipe implements PipeTransform {
transform(alloptions: any[],firstSelectOpt: any<>) {
return alloptions.filter(option => option.value.indexOf(firstSelectOpt.value)==-1); // or whatever your comparator condition is this just for indication
}
}
Then you can use it like
<mat-option *ngFor="let obj of optiontoeditlist | optFilter: 'selectedFirstOption'"
I am trying to display a single object from an array based on a property value.
My list of transactionitems has an accountId property, but I would like to display the account name instead. All accounts are loaded in the accounts$ array. I just can't figure out how to properly use my getAccountById function
Here is the component class
export class NewtransactionComponent implements OnInit {
transaction$: Transaction;
tempItem$: TransactionItem;
accounts$: Array<Account>;
constructor(private data: DataService) { }
ngOnInit() {
this.transaction$ = new Transaction();
this.data.getAccounts().subscribe(data => this.accounts$ = Object.assign(new Array<Account>(), data));
this.tempItem$ = new TransactionItem();
this.transaction$.TransactionDate = new Date();
}
addItem(){
this.transaction$.TransactionItems.push(this.tempItem$);
this.tempItem$ = new TransactionItem();
}
getAccountById(id):Account{
return this.accounts$.find(x => x.id === id);
};
and here is the html view that gives the error "Cannot read property 'name' of undefined"
<div class="items-container">
<mat-form-field>
<input matInput placeholder="Amount" [(ngModel)]="tempItem$.Amount">
</mat-form-field>
<mat-form-field *ngIf="accounts$">
<mat-select placeholder="Account" [(ngModel)]="tempItem$.AccountId">
<mat-option *ngFor="let account of accounts$" [value]="account.id">{{account.name}}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-select placeholder="Credit/Debit" [(ngModel)]="tempItem$.CreditDebit">
<mat-option value="Credit">Credit</mat-option>
<mat-option value="Debit">Debit</mat-option>
</mat-select>
</mat-form-field>
<button mat-mini-fab color="primary" (click)="addItem()">Add</button>
</div>
<table *ngIf="transaction$.TransactionItems.length">
<tr>
<th>Amount</th>
<th>Account</th>
<th>Credit/Debit</th>
</tr>
<tr *ngFor="let item of transaction$.TransactionItems">
<th>{{item.Amount | currency}}</th>
<th>{{getAccountById(item.AccoundId).name}}</th>
<th>{{item.CreditDebit}}</th>
</tr>
</table>
these are the account and transactionitem data models for reference
export class Account {
Id: string;
Name: string;
Category: string;
SubCategory: string;
}
export class TransactionItem{
Id: number;
TransactionId:number;
Accountid: string;
Amount: number;
CreditDebit: string;
}
I assume that the error is here: {{account.name}}?
This is most likely a timing issue. The page will attempt to display before the data is retrieved from the subscription.
One way to resolve the issue is to use an *ngIf
<div class="items-container" *ngIf="account">
That way the page won't try to display until the data is retrieved.
Another option is to use the safe navigation operator:
{{account?.name}}
The question mark tells Angular not to attempt to read the name property if the account is null or undefined.
EDIT:
If there error is here: {{getAccountById(item.AccountId).name}}, then it is telling you that getAccountById(item.AccountId) is undefined or null. Is it possible that one of your transactions has no account?
And looking at your code more closely, JavaScript/TypeScript is case sensitive. So if you declare the id using:
Id: string;
(Upper case I)
You can't then access it using:
return this.accounts$.find(x => x.id === id);
(Lower case)