Angular - Implement NgxMatSelectSearch in mat-select - html

I am trying to use a search box in a mat-select that works correctly only when using data loaded by default. I want to use data from an api. But it does not work properly, the data is not displayed in the mat-select when loading the page, but it is displayed when a focus occurs in the mat-select tag.
I have a model where I use the data from a test API
export interface DataModel {
id: number;
title: string;
userId: number;
}
export const DataModels: DataModel[] = [
{ id: 1, title: 'Option A', userId: 23 },
{ id: 2, title: 'Option B', userId: 24 },
{ id: 3, title: 'Option C', userId: 25 },
{ id: 4, title: 'Option D', userId: 26 }
];
My service where I make the call
#Injectable()
export class DataloadService {
constructor(private http: HttpClient) {}
LoadData(): Observable<any> {
return this.http.get('https://jsonplaceholder.typicode.com/albums');
}
}
The component where the search filter is performed and controls are set. Following the documentation NgxMatSelectSearch
constructor(private service: DataloadService) {}
dataModel: DataModel[] = []; //DataModels
dataCtrl: FormControl = new FormControl();
dataFilterCtrl: FormControl = new FormControl();
filteredData: ReplaySubject<DataModel[]> = new ReplaySubject<DataModel[]>(1);
#ViewChild('singleSelect', { static: true }) singleSelect: MatSelect;
_onDestroy = new Subject<void>();
ngOnInit() {
this.load();
this.filteredData.next(this.dataModel.slice());
this.dataFilterCtrl.valueChanges
.pipe(takeUntil(this._onDestroy))
.subscribe(() => {
this.filterData();
});
}
ngOnDestroy() {
this._onDestroy.next();
this._onDestroy.complete();
}
filterData() {
if (!this.dataModel) {
return;
}
let search = this.dataFilterCtrl.value;
if (!search) {
this.filteredData.next(this.dataModel.slice());
return;
} else {
search = search.toLowerCase();
}
this.filteredData.next(
this.dataModel.filter(
(x: any) => x.title.toLowerCase().indexOf(search) > -1
)
);
}
load() {
return this.service.LoadData().subscribe(res => {
this.dataModel = res;
});
}
And the HTML
<mat-card>
<mat-toolbar>Demo</mat-toolbar><br />
<mat-card-content>
<mat-select [formControl]="dataCtrl" placeholder="Data" #singleSelect>
<mat-option>
<ngx-mat-select-search
[formControl]="dataFilterCtrl"
></ngx-mat-select-search>
</mat-option>
<mat-option *ngFor="let x of filteredData | async" [value]="x.id">
{{x.title}}
</mat-option>
</mat-select>
</mat-card-content>
</mat-card>
If I use the data that is by default in the model to simulate the process using "dataModels"
dataModel: DataModel[] = []; //DataModels
Instead of initializing it empty. It works normally but if I load the data with the request made to the API, the problem arises that it is not loaded after a focus occurs.
The demo I have in Stackblitz: Demo Stackblitz

You should add this line
this.filteredData.next(this.dataModel.slice());
into subscribe event of this.service.LoadData() as it is asynchronous. So that when the response result is returned, the filteredData is bonded with the response result.
load() {
return this.service.LoadData().subscribe(res => {
this.dataModel = res;
this.filteredData.next(this.dataModel.slice());
});
}
Sample Solution on StackBlitz

Related

Mat-Option selected option based on patch value from api subscription

