This is my object:
const ELEMENT_DATA: Element[] = [
{id: 1, name: 'Hydrogen', symbol: 'H'},
{id: 2, name: 'Hydrogen', symbol: 'H1'},
{id: 3, name: 'Helium', symbol: 'He'}
];
which is displayed in datatable with Edit buttons, like so:
When I click Edit (for example I clicked Hydrogen) it should populate with
name: 'Hydrogen', symbol: 'H'.
But now I am getting the Symbol List dropdown empty.
Demo
When I click the Add button, a pop up will come with two dropdowns: Element List and Symbol List. Based on the Element name Symbol List will come.
Now when I click the Edit button in datatable, that should populate that particular row in the popup. How can I do this?
html
<form [formGroup]="addTaskForm" (ngSubmit)="save()" >
<mat-form-field>
<mat-select formControlName="name" placeholder="Element List" (selectionChange)="elementSelectionChange($event)">
<mat-option *ngFor="let element of Elements" [value]="element.name">
{{ element.name }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-select formControlName="symbol" placeholder="Symbol List">
<mat-option *ngFor="let element of selectedElementSymbol" [value]="element.symbol">
{{ element.symbol }}
</mat-option>
</mat-select>
</mat-form-field>
<div mat-dialog-actions>
<button mat-button (click)="onNoClick()">Cancel</button>
<button type="submit" mat-button cdkFocusInitial>Add</button>
</div>
</form>
You shoule update datasource after each value added from the dialog, change your save() function like this:
save() {
const data = this.data.originalform.dataSource.data;
this.addTaskForm.value["id"]=data.length+1
data.push(this.addTaskForm.value);
this.data.originalform.dataSource=new MatTableDataSource<Element>(data);
console.log('working');
console.log(this.addTaskForm.value);
}
And to not forget to add a referer to the parent object originalform from this:
let dialogRef = this.dialog.open(DialogOverviewExampleDialog, {
width: '250px',
data: { element: element,originalform:this }
});
Hope this helps:)
Edit:
By call of this comment, i updated the fiddle in the following to fit the paginating in.
Adding paginator figures as following:
this.data.originalform.dataSource.paginator=this.data.originalform.paginator;
The symbol list is initialized in the method elementSelectionChange, but
you are calling elementSelectionChange($event) only when the Element list selection changes.
<mat-select formControlName="name" placeholder="Element List" (selectionChange)="elementSelectionChange($event)">
One way you can do it is,
elementSelectionChange(event) {
this.loadSymbols(event.value);
}
loadSymbols(name) {
let value = this.Elements.find(e => e.name === name);
this.selectedElementSymbol = this.Symbols.filter(e => e.id === value.id);
console.log(this.selectedElementSymbol);
}
and then call loadSymbols in the constructor
constructor() {
if (data.element) {
this.name = data.element.name;
this.symbol = data.element.symbol;
this.loadSymbols(this.name);
}
}
StackBlitz
Related
I have a select with some filters, every filter should be connected to a div (in the first moment they are hidden, i think with *ngIf, but im not sure how to integrate the it with my code) with his specific functionality. In my project i have the possibility to select which filter i wanna show.
component.ts:
object with filters:
filters: Filter[] = [
{value: 'textSearch', name: 'Denominazione', selected: false},
{value: 'internalReferent', name: 'Referente interno', selected: false},
{value: 'supplierSignUpTimestamp', name: 'Data iscrizione', selected: false},
{value: 'internalReferent', name: 'Merceologia', selected: false},
{value: 'internalReferent', name: 'Sede operativa', selected: false},
{value: 'supplierOperationalHQ', name: 'Stato', selected: false},
]
constructor(private fb: FormBuilder) {
this.supplierFilterForm = this.fb.group({
filter: ['']
});
}
function for toggle:
toggleSelected() {
let filterSelected = this.supplierFilterForm.controls['filter'].value;
let i = this.filters.findIndex(x => x.name == filterSelected);
console.log(filterSelected)
console.log(i)
if (filterSelected == this.filters[i].name) {
this.filters[i].selected = !this.filters[i].selected;
}
}
debugging this function i change in the right way the value of the boolean of each filter, but there is the problem, how can i set an ngIf in my html file with this logic?
component.html
Filter select i have to click on at the start
<div class="col-2 d-flex">
<mat-form-field>
<mat-label>{{ 'supplier.showFilters' | translate }}</mat-label>
<mat-select (selectionChange)="toggleSelected()" formControlName="filter">
<mat-option *ngFor="let filter of filters; let i = index" [value]="filter.name"
>
{{i}} {{filter.name}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
Exemple of filter i would like to let appear selecting his own option in the previous select:
<div class="col-2">
<mat-form-field>
<mat-label>{{ 'supplier.supplierStatus' | translate }}</mat-label>
<mat-select formControlName="supplierStatus">
<mat-option [value]="null">--</mat-option>
<mat-option *ngFor="let option of selectData.supplierStatus" [value]="option.value">
{{ option.display | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
at the start of this 'task' i was thinking about declare a global variable instead of a single boolean to each filter but obviously he toggled off/on every filter
p.s. im sorry for my english skills!
In My UI, Each Row has level1,level2 and level3 dropdown along with "Add Level" Button.
I want on click on "Add Level" button, it will add a new set of three dropdown level1,level2 and level3 in the same row. As much as user click the "Add level" Button, every time it will dynamically add new set of dropdown in same row.
Other row dropdown will not impact with this.
Can anyone please help to modify my old code to work as my expected output.
StackBlitz Code URL
Please refer the attached image for my expected output for level1 level2 and level3 dropdowns
It's always the same. To control any object using ReactiveForms, you create a FormGroup for Object and FormArray for array. If the Array is an object, you create a FormArray of FormGroup, else you create a FormArray of FormGroup
The first is decided the structure of your data, as "levels" is an array, your data can be, e.g.
{
"plannedProduction": 210468,
"factORLossTree": [
{
"skuid": "000000000034087070",
"skuDescription": "SUNLIGHT DESTINY HW PWD YELLOW 8X900G",
"productionOrderNo": "X49033251",
"category": "Fabric Cleaning",
"levels": [
{
"level1": "",
"level2": "",
"level3": ""
}
....
]
},
{
"skuid": "000000000067141695",
"skuDescription": "SUNLIGHT DESTINY YELLOW 24X170G",
"productionOrderNo": "X49039793",
"category": "Fabric Cleaning",
"levels": [
{
"level1": "",
"level2": "",
"level3": ""
}
....
]
},
....
]
}
As always you have a FormArray you use a getter (*) you still have in your code
get lossFormArray() {
return this.orLosstreeForm.get('factORLossTree') as FormArray;
}
As we have a FormArray inside a FormArray to get the inner FormArray we need another "getter". Well, can not be a getter because we need pass as argument the "index" of the formArray
getLevels(index:number)
{
return this.lossFormArray.at(index).get('levels') as FormArray;
}
we can use also an auxiliar function that return the value of the formArray at index to simplyfied the .html
getItemValue(index:number)
{
return this.lossFormArray.at(index).value;
}
Finally we are add a function to create the formGroup "level"
createLevel() {
return this.fb.group({
level1: [''],
level2: [''],
level3: [''],
});
We are going now to create the formGroup change a bit your function setData
setData() {
const resp = {...}
if (resp.factORLossTree.length > 0) {
this.orLosstreeForm.patchValue({
plannedProduction: resp.plannedVolume,
});
this.lossFormArray.clear();
for (const item of resp.factORLossTree) {
const data = {
skuid: item.skuid,
skuDescription: item.skuDescription,
productionOrderNo: item.productionOrderNo,
category: item.category,
//see how transform the data to creaate the array "levels"
levels: [
{
level1: item.level1,
level2: item.level2,
level3: item.level3,
},
],
};
this.addItem(this.createLevelItem(data));
}
}
}
And your function cteateLevelItem:
createLevelItem(data) {
const formgrp = this.fb.group({
skuid: ['', Validators.required],
...
//see how levels is a formArray, at first with an unique element
//we use the before function createLevel()
levels: this.fb.array([this.createLevel()]),
});
if (data !== null) {
formgrp.patchValue(data);
}
return formgrp;
}
A complex part know is the .html, but I want to show how create a series of dropdown mutually exclusive. It's like this SO, but in this case we are going to have an array of arrays of arrays. Don't worry, make it slow is not so difficult as looks like
First we have a lsit of possibles levels and declare an empty array
allLevels = ['Level 1', 'Level 2', 'Level 3', 'Level 4', 'Level 5'];
levelList: any[] = [];
Then we are going to fill the array levelList. The adecuate place is in the function "createLevel". In this function we are subscribe to the valueChanges of the group. but we need know the index if the level. So the function becomes like
createLevel(index: number) {
//we are going to create some like
//levelList[i][j][0], levelList[i][j][1], levelList[i][j][2]
const j = this.orLosstreeForm &&
this.lossFormArray &&
this.lossFormArray.at(index)?
this.getLevels(index).controls.length:0;
const group = this.fb.group({
level1: [''],
level2: [''],
level3: [''],
});
group.valueChanges
.pipe(startWith(null), takeUntil(this.active))
.subscribe((res: any) => {
res = res || { level1: null, level2: null };
if (!this.levelList[index]) this.levelList[index] = [];
this.levelList[index][j] = [];
this.levelList[index][j][0] = this.allLevels;
this.levelList[index][j][1] = this.allLevels.filter(
(x) => x != res.level1
);
this.levelList[index][j][2] = this.allLevels.filter(
(x) => x != res.level1 && x != res.level2
);
//And we check if there any repeat
if (this.levelList[index][j][1].indexOf(res.level2) < 0)
group.get('level2').setValue(null, { emitEvent: false });
if (this.levelList[index][j][2].indexOf(res.level3) < 0)
group.get('level3').setValue(null, { emitEvent: false });
});
return group;
}
Puff, I know is too large, but be patient, the last part is the .html
<div style="background-color: white;">
<form [formGroup]="orLosstreeForm" *ngIf="orLosstreeForm">
...
<!-- Start of OR loss tree table -->
<div class="selection-area" formArrayName="factORLossTree">
<div
*ngFor="let levelItem of lossFormArray.controls;let i=index" [formGroupName]="i"
>
<ng-container>
...here we can use getItemValue(i).skuDescription
or getItemValue(i).skuid or ...
<div class="deviations" formArrayName="levels">
<div class="row" *ngFor="let group of getLevels(i).controls;
let j=index" [formGroupName]="j">
<div class="col-md-3">
<mat-form-field>
<mat-label>Level 1</mat-label>
<mat-select
name="level1"
formControlName="level1"
>
<mat-option value="">--Select--</mat-option>
<mat-option
*ngFor="let item of levelList[i][j][0]"
[value]="item"
>
{{ item }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="col-md-3">
<mat-form-field>
<mat-label>Level 2</mat-label>
<mat-select
name="level2"
formControlName="level2"
>
<mat-option value="">--Select--</mat-option>
<mat-option
*ngFor="let item of levelList[i][j][1]"
[value]="item"
>
{{ item }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="col-md-3">
<mat-form-field>
<mat-label>Level 3</mat-label>
<mat-select name="level3"
formControlName="level3"
>
<mat-option value="">--Select--</mat-option>
<mat-option
*ngFor="let item of levelList[i][j][2]"
[value]="item"
>
{{ item }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="col-md-3">
<button
class="resetButton"
aria-describedby="Reset SKU"
(click)="getLevels(i).push(createLevel(i))"
>
Add Level
</button>
</div>
</div>
....
</div>
</form>
</div>
Se how to add a new group of levels we only need the
<button (click)="getLevels(i).push(createLevel(i))">
Add Level
</button>
The final stackblitz
I currently have an input field that has an autocomplete, I’m trying to have essentially a two stage autocomplete for each entered item, for example typing I would show IP Address, selecting that enters “ipAddress=“ into the input, I would then like to display another autocomplete in the same input based on the selected option to display the available addresses. Then they would add a comma and repeat to add any other filters. I just am not sure what this would be called or how to implement this so any tips or ideas would be greatly appreciated
Try something like this:
TS:
itemList: any[] = [
{ name: "item1", IPForm: new FormControl(), AnotherListValue: [], AnotherForm: new FormControl() },
{ name: "item2", IPForm: new FormControl(), AnotherListValue: [], AnotherForm: new FormControl() }
];
// Fill my another value regarding to selected IP
onSelectedIP(selected: any, item: any) {
this.myService.getListValue(selected.IP).subscribe(result => item.AnotherListValue = result);
}
HTML
<div *ngFor="let item of itemList">
<mat-form-field>
<input type="text" placeholder="Select IP" matInput [matAutocomplete]="itm" [formControl]="item.IPForm">
<mat-autocomplete autoActiveFirstOption #itm="matAutocomplete" [displayWith]="displayFn" (optionSelected)="onSelectedIP($event.option.value, item)">
<mat-option *ngFor="let option of IPList | async" [value]="option">
{{option.IP}}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</div>
You just need to add second auto-complete and so on
My Objective is to show a dropdown menu when i start to write # on the input field.
My Component
myControl: FormControl = new FormControl();
options = [
'One',
'Two',
'Three'
];
filteredOptions: Observable<string[]>;
ngOnInit() {
this.filteredOptions = this.myControl.valueChanges
.pipe(
startWith('#'),
map(val => val.length >= 1 ? this.filter(val): [])
);
}
filter(val: string): string[] {
return this.options.filter(option =>
option.toLowerCase().indexOf(val.toLowerCase()) === 0);
console.log(this.options)
}
This is what i have done, but its not working.
HTML
<form class="example-form">
<mat-form-field class="example-full-width">
<input #inputField type="text" placeholder="Pick one" aria-label="Number" matInput [formControl]="myControl" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let option of filteredOptions | async" [value]="option">
{{ option }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</form>
Your subscription is working great.
You just forget to escape the # in filter function since you don't have an # in your option List.
Try this :
filter(val: string): string[] {
return this.options.filter(option =>
option.toLowerCase().indexOf(val.toLowerCase().replace('#', '')) === 0);
}
Just register to the change event on your input - for example:
(change)='handlerMethode($event)'
Check input for index of # and if you find do what you want.
When I select an option instead of showing me the name it shows me the id. That's because I bind [value] = "option.id". I'm sending an id to the server and want to show name.
<mat-form-field class="example-full-width">
<input
matInput
placeholder="Pretrazite proizvod koji zelite naruciti samo kucajte pocetna slova"
formControlName="id"
[matAutocomplete] = "auto"
>
<mat-autocomplete #auto="matAutocomplete" >
<mat-option *ngFor="let option of allProducts; let i = index" [value]="option.id" (onSelectionChange)="getPosts(i)">
{{ option.name }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
getProducts() {
this.product.getAllProducts().subscribe((data: any) => {
this.allProducts = data.products;
console.log(data.products);
});
}
I also have (onSelectionChange) = "getPosts (i)" function
getPosts(index){
this.selectedProduct = index;
}
My question is how do I forward the id and display the name in mat-autocomplete-mat-option options.
I suppose this onchange is my function, the problem is that I have dynamic fields
Looking my other code
ngOnInit() {
this.getProducts();
this.form = this.fb.group({
address: [null],
phone: [null],
city: [null],
data: this.fb.array([this.createContact()])
});
this.contactList = this.form.get('data') as FormArray;
}
createContact(): FormGroup {
return this.fb.group({
id: [this.selectedProduct],
quantity: [null]
});
In case you need the whole code, ask me, but I didn't want to overwhelm you with a lot of code.
You can do something like this, it should help you:
<mat-autocomplete #auto="matAutocomplete" >
<mat-option *ngFor="let option of allProducts; let i = index" [value]="selectedProduct.name" (onSelectionChange)="getPosts(option)">
{{ option.name }}
</mat-option>
</mat-autocomplete>