Reputation: 4035
I want to iterate over an array checking if a node has lower nodes and if the node is compatible with the user's role.
I thought I might use the "for (let entry of someArray)" to obtain each value of node in the array however the "someArray" is the return value of a function:
public menuList(): any {
return [
// SCHEDULER
{
route: ["", "scheduler"],
name: "scheduler",
moduleId: PLATFORM.moduleName("../components/scheduler/scheduler"),
title: "scheduler",
nav: true,
settings: {
icon: "user",
roles: ["Employee", "Admin"],
pos: "left"
}
},
// CLIENTS
{
route: "clients",
name: "clients",
moduleId: PLATFORM.moduleName("../components/clients/clientList/clientList"),
title: "Clients",
nav: true,
settings: {
icon: "user",
roles: ["Employee", "Admin"],
pos: "left",
nav: [
{
route: "clients/ClientsList",
name: "clientList",
moduleId: PLATFORM.moduleName("../components/clients/clientList/clientList"),
href: "#clients/clientsList",
title: "Client List",
settings: {
icon: "list",
roles: ["Employee", "Admin"],
}
},
{
settings: {
roles: ["Employee", "Admin"],
divider: true,
}
},
{
route: "clients/create",
name: "newClient",
moduleId: PLATFORM.moduleName("../components/clients/newClient/newClient"),
href: "#clients/Create",
title: "Create Client",
settings: {
icon: "user",
roles: ["Employee", "Admin"],
}
}
]
}
},
// JOBS
{
route: "jobs",
name: "jobs",
moduleId: PLATFORM.moduleName("../components/jobs/jobsList"),
title: "Jobs",
nav: true,
settings: {
icon: "list",
roles: ["Employee", "Admin"],
pos: "left"
},
},
// ACCOUNTING
// Accounting - 1st level route WITH SUBROUTES
{
route: "accounting",
name: "accounting",
moduleId: PLATFORM.moduleName("../components/accounting/ledgerEnquiry/ledgerEnquiry"),
title: "Accounting",
nav: true,
settings: {
icon: "usd",
roles: ["Employee", "Admin"],
pos: "left",
nav: [
{
title: "Creditor Cost Invoices",
icon: "tasks",
nav: true,
roles: ["Employee", "Admin"],
settings: {
nav: [
{
title: 'Creditor Payments',
icon: 'usd',
roles: ["Employee", "Admin"],
settings: {
nav: [
{
route: "accounting/creditorCostInvoices/payments/paymentsRegister",
name: "paymentsRegister",
moduleId: PLATFORM.moduleName("../components/accounting/creditorCostInvoices/payments/paymentsRegister/paymentsRegister"),
href: '#accounting/creditorCostInvoices/payments/paymentsRegister',
title: 'Payments Register',
settings: {
icon: 'list',
roles: ["Employee", "Admin"]
}
},
{
settings: {
roles: ["Employee", "Admin"],
divider: true,
}
},
{
route: "accounting/creditorCostInvoices/payments/creditorPromptPayments",
name: "promptPayments",
moduleId: PLATFORM.moduleName("../components/accounting/creditorCostInvoices/payments/creditorPromptPayments/creditorPromptPayments"),
href: '#accounting/creditorCostInvoices/payments/creditorPromptPayments',
title: 'Creditor Prompt Payments',
settings: {
icon: 'usd',
roles: ["Employee", "Admin"]
}
},
{
route: "accounting/creditorCostInvoices/payments/payOutstandingCreditorInvoices",
name: "payments",
moduleId: PLATFORM.moduleName("../components/accounting/creditorCostInvoices/payments/payOutstandingCreditorInvoices/payOutstandingCreditorInvoices"),
href: '#accounting/creditorCostInvoices/payments/payOutstandingCreditorInvoices',
title: 'Pay Outstanding Creditor Invoices',
settings: {
icon: 'edit',
roles: ["Employee"/*, "Admin"*/]
}
},
],
}
},
]
}
},
]
}
}
]
}
Scheduler is one node and client is another however client has an array with three nodes.
I want to iterate over this and create a new array where if any of the nodes do not satisfy the role value then that node is left out while still maintaining the depth structure of the original array.. So in the menuList() if iterate over this checking if the role setting has "Admin" and it doesnt it ignores that node and I get a filtered array with just those nodes that have "Admin" in it.
I did a monster "for loop" with nested "for loops" to try and capture this but failed.
I now thought I would use the let entry of someArray
but I am getting the error:
Error TS2349 (TS) Cannot invoke an expression whose type lacks a call signature. Type 'any[]' has no compatible call signatures
Not sure how to fix this however...
Is there a smart way to filter this array based on role.includes("Admin")etc while still maintaining the structure?
UPDATE
I finally managed to get this to work although I think there will be a multitude of better more succinct ways of doing it.
I took the view of dealing with each node completely before moving to the next and to do this I used a recursive function calling itself for each level.
public userMenu(userName: string, userRole: string): any {
let finishedRoleCheckedMenu = Array();
let userMenuElements = Array();
let returnedElement = {} as any;
for (const key in this.menuList()) {
returnedElement = this.processElement(this.menuList()[key], userRole);
if (returnedElement !== 'undefined') {
userMenuElements.push(returnedElement);
}
}
for (let count = 0; count < this.routeMenuItems.length; count++) {
if (!this.routeMenuItems[count].settings.divider) {
userMenuElements.push(this.routeMenuItems[count]);
}
}
return userMenuElements;
}
processElement(element: any, userRole: string) {
let testedElement = {} as any;
let settingsElement = {} as any;
let navElements = Array();
let navElement = {} as any;
if (element.settings.roles.includes(userRole)) {
for (const key in element) {
if (key === "settings") {
for (const settingsKey in element[key]) {
if (settingsKey === "nav") {
for (const navKey in element[key][settingsKey]) {
navElement = this.processElement(element[key][settingsKey][navKey], userRole); // recursive call.
if (navElement !== 'undefined') {
if (navElement.route) { // Collect only those elements with routes.
this.routeMenuItems.push(navElement); // For adding the total routes at the end.
}
navElements.push(navElement);
}
}
if (navElements.length > 0) {
settingsElement[settingsKey] = navElements;
}
} else {
settingsElement[settingsKey] = element[key][settingsKey];
}
}
testedElement[key] = settingsElement;
} else {
testedElement[key] = element[key];
}
}
return testedElement;
} else {
return 'undefined';
}
}
Upvotes: 4
Views: 165
Reputation: 771
The compiler is complaining about the caller code. Specifically whatever it plans on doing with the returned array. Reason for this is because you have specified the return type as "any". Perhaps return type of array may be more appropriate.
As far as filtering the array, I think you're on the right track, however you would be able to clean up the code a bit by using a map, or map/reduce pattern.
Upvotes: 1