Clear mat-select selection Angular material - html

I have implemented mat-multi-select option. I have a search functionality and I am able to select multiple options at once. Post selection when I click generate button, I want to clear all the selections. I am able to clear the values from search bar but values are still selected if I open the multi select dropdown. How can I clear those values.
HTML Code
<mat-select [formControl]="cMultiCtrl" [multiple]="true" required (selectionChange)="selectionChange($event)">
<mat-option>
<ngx-mat-select-search placeholderLabel="Search" noEntriesFoundLabel="No Matching Value Found" [formControl]="cMultiFilterCtrl"></ngx-mat-select-search>
</mat-option>
<mat-option *ngFor="let val of filteredCMulti | async" [value]="val.value">
{{val.name}}
</mat-option>
</mat-select>
// the button on click of which I want to clear everything post API call
<button mat-raised-button (click)="generate()" class="download-button">Generate
</button>
TS Code
public filteredCMulti: ReplaySubject<any[]> = new ReplaySubject<any[]>(1);
public cMultiCtrl: FormControl = new FormControl();
ngOnInit() {
this.filteredCMulti.next(this.cvalues.slice());
this.cMultiFilterCtrl.valueChanges
.pipe(takeUntil(this._onDestroy))
.subscribe(() => {
this.filterBanksMulti();
});
}
ngAfterViewInit() {
this.setInitialValue();
}
ngOnDestroy() {
this._onDestroy.next();
this._onDestroy.complete();
}
private setInitialValue() {
this.filteredCMulti
.pipe(take(1), takeUntil(this._onDestroy))
.subscribe(() => {
this.singleSelect.compareWith = (a, b) => a.id === b.id;
});
}
selectionChange(event){
this.cvalue = event.value;
}
private filterBanksMulti() {
if (!this.cvalues) {
return;
}
let search = this.cMultiFilterCtrl.value;
if (!search) {
this.filteredCMulti.next(this.cvalues.slice());
return;
} else {
search = search.toLowerCase();
}
// filter the banks
this.filteredCMulti.next(
this.cvalues.filter(bank => bank.name.toLowerCase().indexOf(search) > -1)
);
}
generate(){
let msg = '';
// some logic here
else{
this.commonService.showSnackBar("Value generated")
this.filteredCMulti = new ReplaySubject; // this clears the search bar but not values, they are still selected
this.table();
}}
})
}

Provide an element ref to your <mat-select>
<mat-select #matRef [formControl]="cMultiCtrl" [multiple]="true" required (selectionChange)="selectionChange($event)">
<mat-option>
<ngx-mat-select-search placeholderLabel="Search" noEntriesFoundLabel="No Matching Value Found" [formControl]="cMultiFilterCtrl"></ngx-mat-select-search>
</mat-option>
<mat-option *ngFor="let val of filteredCMulti | async" [value]="val.value">
{{val.name}}
</mat-option>
</mat-select>
And update your ts file like:
#ViewChild('matRef') matRef: MatSelect;
clear() {
this.matRef.options.forEach((data: MatOption) => data.deselect());
}
You can optimize clear() by conditionally calling deselect()

Related

How to select mat checkbox, mat select based on the data

I have a set of mat checkboxes and mat selects that gets populated based on values from a config file. Once these components are created on the page they need to get selected based on the data associated with the user displayed.I have written functions to make them selected based on the data, but don't see them working as expected. Here is my code:
html:
<table class="table">
<tr class="node-width" *ngFor="let element of configArray">
<mat-checkbox color="primary" name="element.type" [checked]="isCheckboxChecked(element.type)"
(change)="getCheckBoxvalue($event, element)">{{element.type}}
</mat-checkbox>
<mat-select placeholder="Select Sub Type" name="subtype" [formControl]="subtypes"
(selectionChange)="getSelectValue($event.value, element)" multiple>
<mat-option *ngFor="let subtype of element.subtypes" [value]="subtype"
[selected]="isOptionSelected(element.type, subtype)">
{{subtype}}
</mat-option>
</mat-select>
</tr>
</table>
typescript:
isCheckboxChecked(elementtype: string): boolean {
this.getTypesForTheCurrentApplication.forEach((item) => {
if (item.element === elementtype) {
this.checked = true;
} else {
this.checked = false;
}
});
this.changeDetector.markForCheck();
return this.checked;
}
isOptionSelected(elementtype: string, subtype: string): boolean {
this.getTypesForTheCurrentApplication.forEach((item) => {
if (item.element === elementtype) {
item.subtypes.forEach((element) => {
if (element === subtype) {
this.selected = true;
} else {
this.selected = false;
}
});
}
});
this.changeDetector.markForCheck();
return this.selected;
}
The function named 'getTypesForTheCurrentApplication' is called to query the back end db to get the json array values for the data displayed. Any help is greatly appreciated.
Thanks.

