Reputation: 69
Trying to implement role based restriction while creating users, For that am creating cascading select option using Angular Material select Multiple with 'All' option.
But can't proceed further, stuck in first step itself. Here the problem is i couldn't find and load childModule data which has relation with parent using parentModuleId
this.loadChildModule = this.parentModuleList.find((parent: any) => parent.parentModuleName === event.target.value).parentModuleName;
Scenario 1 if select 'All' option from parent dropdown, child dropdown should be loaded with corresponding data
example:
dropdown 1 // selecting All
parentModuleName: 'All'
dropdown 2 // load child of parent
childModuleName: 'PM0 CH-1'
childModuleName: 'PM0 CH-2'
childModuleName: 'PM0 CH-3'
childModuleName: 'PM1 CH-1'
childModuleName: 'PM2 CH-2'
childModuleName: 'PM3 CH-3'
childModuleName: 'PM2 CH-1'
childModuleName: 'PM2 CH-2'
childModuleName: 'PM3 CH-3'
Scenario 2 if select single option from parent dropdown, child dropdown should be loaded with corresponding parent's child data
example:
dropdown 1 // selecting single item
parentModuleName: 'Parent Module 0'
dropdown 2 // load child of parent
childModuleName: 'PM0 CH-1'
childModuleName: 'PM0 CH-2'
childModuleName: 'PM0 CH-3'
HTML
<mat-form-field class="permission-module">
<mat-select multiple ngDefaultControl formControlName="parentModule" placeholder="Select Package Files"
[compareWith]="compareFn">
<mat-option #selectAllParentModule (click)="selectAll()" [value]="0">
All
</mat-option>
<mat-option *ngFor="let module of parentModuleList"
[value]="{id:module.parenModuleId, name: module.parentModuleName}"
(click)="selectSingleItem(selectAllModule.viewValue, $event)">
{{module.parentModuleName}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="permission-module">
<mat-select multiple ngDefaultControl formControlName="childModule" placeholder="Select Package Files"
[compareWith]="compareFn">
<mat-option #selectAllChildModule (click)="selectAll()" [value]="0">
All
</mat-option>
<mat-option *ngFor="let module of childModuleList"
[value]="{id:module.childModuleId, name: module.childModuleName}"
(click)="selectSingleItem(selectAllModule.viewValue)">
{{module.childModuleName}}
</mat-option>
</mat-select>
</mat-form-field>
TS
@ViewChild('selectAllParentModule') private selectAllParentModule: MatOption;
@ViewChild('selectAllChildModule') private selectAllChildModule: MatOption;
parentModuleList = [
{ parenModuleId: 0, parentModuleName: 'Parent Module 0' },
{ parenModuleId: 1, parentModuleName: 'Parent Module 1' },
{ parenModuleId: 2, parentModuleName: 'Parent Module 2' },
];
childModuleList = [
{ childModuleId: 0, childModuleName: 'PM0 CH-1', parenModuleId: 0 },
{ childModuleId: 1, childModuleName: 'PM0 CH-2', parenModuleId: 0 },
{ childModuleId: 2, childModuleName: 'PM0 CH-3', parenModuleId: 0 },
{ childModuleId: 3, childModuleName: 'PM0 CH-4', parenModuleId: 0 },
{ childModuleId: 4, childModuleName: 'PM1 CH-1', parenModuleId: 1 },
{ childModuleId: 5, childModuleName: 'PM1 CH-2', parenModuleId: 1 },
{ childModuleId: 6, childModuleName: 'PM1 CH-3', parenModuleId: 1 },
{ childModuleId: 7, childModuleName: 'PM2 CH-1', parenModuleId: 2 },
{ childModuleId: 8, childModuleName: 'PM2 CH-2', parenModuleId: 2 },
{ childModuleId: 9, childModuleName: 'PM2 CH-3', parenModuleId: 3 },
{ childModuleId: 10, childModuleName: 'PM2 CH-4', parenModuleId: 3 },
];
testForm: FormGroup;
this.testForm = this.formBuilder.group({
parentModule: [''],
childModule: ['']
});
selectAll(): void {
/* select all for parent module */
console.log('select all', this.rolesForm.controls.parentModule.value);
if (this.selectAllParentModule.selected) {
this.rolesForm.controls.parentModule.patchValue([
...this.parentModuleList.map((item) => {
return {
id: item.parenModuleId,
name: item.parentModuleName
};
}), 0,
]);
} else {
this.rolesForm.controls.parentModule.patchValue([]);
}
/* select all for child module */
if (this.selectAllChildModule.selected) {
this.rolesForm.controls.childModule.patchValue([
...this.childModuleList.map((item) => {
return {
id: item.childModuleId,
name: item.childModuleName
};
}), 0,
]);
} else {
this.rolesForm.controls.childModule.patchValue([]);
}
}
selectSingleItem(event): any {
console.log('single select', this.rolesForm.controls.parentModule.value);
// this.loadChildModule = this.parentModuleList.find((parent: any) => parent.parentModuleName === event.target.value).parentModuleName;
/* single select for parent module */
if (this.selectAllParentModule.selected) {
this.selectAllParentModule.deselect();
return false;
}
if (this.rolesForm.controls.parentModule.value !== null &&
(this.rolesForm.controls.parentModule.value.length === this.parentModuleList.length)) {
this.selectAllParentModule.select();
}
/* single select for child module */
if (this.selectAllChildModule.selected) {
this.selectAllChildModule.deselect();
return false;
}
if (this.rolesForm.controls.childModle.value !== null &&
(this.rolesForm.controls.childModle.value.length === this.childModuleList.length)) {
this.selectAllChildModule.select();
}
}
compareFn(obj1: any, obj2: any): boolean {
return obj1 && obj2 ? obj1.moduleId === obj2.moduleId : obj1 === obj2;
}
I don't understand what am doing wrong.
Could someone pls help me on this? Hitting my head for past 1 1/2 days
Update
my another simplified version, which also not working. Getting id of undefined error when iterating inside checkUncheckAll() method
Upvotes: 1
Views: 291
Reputation: 57939
The fisrt step is simple the code adding a "checkAll" like this SO. It's only add a div between the select and the option
<mat-select ...>
<div class="mat-option">
<mat-checkbox ...>Select All</mat-checkbox>
</div>
<!--here the mat-options-->
<mat-option...>
<mat-option...>
</div>
We are going to use two auxiliars variables to control this "allChecked"
selectAllParent:boolean=false
allSelected:boolean=false
Futhermore, I choose only store the "id" in the FormControls (you're store the object, but this is unnecesary (*), you can simply create two functions)
getparent(){
const value=this.testForm.value.parentModule
return this.parentModuleList.filter(x=>value.includes(x.parenModuleId))
}
getchild(){
const value=this.testForm.value.childModule
return this.childModuleList.filter(x=>value.includes(x.childModuleId)).map(x=>(
{childModuleId:x.childModuleId,
childModuleName:x.childModuleName
}
))
}
And use if you need, e.g. when you want to send to a service or you want to store -tipical in submit function-
Remember that a multiple matselect store the values in an array
As we are using [(ngModel)]
-the variables don't belong to the formGroup- we need use [ngModelOptions]={standalone:true}
. For each one we need two functions, one to "toggle" the selection and another one that, when change the select, change the variables to show checked or not.
The second mat-select has all the options that has parenModuleId is included in the value of testForm.get('parentModule'): *ngIf="testForm.get('parentModule').value && testForm.get('parentModule').value.includes(child.parenModuleId))"
So our .html becomes like:
<form [formGroup]="testForm" (submit)="submit()">
<mat-form-field class="permission-module">
<mat-select (selectionChange)="selectionParentChange()"
multiple
ngDefaultControl
formControlName="parentModule"
placeholder="Select parent module"
>
<div class="mat-option">
<mat-checkbox
[(ngModel)]="selectAllParent"
[ngModelOptions]="{standalone: true}"
(change)="toggleAllSelection($event.checked)"
>Select All</mat-checkbox
>
</div>
<mat-option
*ngFor="let module of parentModuleList"
[value]="module.parenModuleId"
>
{{module.parentModuleName}}
</mat-option>
</mat-select>
</mat-form-field>
<br />
<mat-form-field class="permission-module">
<mat-select (selectionChange)="selectionChildChange()"
multiple
ngDefaultControl
formControlName="childModule"
placeholder="Select child modules"
>
<div class="mat-option">
<mat-checkbox [(ngModel)]="allSelected"
[ngModelOptions]="{standalone: true}"
(change)="toggleAllChild($event.checked)"
>Select All</mat-checkbox
>
</div>
<ng-container *ngFor="let child of childModuleList">
<mat-option *ngIf="testForm.get('parentModule').value &&
testForm.get('parentModule').value.includes(child.parenModuleId)"
[value]="child.childModuleId"
>
{{child.childModuleName}}
</mat-option>
</ng-container>
</mat-select>
</mat-form-field>
<br/>
<button mat-raised-button color="primary">Submit</button>
</form>
The auxiliars functions:
toggleAllSelection(checked: boolean) {
this.testForm
.get('parentModule')
.setValue(
checked ? this.parentModuleList.map((x) => x.parenModuleId) : null
);
}
toggleAllChild(checked: boolean) {
const parent = this.testForm.get('parentModule').value;
const value = checked
? this.childModuleList
.filter((x) => parent.includes(x.parenModuleId))
.map((x) => x.childModuleId)
: null;
this.testForm.get('childModule').setValue(value);
}
selectionParentChange() {
this.selectAllParent =
this.testForm.get('parentModule').value.length ==
this.parentModuleList.length;
}
selectionChildChange() {
const parent = this.testForm.get('parentModule').value;
const value = this.childModuleList.filter(
(x) => parent.includes(x.parenModuleId)
);
this.allSelected =
value.length == this.testForm.get('childModule').value.length;
}
The stackblitz
Update we need check if, when we has selected a childrenModule and deselect a ChildrenParent, to remove this values. So our function selectionParentChange() becomes like
selectionParentChange() {
this.selectAllParent = ...
//Here: we need check if there some selected in childModule that it's not
//in parent
const parent = this.testForm.get('parentModule').value;
const child=this.testForm.get('childModule').value;
const value=child.filter(x=>{
const child=this.childModuleList.find(c=>c.childModuleId==x)
return child && parent.includes(child.parenModuleId)
})
if (child!=value)
this.testForm.get('childModule').setValue(value)
}
(*)you can do it changing a bit the functions related
Upvotes: 2