In my case i have several controls with mat-autocomplete with mat-options. in which the same form is handled for new entry creation and edit an existing entry data. So i have tried the following methods to implement the mat-option selected by default or mat-option is active based on the data fetched from the server side api, but the approach is not working. Please refer my code below provide a solution to make this case working.
HTML
<mat-form-field floatLabel="never" class="dropdown-arrow-icon">
<span class="material-icons">arrow_drop_down</span>
<input type="text" matInput formControlName="UOM" [matAutocomplete]="uomSelect" (keyup)="onUomKeyUp($event.target.value)">
<mat-autocomplete #uomSelect="matAutocomplete" [displayWith]="selectedDropdownForUom">
<mat-option *ngFor="let uom of uomOption" [value]="uom.value">
{{ uom.value }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
TYPESCRIPT
uomData = [
{ id: 1, value: "Parts", key: "PARTS" },
{ id: 2, value: "G/KG", key: "KG" },
{ id: 3, value: "L/Gal", key: "L" },
{ id: 4, value: "oz/lb", key: "OZ" },
{ id: 5, value: "%", key: "PERC" },
]
machineDetails;
ngOnInit(): void {
this.patchValue();
}
public selectedDropdownForUom = (uomData?: string): string => {
return uomData;
};
public onUomKeyUp(value: string): void {
if (value === "") {
// eslint-disable-next-line no-self-assign
this.uomOption = this.uomData;
} else {
this.uomOption = this.uomData.filter((item) => new RegExp(value, "gi").test(item.value));
}
}
private selectDefaultAutocompleteData(selectedUOM) {
find(this.uomSelect.options.toArray(), (uomOption) => uomOption.value === selectedUOM? .value)?.select();
}
public patchValue(){
this.httpClient.get("https://conversion.intil.io/units/45").subscribe(data =>
{this.machineDetails = data;
const selectedUOM = this.uomData.find(uom => uom.id === this.machineDetails.UoMID
this.experimentForm.patchValue({ UOM: selectedUOM.value});
this.selectDefaultAutocompleteData(selectedUOM);
})
}
RECIEVING OUTPUT
actual output recieving
EXPECTED OUTPUT
expected output

angular ngx-treeview from Json object error : A text of item must be string object

this treeview item text is confusing me for the past week
this is how I load the items into the tree view:
ngOnInit(): void {
this.items = this.getItems([JSON.stringify(this.json_obj)]);
}
getItems(parentChildObj: any[]) {
let itemsArray: TreeviewItem[] = [];
parentChildObj.forEach((set: TreeItem) => {
itemsArray.push(new TreeviewItem(set,true))
});
return itemsArray;
}
and this is how I create the nested Json object from non-nested Json file :
this.departments.forEach(element => {
if(element.ParentID == 0){
p_counter++;
this.json_obj.push(
{
text: element.DepartmentName ,
value: 'p'+p_counter.toString() ,
children: [],
id: element.DepartmentID.toString() ,
} as never
);
element.DepartmentName = 'fixed';
}
});
the template is simple as that:
<ngx-treeview [items]="items" dir ="rtl"></ngx-treeview>
btw- it creates a perfect nesting but it cant read the object properties in
new TreeviewItem(set,true);
because it's undefined.
Error : A text of item must be string object at new TreeviewItem
please help me figure this out, what can I do to make it work?
You have used
text: element.DepartmentName ,
value: 'p'+p_counter.toString() ,
children: [],
id: element.DepartmentID.toString()
It seems you have not followed TreeItem interface given in treeview-item.d.ts
export interface TreeItem {
text: string;
value: any;
disabled?: boolean;
checked?: boolean;
collapsed?: boolean;
children?: TreeItem[];
}
you should remove id because that is not property of TreeItem interface.
import { Component,OnInit } from '#angular/core';
import { TreeviewItem } from 'ngx-treeview';
#Component({
selector: 'my-app',
template: `<ngx-treeview [items]="items"></ngx-treeview>`
})
export class AppComponent implements OnInit {
items: TreeviewItem[];
ngOnInit() {
this.items = this.getItems();
}
getItems(){
// fetch api response
// convert response into this format (object can be nested, should contain below keys only with given type)
// {
// text: string;
// value: any;
// disabled ?: boolean;
// checked ?: boolean;
// collapsed ?: boolean;
// children ?: TreeItem[];
// }
const item = new TreeviewItem({
text: 'Children', value: 1, children: [
{ text: 'Baby 3-5', value: 11 },
{ text: 'Baby 6-8', value: 12 },
{ text: 'Baby 9-12', value: 13 }
]
});
return [ item ];
}
}
stackblitz example

Can't render data from api call from one component to another in Angular 8