How to implement Angular Drop-down with Search

How to do a drop-down with search. I don't know how to implement the search here in my drop down. hoping someone can help me. I can do the dropdown with search if the data I'm getting is static.
here in my code, the data I'm supplying in my drop is dynamic supplier: SupplierDTO[]; This (supplier) is my created model wherein I'm fetching the fields I need.
.html
<mat-form-field class="box" appearance="fill">
<mat-label>Supplier</mat-label>
<mat-select formControlName="supplierId" (selectionChange)="selectedSupplier($event)">
<mat-option *ngFor="let items of supplier" [value]="items.id">
{{items.companyName}}
</mat-option>
</mat-select>
</mat-form-field>
.ts
export class DeliveryReceiptComponent implements OnInit {
supplier: SupplierDTO[];
}
ngOnInit(): void {
this.selectedSupplier;
}
selectedSupplier(trigger: MatSelectChange)
{
this.selectedSupplierID = trigger.value;
this.supplierSvc.getSupplierDR(this.selectedSupplierID).subscribe((response: any) => {
var splitted = response.split(":", 4)
this.deliveryReceiptForm.get('Bank').patchValue(splitted[0]);
this.deliveryReceiptForm.get('AccountName').patchValue(splitted[1]);
this.deliveryReceiptForm.get('AccountNumber').patchValue(splitted[2]);
this.deliveryReceiptForm.get('Branch').patchValue(splitted[3]);
if(this.deliveryReceiptForm.get('AccountName').value === this.dbmRequired ){
this.APRDisplayed = true;
this.deliveryReceiptForm.get('purchaseRequestNumber').addValidators(Validators.required);
this.deliveryReceiptForm.get('purchaseRequestDate').addValidators(Validators.required);
this.deliveryReceiptForm.get('drNumFrSupplier').addValidators(Validators.required);
this.deliveryReceiptForm.get('drDate').addValidators(Validators.required);
this.deliveryReceiptForm.get('aprNum').addValidators(Validators.required);
this.deliveryReceiptForm.get('aprDate').addValidators(Validators.required);
this.deliveryReceiptForm.get('purchaseOrderDate').setValue('0001-01-01');
this.deliveryReceiptForm.get('saleInvoiceDate').setValue('0001-01-01');
this.deliveryReceiptForm.get('aprDate').reset();
}
else{
this.APRDisplayed = false;
this.deliveryReceiptForm.get('purchaseOrderNumber').addValidators(Validators.required);
this.deliveryReceiptForm.get('purchaseOrderDate').addValidators(Validators.required);
this.deliveryReceiptForm.get('purchaseRequestNumber').addValidators(Validators.required);
this.deliveryReceiptForm.get('purchaseRequestDate').addValidators(Validators.required);
this.deliveryReceiptForm.get('saleInvoiceDate').addValidators(Validators.required);
this.deliveryReceiptForm.get('saleInvoiceNumber').addValidators(Validators.required);
this.deliveryReceiptForm.get('drNumFrSupplier').addValidators(Validators.required);
this.deliveryReceiptForm.get('drDate').addValidators(Validators.required);
this.deliveryReceiptForm.get('aprDate').setValue('0001-01-01');
}
})
}
It seems like your response is not an array, but an object instead. Therefore you should map the following:
getData(){
return this.http.get('https://localhost:44350/api/EamisDeliveryReceipt/list')
.pipe(
map(response => response.items.map(suppliers => suppliers.supplier.companyName)
)
)
}
Hint: You should probably define an interface for the type returned by this API. This way it's way easier to perform any mapping on the data.

Load data in a td of the same row where an option is marked

