I want to add search functionality in my mat multiple select dropdown.
I have done this thing in Jquery but I want to do same thing in Angular material.
Here is the design pic.
Here is the image
Try this example in stackblitz
stackblitz example with angular 8
in your html file
<h2>Multiple selection</h2>
<p>
<mat-form-field>
<mat-select [formControl]="bankMultiCtrl" placeholder="Banks" [multiple]="true">
<mat-select-search [formControl]="bankMultiFilterCtrl"></mat-select-search>
<mat-option *ngFor="let bank of filteredBanksMulti | async" [value]="bank">
{{bank.name}}
</mat-option>
</mat-select>
</mat-form-field>
</p>
<p>
Selected Banks:
</p>
<ul *ngFor="let bank of bankMultiCtrl?.value">
<li>{{bank.name}}</li>
</ul>
The problem is that the material select doesn't have the functionality for an input. Therefore you need to do a bit of css to make it look good.
<mat-select multiple>
<mat-form-field>
<input matInput placeholer="Search" (input)="filterOptione($event.target.value)" />
</mat-form-field>
<mat-option *ngFor="let option of options" [value]="option">
{{option}}
</mat-option>
</mat-select>
The filter function:
public filterOptions(filter: string): void {
this.options = this._unfilteredOptions.filter(x => x.toLowerCase().includes(filter.toLowerCase()));
}
Instead of using a mat-select, use a mat-autocomplete. https://material.angular.io/components/autocomplete/overview
You can make it work the same as a mat-select by populating it with all values when the input is blank, then filter the auto-complete options when the input value changes.
Again, the trick is to make it show all available options when the input is empty.
Hope this helps.
Edit:
I should add that I use a mat-chip-list (https://material.angular.io/components/chips/overview) for the multi-select part of the equation. When an option is selected, add it to the chip list, and you can add X icons to each chip to remove things from the list.
Granted, this is my personal flavor of a multi-select input, but I thought it might give you some ideas.
My approach for this solution is as follow:
I populated mat-option from an array of object with many parameters. Of course, I used only one or 2 parameters to show, but I want my user to search by any value of the object even the values of the parameters that are not shown in the mat-option, So, in the template I created an input field like the follow
<mat-form-field>
<mat-select multiple>
<input matInput placeholer="Search" (input)="search($event)" />
<mat-option *ngFor="let d of data" [value]="d">
{{d}}
</mat-option>
</mat-select>
</mat-form-field>
And in the .ts file:
AllData:any[]; //of course the type will be the type of your data.
Search(event: any, PropertyToSearch: string)
{
this.data = this.AllData.filter(x =>
{
for (let v of String(event.target.value).trim().split(" "))
{
if (Object.values(x).join(" ").toLowerCase().includes(v.toLowerCase()))
return true;
}
return false;
});
}
In this function, I creates an array of the words entered by the user, and joined all the values of the each object inside the data array and search each word inside that joined string. Why did I do that? Because there is a case when the user by mistake add 2 spaces between 2 words like this "word1 word2".
If I used simple solution like
this.AllData.filter(x => x.toLowerCase().includes(event.target.value.toLowerCase()));
and the user write the words with only one space between the words, the matched result will not shown. Also, my solution make the user use any value he remember to search with.
Related
Selected value in one dropdown should not appear in other two dropdowns in Angular material. How to remove the selected value in one dropdown from all other dropdowns? (I have dynamic dropdown which gets added each time a button in clicked)
I tried using this https://stackblitz.com/edit/angular-dqvvf5?file=src%2Fapp%2Fapp.component.html but the logic is not working for angular material.
I've taken your Stackblitz and managed to get it working using mat-select instead of the standard HTML select. Please see this Stackblitz for a working demo.
The main changes were in the HTML...
HTML
<div *ngFor="let field of fieldArray; index as idx" style="margin-bottom: 8px">
<mat-form-field appearance="fill">
<mat-label>Select Language</mat-label>
<mat-select #selectLang (selectionChange)="selected(selectLang.value,i)">
<ng-container *ngFor="let lang of languageList" >
<mat-option *ngIf="selectLang.value === lang.name || !isSelected(lang.name)" [value]="lang.name">
{{lang.name}}
</mat-option>
</ng-container>
</mat-select>
</mat-form-field>
<button (click)="deleteFieldValue(idx, selectLang.value)" style="margin-left: 8px">X</button>
</div>
<button (click)="addFieldValue()">Add Select</button>
The Typescript is pretty much the same as in your example, except I've modified the signature of the selected() function to read selected(value: string, idx: number) as you're passing 2 parameters into that from your HTML.
I spent a lot of time solving this problem and failed. I want to select to display the name and get the id. The problem is, as soon as a value is selected, the number (id) of that value is displayed. My code:
<mat-form-field class="example-full-width">
<input
matInput
placeholder="Search your product"
formControlName="id"
[matAutocomplete] = "auto">
<mat-autocomplete #auto="matAutocomplete" >
<mat-option *ngFor="let option of allProducts; let i = index" [value]="option.id" >
{{ option.name }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
I tried to create a function (onSelectionChange) but could not display the name and take the value as (id)
Well, you have to use the input function of auto-complete called displayWith.
you can find the solution to your problem in this link here
plus I 've made a stackblitz example for you to simplify it more.
Example
to describe the solution:
you have to add to your autocomplete component an input called displayWith
<mat-autocomplete #auto="matAutocomplete" [displayWith]="checkValue">
and then in your component you have to create the function checkValue
checkValue(value: any) {
// here you can get your id or whatever you want
console.log(value)
}
then you have to add to your mat option the value and give it your whole object as value
<mat-option *ngFor="let option of options" [value]="option" >
{{option.name}}
</mat-option>
Please check the stackblitz example for more details
<mat-select required formControlName="country" [(ngModel)]="defaultCountry" placeholder="Country">
<mat-option *ngFor="let key of Object.keys(countryList)" [value]="[key]">{{[key]}}</mat-option>
</mat-select>
I have the above code. The countries are displayed fine in the Material Select, but the default option "DefaultCountry" is not working. I believe this is due to me using Object.keys because the following code does work with the default option.
<mat-select required formControlName="country" [(ngModel)]="defaultCountry" placeholder="Country">
<mat-option *ngFor="let country of deleteLaterCountryList" [value]="country.countryCode">{{country.countryCode}}</mat-option>
</mat-select>
I want to know how I can make my first piece of code work using the default option. I don't want to use the second piece of code.
I really need more details here, but I can see that you are using Reactive Form. In that case, there is no need for ngModel. Let us assume that you are using a formGroup called 'yourForm'.
<form [formGroup]="yourForm">
.
.
<mat-select required formControlName="country" placeholder="Country">
<mat-option *ngFor="let key of Object.keys(countryList)" [value]="[key]">{{[key]}}</mat-option>
</mat-select>
.
.
<form>
On the component.ts, you can use patchValue to assign a value to country formControl, which will be displayed as a default option.
yourForm: FormGroup = this.formBuilder.group({
country: [null, Validators.required],
.
.
// other form controls
});
ngOnInit() {
this.yourForm.patchValue({
country: 'SG'
});
}
I need to set default value if there is one one option in the mat-select. otherwise leave black to select by user.
This is my code in HTML
<mat-form-field>
<mat-select (selectionChange)="CompartSelected($event, element, cmpart)" disableRipple [value]="element.Compart.Id" name="cmpart" >
<mat-option *ngIf="getAvailableCompartList(element.Compart.CompartType.Id).length > 1" value="">-- Please Select an Option --</mat-option>
<mat-option *ngFor="let cmpart of getAvailableCompartList(element.Compart.CompartType.Id)" title="{{Translate(cmpart.CompartNote)}}"
[value]="cmpart.Id">{{cmpart.CompartTitle}}
</mat-option>
according to the #Sean solution I have update the code. But it doesn't set the value to the mat-select.
I have attached some pic of those mat-select.
Edit: Sorry - misunderstood. You want the value selected when there is only one option (default behavior), otherwise leave blank when 2+ options are available. Add an additional mat-option outside of your ngFor only when the ngFor expression returns more than 1 item.
<mat-form-field>
<mat-select (selectionChange)="CompartSelected($event, element, cmpart)" disableRipple [value]="element.Compart.Id" name="cmpart" >
<mat-option *ngIf="getAvailableCompartList(element.Compart.CompartType.Id).length > 1" value="">-- Please Select an Option --</mat-option>
<mat-option *ngFor="let cmpart of getAvailableCompartList(element.Compart.CompartType.Id)" title="{{Translate(cmpart.CompartNote)}}"
[value]="cmpart.Id">{{cmpart.CompartTitle}}
</mat-option>
</mat-select>
</mat-form-field>
You can clean this up a little by placing the function call to getAvailableCompartList inside your component so you're not eval'ing the function twice as the template is rendered.
In case someone still looking for a hint.
Normally when your select vary the size, means it is coming from an observable.
So you could do this:
Typescript code:
//HERE IS THE FUNCTION THAT LOADS YOUR OPTIONS. IN THIS CASE 'val' IS MY API RETURN.
//IN CASE SIZE IS 1, PASS THE FIRST VALUE TO YOUR VARIABLE MODEL.
loadParameter(){
this.service.getParameters().subscribe((val: any) => {
this.parameters = val;
if (val.length == 1) {
this.selectedParameter = val[0].ParameterId;
}
});
}
----------------------------------------------------------------------------
Here the front code:
<mat-label>Example</mat-label>
<mat-select [(ngModel)]="selectedParameter">
<mat-option *ngFor="let parameter of parameters" [value]="parameter.ParameterId">{{parameter.ParameterValue}}</mat-option>
</mat-select>
------------------------------------------------------------------------
I have a simple form which operates on the logic of:
Select Box 1 (Yes/No)
Select Box 2 (Show if Yes)
Select Box 3 (Show if No)
Only the shown selection box should be set to required, the other is not required.
<mat-select placeholder='Show First Options' formControlName='b' [(ngModel)]="view">
<mat-option [value]="'first'">
First
</mat-option>
<mat-option [value]="'second'">
Second
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field *ngIf="view === 'first'">
<mat-select
placeholder='First Items'
formControlName='one'
[required]="view === 'first'">
<mat-option *ngFor="let item of items1" [value]="item">
{{item}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field *ngIf="view === 'second'">
<mat-select
placeholder='Second Items'
formControlName='two'
[required]="view === 'second'">
<mat-option *ngFor="let item of items2" [value]="item">
{{item}}
</mat-option>
</mat-select>
</mat-form-field>
This works fine for the first, default select box. However, when you change to the second box, the first box seems to keep its required validator.
I have written a demo to demonstrate this:
https://stackblitz.com/edit/angular-9eoffq
How can I ensure only the viewed select box is required?
Apologies for editing the demo provided as people were reviewing it which caused confusion, however I seemed to have solved it - the issue seemed to stem from the DOM re-rendering to remove the mat-form-field before removing the required tag. This meant that the form still had the required attribute.
The demo has been updated with the solution - I had to programatically remove the required validator and add it to the appropriate form control. Then I had to update the value and validity to clear the errors after the validators were changed.
this.form.valueChanges.subscribe(value => {
if(value.b === 'first') {
this.form.controls['one'].setValidators(Validators.required)
this.form.controls['two'].clearValidators()
} else {
this.form.controls['two'].setValidators(Validators.required)
this.form.controls['one'].clearValidators()
}
this.form.controls['one'].updateValueAndValidity({onlySelf:true})
this.form.controls['two'].updateValueAndValidity({onlySelf:true})
})
Instead of using ngModel you can instead make full use of your reactive form, and utilize the form controls. Also, by disabling and enabling fields they are either included/excluded from the form object, therefore also validations will not apply.
You could listen to valueChanges of form, but I like to avoid it since if you are having a large form, it will be fired excessively. But if you want to only listen to a specific control changes, you can also do that. I like a simple change event here though:
<mat-select formControlName='b' (change)="onChange()">
and then hiding/showing the other select:
<mat-form-field *ngIf="form.controls.b.value === 'first'">
<!-- -->
<mat-form-field *ngIf="form.controls.b.value === 'second'">
and the change event:
onChange() {
if(this.form.get('b').value === 'first') {
this.form.get('one').enable()
this.form.get('two').disable()
} else {
this.form.get('one').disable()
this.form.get('two').enable()
}
}
DEMO
With this, we need to remember that the disabled control(s) are excluded from the form object, if you want all values (disabled included), you need to call this.form.getRawValue()
Still some one is looking for another solution, this may help.
<mat-select placeholder='Show First Options' formControlName='b' [(ngModel)]="view" #optionsSelector>
<mat-option value="first"> First </mat-option>
<mat-option value="second"> Second </mat-option>
</mat-select>
</mat-form-field>
<mat-form-field *ngIf="optionsSelector.value === 'first'">
<mat-select placeholder='First Items' formControlName='one'>
<mat-option *ngFor="let item of items1" [value]="item">
{{item}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field *ngIf="optionsSelector.value === 'second'">
<mat-select placeholder='Second Items' formControlName='two'>
<mat-option *ngFor="let item of items2" [value]="item">
{{item}}
</mat-option>
</mat-select>
</mat-form-field>