I am new to Angular and I am facing issue in rendering data in UI from an api call. I want to show the data received as response in the parent and show it in a component called webex-uptime-chart.
The file with API call is as shown below:
public uptimeChartConfig: Array<{ [key: string]: string | any }>;
this.uptimeChartConfig = [
{
rpcMethod: 'getNodeStatus',
node: this.NodeId,
duration: '10 mins'
},
];
// API call to get the Uptime Chart data
this.uptimeChartConfig
.filter(config => config.rpcMethod)
.map(config => {
return this.rpcService
.invoke({
method: 'getNodeStatus',
args: ['2d945891-be9b-46a8-973e-3f343a8999ad'],
})
.then((data: any) => {
if (data && data.response) {
const labels: Array<string> = data.response.map(value =>
this.datePipe.transform(value.epochtime * 1000, 'shortTime')
);
const nodeList = {};
data.response.forEach(node => {
if (nodeList[node.nodeId]) {
nodeList[node.nodeId] = [...nodeList[node.nodeId], node.uptime];
} else {
nodeList[node.nodeId] = [node.uptime];
}
});
this.lineChartData[config.rpcMethod] = {
labels: labels,
dataSets: nodeList,
};
} else {
this.lineChartData[config.rpcMethod] = {
lables: [],
dataSets: [],
};
}
});
The response looks as shown below:
The parent component's html where the webex-uptime-chart is called looks as shown below:
<webex-uptime-chart
*ngFor="let config of uptimeChartConfig"
[config]="config"
[incomingData]="lineChartData[config.rpcMethod]">
</webex-uptime-chart>
The webex-uptime-chart.ts component file is:
import { Component, Input, OnInit } from '#angular/core';
#Component({
selector: 'webex-uptime-chart',
templateUrl: './uptime-chart.component.html',
styleUrls: ['./uptime-chart.component.scss']
})
export class UptimeChartComponent implements OnInit {
#Input() chartData: any[];
#Input() public config;
#Input() public incomingData: any;
public labels: Array<string> = [];
public dataSets: any = {};
constructor() { }
ngOnInit() {
this.labels = this.incomingData.labels;
this.dataSets = this.incomingData.dataSets;
}
}
The webex-uptime-chart.html file is:
<div class="uptime-container">
<ul *ngFor="let data of chartData">
<li [ngClass]="data.status === 'down' ? 'my-class red-circle' : 'my-class green-circle '">
<span>{{ config.node }}</span>
</li>
<p class="right-text">{{ config.duration }}</p>
<hr />
</ul>
</div>
I get the below error while trying to run :
I don't know how to proceed.
incomingData is asynchronous. As a result it is initially provided as undefined to the child component until the promise then callback was executed. But this change is not registered within child component, since you only read incomingData within ngOnInit.
You could use ngOnChanges instead of ngOnInit.
ngOnChanges(changes: SimpleChanges) {
if (changes['incomingData'] && !!changes['incomingData'].previousValue) {
this.labels = changes['incomingData'].currentValue.labels;
this.dataSets = changes['incomingData'].currentValue.dataSets;
}
}

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);
}

JSON data not displaying in Ionic HTML file

I have got my data from my API and it is logged in the console. However, I am unable to access it through my HTML file. I'm not sure why it isn't working? (Looked at other questions and still no joy).
ts file
patients: [];
constructor(private viewService: ViewPatientService ) { }
ngOnInit() {
this.viewService.viewPatient().subscribe(data => {
console.log(data);
});
html file
<ion-item *ngFor="let patient of patients">
Name: {{patient.patients.data[0].FirstName}}
</ion-item>
Please note data should hold the patients array, if not add the property as needed.
patients: [];
constructor(private viewService: ViewPatientService) { }
ngOnInit() {
this.viewService.viewPatient().subscribe(data => {
console.log(data);
this.patients = data
});
}
You haven't assigned the value to patients array
patients: [];
constructor(private viewService: ViewPatientService ) { }
ngOnInit() {
this.viewService.viewPatient().subscribe(data => {
**this.patients = data;**
console.log(data);
});
patients: any;
constructor(private viewService: ViewPatientService ) { }
ngOnInit() {
this.viewService.viewPatient().subscribe(data => {
this.patients = [data];
console.log(data);
});