To make a demo of the problem I have a table where I show a list of details and several mat-select that when selecting an option the "size" of this marked option is loaded in the td.
The problem is that it does not load the "size" in the same row where an option is selected. How I can get this? The size is shown in the td but in order from top to bottom.
For example if there is no option marked and I select an option from row 3. The "size" of this option would be loaded in the first row and if I mark an option in any other row the data is loaded following the order of the rows.
I have the demo in stackblitz: DemoStackblitz
HTML
<table class="table">
<tr>
<th>Titles</th>
<th>Size</th>
<th>Detail</th>
<th>Duration</th>
</tr>
<tr *ngFor="let row of dataDetails let i = index">
<td>
<mat-form-field appearance="outline">
<mat-select [formControl]="dataCtrl" placeholder="Select" [compareWith]="compareItems" #singleSelect>
<mat-option>
<ngx-mat-select-search [formControl]="dataFilterCtrl"></ngx-mat-select-search>
</mat-option>
<mat-option *ngFor="let op of filteredData | async" [value]="op.id" (click)="onSelect(op)"
[disabled]="op.condition===1">
{{op.title}}
</mat-option>
</mat-select>
</mat-form-field>
</td>
<td>{{OptionSelected[i]?.size}}</td>
<td>{{row.detail}}</td>
<td>{{row.duration}}</td>
</tr>
</table>
TS
dataTitles: any = DataTitles;
dataDetails: any = DataDetails;
dataCtrl: FormControl = new FormControl();
dataFilterCtrl: FormControl = new FormControl();
filteredData: ReplaySubject<any> = new ReplaySubject<any>(1);
#ViewChild('singleSelect', { static: true }) singleSelect: MatSelect;
_onDestroy = new Subject<void>();
ngOnInit() {
this.dataCtrl.setValue(this.dataDetails[0]);
this.filteredData.next(this.dataTitles.slice());
this.dataFilterCtrl.valueChanges
.pipe(takeUntil(this._onDestroy))
.subscribe(() => {
this.filterData();
});
}
compareItems(i1: any, i2: any) {
return i1 && i2 && i1.key === i2.key;
}
ngOnDestroy() {
this._onDestroy.next();
this._onDestroy.complete();
}
OptionSelected: any = [];
onSelect(option: any) {
var objIndex = this.dataTitles.findIndex(x => x.id == option.id);
if (this.dataTitles[objIndex].title !== 'None') {
this.dataTitles[objIndex].condition = 1;
}
if (this.OptionSelected.length === 0) {
this.OptionSelected.push({ id: option.id, size: option.size });
} else {
let check = this.OptionSelected.map((item: any) => item.id).includes(
option.id
);
if (check === false) {
this.OptionSelected.push({ id: option.id, size: option.size });
}
}
console.log(this.OptionSelected);
}
filterData() {
if (!this.dataTitles) {
return;
}
let search = this.dataFilterCtrl.value;
if (!search) {
this.filteredData.next(this.dataTitles.slice());
return;
} else {
search = search.toLowerCase();
}
this.filteredData.next(
this.dataTitles.filter(
(x: any) => x.title.toLowerCase().indexOf(search) > -1
)
);
}
Issue
You´r pushing the current selected value into the OptionSelected during the call of
onSelect. This causes the mismatch of selected row data and which row actualy shows the data.
Pseudo code example:
Select TitleA in Row 4
Push new Data into OptionSelected
OptionSelected[0]?.size displays data in the first row
Solution
You are already tracking the value of the current selected value with the template variable #singleSelect over here:
<mat-select ... #singleSelect>
The <mat-select> component has a value input.
#Input()
value: any Value of the select control.
So you can display the current value by simply using the template ref:
<td>{{singleSelect.value}}</td>
Stackblitz example

Angular 6 show dropdown dependent selected option

