How to implement Angular Drop-down with Search - json

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.

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.

Clear mat-select selection Angular material

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()

how to hide items from switch statement in angular

I'm learning Angular and wondering how I can't hide some item and show certain items only when the user select specific item in the drop down list.
For example, In my page, I have Chart TextBox, Text TextBox, Grid TextBox and a drop down list that contain Chart, Text, and Grid. when ever user select Text, I want to show only Text TextBox and hide the rest.
right now, the three chart type options are showing on drop drop list when ever I run the project but it's not doing anything when I select Text and also I got an error on my ngIf saying that
"Property 'text' does not exist on type 'ChartType'."
I would be really appreciate if can somebody teach me or help me.
The problem I have is in the project I found from from github, the data for drop down list is in the another file called chart.model.ts and it written like this
export class ChartUtil {
public static getChartTypeDisplay(type: ChartType): string {
switch (type) {
case ChartType.chart:
return "Chart"
case ChartType.text:
return "Text";
case ChartType.grid:
return "Grid";
default:
return "unknown";
}
}
}
and display it like this
design.component.ts
chartTypes: TypeListItem[] = [];
setupTypes() {
let keys = Object.keys(ChartType);
for (let i = 0; i < (keys.length / 2); i++) {
this.chartTypes.push({ value: parseInt(keys[i]), display: ChartUtil.getChartTypeDisplay(parseInt(keys[i])) })
}
html
<ng-container *ngIf="chart.chartTypes == chartTypes.text">
<mat-form-field appearance="fill">
<mat-label>Text</mat-label>
<input matInput />
</mat-form-field>
I can't uploaded the full project on stackblitz but I uploaded all the code from those file over https://stackblitz.com/edit/angular-ivy-dmf3vn
This is normally how you would tackle a ng-switch
Component Code (badly done)
import { Component, VERSION, OnInit } from '#angular/core';
export class ChartType {
chart = 1;
text = 2;
grid = 3;
}
export class TypeListItem {
public value = 0;
public display = '';
public chartType = -1;
}
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
public chartTypesEnum = new ChartType();
public chartTypes: TypeListItem[] = [];
ngOnInit() {
let keys = Object.keys(this.chartTypesEnum);
for (let i = 0; i < (keys.length ); i++) {
this.chartTypes.push(
{
value: parseInt(ChartType[keys[i]]),
chartType: this.chartTypesEnum[keys[i]],
display: `This is a ${keys[i]}`
})
}
}
}
HTML (again badly done but simple)
<ng-container *ngFor="let chart of chartTypes">
<ng-container [ngSwitch]="chart.chartType" >
<div *ngSwitchCase="chartTypesEnum.chart">Chart</div>
<div *ngSwitchCase="chartTypesEnum.grid">Grid</div>
<div *ngSwitchCase="chartTypesEnum.text">Text</div>
</ng-container>
</ng-container>
Example
https://stackblitz.com/edit/angular-ivy-bievwr
Example 2
https://stackblitz.com/edit/angular-ivy-jhv4bq
This solution will render an Angular Material dropdown List using Enum with the ChartTypes insted of the your switch.
The Component:
import { Component, OnInit } from '#angular/core';
export enum ChartTypes {
Chart = 'Chart',
Text = 'Text',
Grid = 'Grid',
}
#Component({
selector: 'app-select-by-type',
templateUrl: './select-by-type.component.html',
styleUrls: ['./select-by-type.component.css']
})
export class SelectByTypeComponent implements OnInit {
// create an enum variable to work with on the view
chartTypes = ChartTypes;
// initialize the dropdown to a default value (in this case it's Chart)
chartsValue: string = ChartTypes.Chart;
constructor() { }
ngOnInit() {
}
}
The View:
<mat-form-field appearance="fill">
<mat-select required [(value)]="chartsValue">
<mat-option *ngFor="let chartType of chartTypes | keyvalue" [value]="chartType.value">{{chartType.value}}
</mat-option>
</mat-select>
<mat-label>Chart Type</mat-label>
</mat-form-field>
<ng-container *ngIf="chartsValue === chartTypes.Text">
<mat-form-field appearance="fill">
<mat-label>Text</mat-label>
<input matInput />
</mat-form-field>
</ng-container>
<ng-container *ngIf="chartsValue === chartTypes.Chart">
Chart In Template
</ng-container>
<ng-container *ngIf="chartsValue === chartTypes.Grid">
Grid In Template
</ng-container>

How can I filter my datasource using checkboxes using Angular Material?

