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

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

Related

Access nested object properties in json object

how can I loop through the following object in Angular using *ngFor(suppousing there are many objects)? I can't find how to access "type" property. Also I wonder whether "Animals Catalog" property is correct? Thanks in advance.
{
"name":"Richard",
"lastname":"Garcia",
"age":32,
"pets":{
"Animals Catalog":{
"type":"cat",
"gender":"male"
}
},
}
that property is correct and you have to access it like this:
<h1 *ngFor="let item of items">{{item.pets['Animals Catalog'].type}}</h1>
you need to declare and access object.keys to use it in template
app.component.ts
objectKeys = Object.keys;
obj = {
name: 'Richard',
lastname: 'Garcia',
age: 32,
pets: {
'Animals Catalog': {
type: 'cat',
gender: 'male',
},
},
};
app.component.html
<div *ngFor="let key of objectKeys(obj)">{{ key + ' : ' + obj[key] }}</div>
reference: Object.keys()
Based on your code example:
interface
export interface User {
name: string;
lastname: string;
age: number;
pets: Pet;
}
export interface Pet {
[key: string]: PetDetail;
}
export interface PetDetail {
type: string;
gender: string;
}
component
<div *ngFor="let user of users">
<p>{{ getValue(user) }}</p>
</div>
#Component(...)
export class Component {
getValue(user: User): string {
const keys = Object.keys(user);
for (const key of keys) {
const isPetObject = this.validateType(user[key]);
if (!isPetObject) return user[key];
const pets = user[key];
const petKey = Object.keys(pets)[0]; // 'Animal Catalog'
return pets[petKey]?.type; //cat
}
}
private validateType(value: any): boolean {
return typeof value !== string && typeof value !== number;
}
}

Angular - Implement NgxMatSelectSearch in mat-select

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

Angular: How do I return all data for selected item in mat-select on mat-dialog whilst keeping code for selected default value