I have two dropdown select option, but I'm struggling update second's selected option based on first one.
HTML
<form novalidate [formGroup]="editModuleForm" (ngSubmit)="onSubmitForm()">
<div align="center">
<mat-form-field>
<mat-select placeholder="Select Module" formControlName="moduleControl" required>
<mat-option *ngFor="let module of modules" [value]="module" (click)="popData()">
{{ module.title }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div *ngIf="editModuleForm.get('moduleControl').value" align="center">
<div align="center">
<mat-form-field>
<mat-select placeholder="Select Course" formControlName="courseControl">
<mat-option *ngFor="let course of courses" [value]="course.courseId" >
{{ course.name }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
Component
constructor(private putService: PutService, private getService: GetService,
private router: Router, private formBuilder: FormBuilder) {}
ngOnInit() {
this.getService.findAllModule().subscribe(modules => {
this.modules = modules;
});
this.getService.findAllCourses().subscribe(courses => {
this.courses = courses;
});
this.editModuleForm = this.formBuilder.group({
moduleControl: this.formBuilder.control(null),
courseControl: this.formBuilder.control(null)
});}
popData() {
this.editModuleForm.get('moduleControl').valueChanges.subscribe(
value => {
this.editModuleForm.controls['courseControl'].setValue(value.course);
console.log(value);
}
);}
After selecting an item in the first dropdown, I would like to default select module's course in the second dropdown and refresh after selecting another one.
You need subscribe changes on ngOnInit() no need to call click event
here's an example
ngOnInit() {
this.editModuleForm = this.formBuilder.group({
moduleControl: '',
courseControl: ''
});
this.popData();
}
popData() {
this.editModuleForm.controls['moduleControl'].valueChanges.subscribe(
value => {
this.editModuleForm.controls['courseControl'].setValue(value.id);
}
);
}
Stackblitz demo
I think the problem is you are calling this.editModuleForm.get('moduleControl').valueChanges in the click event, you actually want to call that in the ngOnInit. Its starting to listen to the value changes are the value has already changed!
Maybe try this:
constructor(private putService: PutService, private getService: GetService,
private router: Router, private formBuilder: FormBuilder) {}
ngOnInit() {
this.getService.findAllModule().subscribe(modules => {
this.modules = modules;
});
this.getService.findAllCourses().subscribe(courses => {
this.courses = courses;
});
this.editModuleForm = this.formBuilder.group({
moduleControl: this.formBuilder.control(null),
courseControl: this.formBuilder.control(null)
});
this.editModuleForm.get('moduleControl').valueChanges.subscribe(
value => {
this.editModuleForm.controls['courseControl'].setValue(value.course);
console.log(value);
}
}
You can get rid of the click event and the pop() method

Binding with ngModel angular dialog

I have a project that I can edit with dialog angular, but the problem is when I open the edit dialog what is changed automatically shows me in my UI I want to change after I save because if I change and click cancel that stays changed. Below you can find a code when i inject data and save.
What I change in dialog immediately overwrites changes, I wan'T after I save these will change.
Here is the dialog when it will open for edit.
openprojecteditdialog(project) {
this.dialog.open(ProjectEditDialogComponent, {
data: project, disableClose: true});
}
This is the template of edit dialog:
<mat-dialog-content>
<mat-tab-group>
<mat-tab label="Project">
<div id="general-content">
<mat-input-container>
<label>*Name</label>
<input placeholder="" matInput [(ngModel)]="project.name">
</mat-input-container>
<br>
<mat-input-container>
<label>*Type</label>
<mat-select class="tab-content-item" placeholder="" matInput
[(ngModel)]="project.type">
<mat-option *ngFor="let type of projectsType; let i = index"
[value]="i">
{{type}}
</mat-option>
</mat-select>
</mat-input-container>
<br>
<mat-input-container>
<label>*State</label>
<mat-select class="tab-content-item" placeholder="" matInput
[(ngModel)]="project.state">
<mat-option *ngFor="let state of projectsState; let i
=index" [value]="i">
{{state}}
</mat-option>
</mat-select>
</mat-input-container>
</div>
</mat-tab>
</mat-tab-group>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button mat-dialog-close (click)="save()"
[disabled]="project.name.length === 0">Save</button>
<button mat-button mat-dialog-close>Cancel</button>
</mat-dialog-actions>
This is the TS file of edit dialog.
export class ProjectEditDialogComponent implements OnInit {
readonly projectsState = ProjectState;
readonly projectsType = ProjectType;
readonly level: string[] = [];
working = false;
newType = '';
newState = '';
constructor(
public store: Store<ApplicationState>,
public dialogRef: MatDialogRef<ProjectEditDialogComponent>[],
#Inject(MAT_DIALOG_DATA) public project: any) {
}
ngOnInit() {
}
save() {
if (this.project.name.length > 0) {
this.working = true;
this.project.ProjectType = this.newType;
this.project.ProjectState = this.newState;
this.store.dispatch(new UpsertProjectInternalAction(this.project));
}
}
}
You are passing the reference of original project in edit dialog. So it will reflect the changes even if you don't save. Create the copy of project data so that it will not reflect with original project . And after save update the field which you want of original project.
openprojecteditdialog(project) {
let editProject = Object.assign({}, project);
this.dialog.open(ProjectEditDialogComponent, {
data: editProject, disableClose: true});
}
and save function will be
save() {
if (this.editProject.name.length > 0) {
this.working = true;
this.project.ProjectType = this.editProject.newType;
this.project.ProjectState = this.editProject.newState;
this.store.dispatch(new UpsertProjectInternalAction(this.project));
}
}