I am trying to filter my datatable by clicking a checkbox. Checking one checkbox filters it properly, but checking multiple is the problem.
The Angular Material documentation is very vague regarding proper filtering where a lot of elements are involved. It has something to do with filterPredicate, but there is almost no (or just awfully vague) online documentation.
<mat-form-field appearance="legacy">
<mat-label>Select Province(s)</mat-label>
<mat-select placeholder='Provinces' formControlName="provinceSelector" multiple>
<mat-option *ngFor="let p of provinces" [value]='p.provinceName' (click)='addfilter()'>
{{p.provinceName}} ({{p.adsInProvince}})
</mat-option>
</mat-select>
</mat-form-field>
this.registeredUserService.GetAllAdverts().subscribe(val => {
this.dataSource = new MatTableDataSource<Card>(val);
this.dataSource.paginator = this.paginator;
const myPredicate = (myObject:IProvince,filterString:any)=>
{
let filterObj:IProvince = JSON.parse(filterString);
if(!filterObj.provinceName.includes((obj)=>obj=myObject.provinceName))
{
return false;
}
else
{return true;}
}
this.dataSource.filterPredicate=myPredicate;
myFilter:IProvince={
provinceName:[]
}
addfilter() {
this.myFilter.provinceName=this.search.value;
this.dataSource.filter = JSON.stringify(this.myFilter);
}
export interface Card {
advertDate: any;
advertDescription: any;
advertID: any;
cityName: any;
provinceName: any;
sellerID: any;
sellingPrice: any;
vehicleColor: any;
vehicleMake: any;
vehicleMileage: any;
vehicleModel: any;
vehicleYear: any;
}
export interface IProvince{
provinceName:any[];
}
it should just filter through the selected values...
it does not do it properly.
You are right with the filter predicate. You can define how to filter you dataSource. It is a function which returns true(if filter matches) or false.
const myPredicate = (myObject, filterString) => {
let filterObj: MyFilterObj = JSON.parse(filterString);
if(!filterObj.attributeA.find((obj) => obj == myObject.name) ||myObject.attributeB != filterObject.attributeB) { // <-- edit includes to find
return false;
} else {
return true;
}
}
Put the following code after generating your MatTableDataSource:
this.dataSource.filterPredicate = this.myPredicate.
Use the following method for setting your filter. I always give a type string for determining which filter I want to set.
myFilter: MyFilterObject = { // <-- define this variable in your ngOnInit method
attributeA: [], // <-- this is the place where you can put your selected options
attributeB: null
}
applyFilter(value: any, type: string) {
switch(type) {
case "attributeA":
this.myFilter.attributeA = value;
break;
case "attributeB":
this.myFilter.attributeB = value;
break;
default:
break;
}
this.dataSource.filter = JSON.stringify(this.myFilter);
}

Unable to apply search filter for data present in json file

This is my (custom.component.html) file
<input ng-model="searchText" placeholder=" Enter the name"
class="seacrh-field"><br><br>
<mat-card class="example-card" *ngFor="let spaceScreen of
spaceScreens;
let i = index">
<mat-card-header>
<div mat-card-avatar class="example-header-image" >
<img mat-card-image class="list-img" src="
{{spaceScreen?.img}}" >
</div>
<mat-card-content class="names">{{ spaceScreen?.name }}
</mat-card-content>
</mat-card-header>
</mat-card>
This is (custom.component.ts) file
import { Component, OnInit } from '#angular/core';
import { Http } from '#angular/http';
import { map } from 'rxjs/operators'
#Component({
selector: 'ylb-customer',
templateUrl: './customer.component.html',
styleUrls: ['./customer.component.css']
})
export class CustomerComponent {
spaceScreens: Array<any>;
constructor(private http:Http){
this.http.get('assets/app.json').pipe(
map(response => response.json().screenshots)
).subscribe(res => this.spaceScreens = res);
}
}
This is(app.json) file present in assets folder
{
"screenshots":[
{
"img":"assets/img/json_2.jpg",
"name":"Virat Kohli"
},
{
"img":"assets/img/json_2.jpg",
"name":"Joe Root"
},
{
"img":"assets/img/json_2.jpg",
"name":"Adam Gilchrist"
},
{
"img":"assets/img/json_2.jpg",
"name":"Kevin Peterson"
},
{
"img":"assets/img/json_2.jpg",
"name":"Misbhah-Ul-Hak"
},
{
"img":"assets/img/json_2.jpg",
"name":"ABD Develliers"
},
{
"img":"assets/img/json_2.jpg",
"name":"Ben stokes"
},
{
"img":"assets/img/json_2.jpg",
"name":"Chris Gayle"
}
]
}
Everything is working fine,how can i apply search filter(like contact list in mobile)to data present in app.json file.Tried many method,still no result.How can i achieve easily using custom pipes
In Angular 1 most people did filtering with pipes. This was pointed out by trichetriche in the comments of the other answer and is correct. Problem is allowing this behavior leads to poor performance because every digest cycle would trigger the filtering (and this happens a lot). So in Angular 2+ they want you to filter the results in the components and store the results in an array, then just use *ngFor="myFilteredArray".
First setup binding on your input so we can get what the user wants to search for.
//Use this method if you want to track name for more than a filter.
//Result will be stored in name var
<input type="text" [(ngModel)]="name" (ngModelChange)="onNameChange()">
//Use this if you don't need to track the name text for anything else.
<input type="text" (input)="onNameChangeInput($event.target.value)">
<div *ngFor="let s of filteredScreenshots">
{{s | json}}
</div>
Second add change functions to your component.ts
You are going to want a library like lodash or underscore.
If you don't have lodash install with - https://lodash.com/docs
npm install lodash
component.ts
import * as _ from 'lodash';
export class CustomerComponent {
spaceScreens: Array<any>;
filteredScreens = [];
name: string;
constructor(private http:Http){
this.http.get('assets/app.json').pipe(
map(response => response.json().screenshots)
)
.subscribe(res => {
this.spaceScreens = res; //this is what we filter against
this.filteredScreens = res; //init with full list
});
}
onNameChange() {
this.filteredScreens = _.filter(this.spaceScreens, (screenshot) => {
const name = screenshot['name'];
const filteredName = this.name.toUpperCase();
return name === undefined ? false : name.toUpperCase().startsWith(filteredName);
});
}
onNameChangeInput(filteredName: string) {
this.filteredScreens = _.filter(this.spaceScreens, (screenshot) => {
const name = screenshot['name'];
filteredName = filteredName.toUpperCase();
return name === undefined ? false : name.toUpperCase().startsWith(filteredName);
});
}
}
You only need to go with one input and one change function, they are named appropriately so it's obvious which method goes with each input.
Edit:
I forgot to mention this solution is case insensitive, since generally on a search like this you don’t care about case. If you want it to be case sensitive then remove the .toUpperCase().