I have a modal mat-dialog populating a mat-select. The dropdown is populated with languages. I'm binding the (value) to the ID of the language so it can be preselected with a default value if data is passed into the dialog. However, I would like to bring back the "ID" and the "language description" seen in the dropdown. Is there a way to populate both data fields when returning the data of a mat-select on mat-dialog-close? I do understand why I am getting "data.ID" and "data.translation" back, but how can I get the "data.language" selected populated? Important: dropdown is pre-selected with data.id when a language has previously been selected, so do not want to lose default value functionality unless there is another way.
Thank you in advance.
.html
<div mat-dialog-content class="fullwidth">
<p>Select language to translate</p>
<mat-form-field class="fullwidth">
<mat-label>Language</mat-label>
<mat-select [(value)]="data.id">
<mat-option *ngFor="let langItem of languageList$;" [value]="langItem.value">
{{langItem.description}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="fullwidth">
<mat-label>Enter translation here</mat-label>
<textarea class="textarea" matInput placeholder="Ex. Translation here..." [(ngModel)]="data.translation"></textarea>
</mat-form-field>
</div> <div mat-dialog-actions> <button mat-stroked-button class="right" [mat-dialog-close]="data" cdkFocusInitial>Save</button> </div>
.ts
openTranslationDialog(event, elem): void {
const dialogRef = this.translateDialog.open(TranslationModalComponent, {
data: {id: String(this._transData?.id), language: this._transData?.language, translation: this._transData?.translation}
});
dialogRef.afterClosed().subscribe(result => {
console.log("Language Description: " + result.description);
console.log("Language ID: " + result.id); //only get ID back
});
}
json
{code: "1033", description: "Afrikaans", value: "100000001"}
{code: "1033", description: "Damara", value: "100000002"}
{code: "1033", description: "English", value: "100000000"}
{code: "1033", description: "German", value: "100000003"}
{code: "1033", description: "isiNdebele", value: "100000004"}
{code: "1033", description: "isiXhosa", value: "100000005"}
{code: "1033", description: "isiZulu", value: "100000006"}
it's in the [mat-dialog-close] where you indicate what value do you want to received in the "result" when subscribe. So you can create a function in your TranslationModalComponent
getData() {
const language = this.languageList$.find(
x => x.value == (this.data as any).id
);
return { ...this.data, ...language };
}
And use
<button [mat-dialog-close]="getData()" ...>Save</button>
Final piece of code that worked, thanks to #Eliseo
getData() {
if ((this.languageService.languageList.length > 0) && ((this.data as any).id > 0))
{
const language = this.languageList.find(
x => x.value == (this.data as any).id
);
if (language)
return { id: language.value, language: language.description, translation: this.data.translation};
else
return { id: "", language: "", translation: ""};
}
}

Angular 4/5 - filtering drop-downs according to headings

In Angular, I wrote a rest call to obtain the following data (I will represent it in json format):
First equipment type json data, for drop-down headings
{
id: 1,
description: "Head Protection"
},
{
id: 2,
description: "Hand Protection"
},
{...}, ...
Secondly equipment json data, for drop-down values
{
id: 1,
equipmentTypeId: 1,
equipmentTypeDescription: "Head Protection",
description: "Climbers helmet"
},
{
id: 2,
equipmentTypeId: 2,
equipmentTypeDescription: "Hand Protection",
description: "Climbers gloves"
},
{...}, ...
Here is a screen shot of what I want to do (but filtered):
I have the following models in place:
Equipment Type Model
export class EquipmentType implements BaseEntity {
constructor(
public id?: number,
public description?: string,
...
) {
}
}
Equipment Model
export class Equipment implements BaseEntity {
constructor(
public id?: number,
public description?: string,
public equipmentTypeId?: number,
...
) {}
}
In my component.ts file, I have the following codes:
// class defined of course
// property declearations
equipment: Equipment[];
equipmentObj: Equipment;
equipmentType: EquipmentType[];
constructor(
private equipmentService: EquipmentService,
private equipmentTypeService: EquipmentTypeService
) {}
ngOnInit() {
this.equipmentTypeService.query()
.subscribe((res: ResponseWrapper) => {
this.equipmentType = res.json;
this.equipmentService.query()
.subscribe((res: ResponseWrapper) => {
this.equipment = res.json;
for ( let x = 0; x < this.equipment.length; x++) {
this.equipmentService.find(this.equipment[x].id).subscribe((equip) => {
this.filterProtection = this.equipment.filter((equipmentType) => {
equipmentType.equipmentTypeId === this.equipment[x].id
console.log(this.equipmentType);
});
console.log(this.filterProtection);
});
}
}, (res: ResponseWrapper) => this.onError(res.json));
}, (res: ResponseWrapper) => this.onError(res.json));
}
trackEquipmentById(index: number, item: Equipment) {
return item.id;
}
trackEquipmentTypeById(index: number, item: EquipmentType) {
return item.id;
}
My HTML (component.html) has the following codes:
<div *ngFor="let equipmentOption of equipmentType">
<span>{{equipmentOption.description}}</span>
<select class="form-control" id="field_issuedBy" name="issuedBy" [(ngModel)]="equipmentOption.id">
<option [ngValue]="null"></option>
<option [ngValue]="equipmentOpt.id" *ngFor="let equipmentOpt of equipment; trackBy: trackStatusById">{{equipmentOpt.description}}</option>
</select>
</div>
Now with that been defined, I am struggling to filter Equipment Type with Equipment for my drop-down value.
For instance, if a user select the Head Protection drop-down (from equipement type), only equipements with the same equipmentTypeId(s) should appear as values for the drop-down.

How to v-model 2 values?

I was using buefy <b-autocomplete> component and there is one property called v-model which is binding values to the input field
now I wanna bind Full Name into the field but the data consist with list[index].first_name and list[index].last_name, and the index is from a v-for loop.
Since v-model cannot bind a function (it has specific index so I cannot just concat it on computed then pass it on) so it's either v-model="list[index].first_name" or v-model="list[index].last_name"
How do I make it bind's these two?
You need a settable computed for full name, and you can v-model that. You just have to decide on a rule for where extra spaces go and what to do if there is no space.
new Vue({
el: '#app',
data: {
firstName: 'Joe',
lastName: 'Smith'
},
computed: {
fullName: {
get() {
return `${this.firstName} ${this.lastName}`;
},
set(newValue) {
const m = newValue.match(/(\S*)\s+(.*)/);
this.firstName = m[1];
this.lastName = m[2];
}
}
}
});
<script src="//unpkg.com/vue#latest/dist/vue.js"></script>
<div id="app">
First: {{firstName}}<br>
Last: {{lastName}}<br>
Full Name: <input v-model="fullName">
</div>
I am not sure if i get the question,but i am assuming that you have a list of names and last names and you want to give the ability to user to change those proprties of list.For more See the example in action
The "html" part
<div id="app">
<template v-for="item in list" :key="list.id">
<input type="text" :value="item.name" #input="changeList($event, item.id, 'name')">
<input type="text" :value="item.last_name" #input="changeList($event, item.id, 'last-name')">
Your full name is {{item.name}} {{item.last_name}}
<hr>
</template>
</div>
The "javascript(vue)" part
new Vue({
el: "#app",
data: {
list: [
{ id: 1, name: "name1", last_name: 'last_name 1' },
{ id: 2, name: "name2", last_name: 'last_name 2' },
{ id: 3, name: "name3", last_name: 'last_name 3' },
{ id: 4, name: "name4", last_name: 'last_name 4' }
]
},
methods: {
changeList(event, id, property) {
let value = event.target.value
for (item of this.list) {
if (item.id === id) {
if(property === 'name') {
item.name = value
}else if (property === 'last-name') {
item.last_name = value
}
}
}
}
}
})
As it's been said you can't use 2 values in v-model
But if you want to use <b-autocomplete> that means you already have the data and you can compute it in any way you want.
If you have an array of user objects (with firstname and lastname for example) you can do something like:
data() {
return {
users: [
//...
],
computedUsers: [],
}
},
mounted() {
this.users.forEach(user => {
this.computedUsers.push(user.firstname + ' ' + user.lastname)
})
}
and use this new array in the filteredDataArray (from the Buefy autocomplete example)
Fiddle example here