ES6 get properties from nested objects - javascript

i'm doing a project with VueJS, Lodash and Babel, and my problem is that i need to populate a sidebar in my app with routes from a routes.js file. Problem is i don't really know how i can retrieve the data i need.
export default [
{
path: "/admin/login",
name: "admin.login",
component: Login
},
{
path: "/admin",
component: MasterLayout,
meta: { auth: true },
children: [
{
path: "/admin/dashboard",
alias: "/",
menu: { text: "", icon: ""},
name: "admin.dashboard",
component: DashboardIndex
}
]
},
{
path: '/403',
name: 'error.forbidden',
component: ErrorForbidden
},
{
path: "*",
name: 'error.notfound',
component: ErrorPageNotFound
}
];
That is my routes file, i basically need to iterate over each route, check if it has children or a menu property, if it has a menu it gets added to my list of sidebar items, and it's children get added as well with their own menu properties being withdrawn
In the end i'd like to get something like the following
sidebarItems: [
{
name: 'Dashboard',
icon: 'dashboard',
name: 'admin.dashboard'
},
{
name: 'OtherRoute',
icon: 'other-route',
name: 'admin.other-route',
children: [
{
name: 'SubRoute',
icon: 'sub-route',
name: 'admin.sub'
}
]
}
]
This should only account for the routes that contain a menu property.

You basically need iterate recursive over the array, so please try this:
function iterateArrayRoutes(routeArray) {
if(routeArray instanceof Array) {
var routeFormatted = routeArray.reduce(function(last, route){
if (route.hasOwnProperty("menu")) {
var item = {
name: route.name,
icon: route.menu.icon
};
if(route.hasOwnProperty("children") && route.children.length > 0) {
item.children = iterateArrayRoutes(route.children);
}
last.push(item);
}
return last;
}, []);
return routeFormatted;
}
};
Here you have a Demo https://jsfiddle.net/vm1hrwLL/

Related

Map an nested arrays and return matched ones

I know, there are already tons of same questions i saw some of them, but couldn't get full answer.
So, I have an array something like this (simplified for demonstration):
// links.js
const links = [
{
name: 'Page 1',
path: '/page-1'
},
{
name: 'Page-2',
subpages:[
{ name: 'Page (2-1)', path: '/page-2-1' },
{ name: 'Page (2-2)', path: '/page-2-2' }
]
},
{
name: 'Page 3',
path: '/page-3'
},
{
name: 'Page 4',
subpages:[
{ name: 'Page (4-1)', path: '/page-4-1' },
{ name: 'Page (4-2)', path: '/page-4-2' },
{ name: 'Page (4-3)', path: '/page-4-3' }
]
},
...
]
export default links
The above object is menu-links data, i render them on the screen for nagivating between pages and subpages is dropdown. They have either path or subpages, not both and there might be more nested.
There are 2 tasks i want help with.
First:
Every page of my site has a title and most of them is same as its name property shown above.
So, i have a function rendered on every page that returns the pathname of the current route, so what i want is to map through the links and get the name of matched path.
For example, if i give /page-4-1, i wanna get the name property of the matched path, So that is name: Page 4
Second
This time, it is something like a breadcrumb, If i give ['/page-1', '/page-2-1', '/page-4-2'], i wanna get:
[
{
name: 'Page 1',
path: '/page-1'
},
{
name: 'Page (2-1)',
path: '/page-2-1'
},
{
name: 'Page (4-2)',
path: '/page-4-2'
},
]
There will be cases where there might not be a matched result, in that case i would like to insert {name: document.body.title, path: null}
I tried
i'm using Nextjs
import { useRouter } from 'next/router'
import links from 'links.js'
const router = useRouter()
const splitted = router.asPath
.split('/')
.filter(
(sp) =>
sp !== ''
)
cost ready = []
for (let sp = 0; sp < splitted.length; sp++) {
for (let ln = 0; ln < links.length; ln++) {
if (links[ln].path) {
if (links[ln].path === '/' + splitted[sp]) {
ready.push(links[ln])
}
} else {
for (let sb = 0; sb < links[ln].sublinks.length; sb++) {
if (links[ln].sublinks[sb].path === '/' + splitted[sp]) {
ready.push(links[ln].sublinks[sb])
}
}
}
}
}
This is partly working but is messy, there should be a better ways with map, filter and find but i couldn't succeed with my tries on them.
Thank you in advance for your help!
EDIT:
Oops! my question had a big mistake, the links object only contains path key, not conditional path and link.
const links = [
{
name: 'Page 1',
path: '/page-1'
},
{
name: 'Page-2',
subpages:[
{ name: 'Page (2-1)', path: '/page-2-1' },
{ name: 'Page (2-2)', path: '/page-2-2' }
]
},
{
name: 'Page 3',
path: '/page-3'
},
{
name: 'Page 4',
subpages:[
{ name: 'Page (4-1)', path: '/page-4-1' },
{ name: 'Page (4-2)', path: '/page-4-2' },
{ name: 'Page (4-3)', path: '/page-4-3' }
]
},
];
const findPathObj = (path,links) => {
let result = null;
for(const item of links){
if(item.path == path) return item;
if(item.subpages) result = findPathObj(path, item.subpages)
if(result) break;
}
return result;
}
const findPageName = (path,links) => findPathObj(path,links)?.name;
const findBreadcrumb = (pathes, links) => pathes.map(path => findPathObj(path,links) || {name: document.title, path: null});
console.log(findPageName('/page-4-1', links));
console.log(findBreadcrumb(['/page-1', '/page-2-1', '/page-4-2'],links))
For your first question try the following
const links = [
{
name: "Page 1",
path: "/page-1",
},
{
name: "Page-2",
subpages: [
{ name: "Page (2-1)", path: "/page-2-1" },
{ name: "Page (2-2)", path: "/page-2-2" },
],
},
{
name: "Page 3",
link: "/page-3",
},
{
name: "Page 4",
subpages: [
{ name: "Page (4-1)", link: "/page-4-1" },
{ name: "Page (4-2)", link: "/page-4-2" },
{ name: "Page (4-3)", link: "/page-4-3" },
],
},
];
// Find out function
// Level must 0 at beginning
function findout(pages, search, level = 0) {
for (const page of pages) {
if (page.link === search || page.path === search) {
if (level === 0) {
return page.name;
}
return true;
}
if (Array.isArray(page.subpages)) {
if (findout(page.subpages, search, level + 1)) {
if (level === 0) {
return page.name;
}
return true;
}
}
}
return false;
}
console.log(findout(links, "/page-4-3"))
The second question I suggest this
const links = [
{
name: "Page 1",
path: "/page-1",
},
{
name: "Page-2",
subpages: [
{ name: "Page (2-1)", path: "/page-2-1" },
{ name: "Page (2-2)", path: "/page-2-2" },
],
},
{
name: "Page 3",
link: "/page-3",
},
{
name: "Page 4",
subpages: [
{ name: "Page (4-1)", link: "/page-4-1" },
{ name: "Page (4-2)", link: "/page-4-2" },
{ name: "Page (4-3)", link: "/page-4-3" },
],
},
];
function findout2(pages, search, result = []) {
for (const page of pages) {
if (typeof page.link === "string" && search.includes(page.link)) {
result.push({ name: page.name, link: page.link });
} else if (typeof page.path === "string" && search.includes(page.path)) {
result.push({ name: page.name, path: page.path });
}
if (Array.isArray(page.subpages)){
findout2(page.subpages, search, result)
}
}
return result
}
console.log(findout2(links, ['/page-1', '/page-2-1', '/page-4-2']))

Convert an array to a component in React

In the project there is an array of objects used for populating the breadcrumb:
export const BREADCRUMBS_LIST = [
{ label: 'Home', path: '/', active: false },
{ label: 'Account', path: '/accounts', active: false },
{ label: 'This Account', path: '/accounts', active: true }
];
it is used to populate the list in the Breadcrumbs component:
import { BREADCRUMBS_LIST } from './...'
...
<Breadcrumbs list={BREADCRUMBS_LIST} />
Everything works fine.
The problem appears when we need to translate those labels based on the user's language. For this, we are using react-intl.
So, I transformed the original array into a component of this form:
import { useIntl } from 'react-intl';
export const BreadcrumbsList = () => {
const intl = useIntl();
return [
{ label: intl.formatMessage({ id: 'Home' }), path: '/', active: false },
{
label: intl.formatMessage({ id: 'Account' }),
path: '/accounts',
active: false
},
{
label: intl.formatMessage({ id: 'This Account' }),
path: '/accounts',
active: true
}
];
};
and use it like this:
<Breadcrumbs list={BreadcrumbsList} />
it seems to be wrong because it returns an error saying:
Cannot read property 'map' of undefined.
In that component, the list was used with map: {list.map(({path, label, active}, index) => {...})
Any ideas how to solve this problem?
Your BreadcrumbsList is actually a custom hook, in order to stick with the Rules of Hooks you need to call it on component's level:
// Add "use" prefix as its a custom hook
const useBreadcrumbsList = () => {
const intl = useIntl();
return [
{ label: intl.formatMessage({ id: "Home" }), path: "/", active: false },
{
label: intl.formatMessage({ id: "Account" }),
path: "/accounts",
active: false,
},
{
label: intl.formatMessage({ id: "This Account" }),
path: "/accounts",
active: true,
},
];
};
// Usage
const Component = () => {
const breadcrumbsList = useBreadcrumbsList();
return <Breadcrumbs list={breadcrumbsList} />;
};

after login when click hardware back button navigated to login page how to prevent it in ionic 4

const routes: Routes = [
{
path: '',
component: TabsPage,
children: [
//new page add
{
path: 'home',
children: [
{
path: '', loadChildren: () => import('../AllPages/home/home.module').then(m => m.HomePageModule)
},
{
path: 'new-arrival',
children: [
{
path: '',
loadChildren: () => import('../AllPages/new-arrival/new-arrival.module').then(m => m.NewArrivalPageModule)
},
{
path: 'product-item-list',
children: [
{
path: '',
loadChildren: () => import('../Allpages/product-item-list/product-item-list.module').then(m => m.ProductItemListPageModule)
},
{
path: 'product-details',
children: [
{
path: '',
loadChildren: () => import('../Allpages/product-details/product-details.module').then(m => m.ProductDetailsPageModule)
},
]
}
]
}
]
},
Use Ionic NavController instead of Angular Router.
Then, use the navigateRootmethod.
Going root means that all existing pages in the stack will be removed, and the navigated page will become the single page in the stack.
import { NavController } from '#ionic/angular';
...
constructor(private navController: NavController) {}
...
this.navController.navigateRoot('/tabs/home');

vuepress additionalPages added to sidebar

I'm trying to create an AdditionalPage and add it inside the navbar.
I cannot find out how to make it happen:
This is my plugin:
async additionalPages() {
...
errorsPages.push({
path: '/errors',
content: '## Errors',
frontmatter: {
sidebar: true,
},
})
return errorsPages
When I only do this, of course no sidebar are present. This is because I did not add my /errors page inside the navbar.
When I try to add it to
config.js in the sidebar option:
'sidebar': {
'/references/': [
{
title: '📒 API References',
path: '/references/',
collapsable: false,
children: [
'/references/indexes',
'/references/keys',
{
title: 'Errors',
path: '/errors/',
collapsable: false,
},
]
}
]
}
Then the build failes.
Any idea how to add my additionalpages inside my sidebar ?
For add groups in sidebar, try this code:
sidebar: {
'/guide/': [
{
title: 'Group Guide',
children: ['', 'introduction-guide', 'details-guide'],
},
],
'/api/': [
{
title: 'Group API',
children: ['', 'introduction-api', 'details-api'],
},
],
},
The introduction-guide and details-guide are files presents in guide directory.
The introduction-api and details-api are files presents in api directory.
For more details: https://vuepress.vuejs.org/theme/default-theme-config.html#sidebar-groups

How to properly create dictionaries for each record in an api call in JS?

I am trying to create a dictionary for each record returned in an API call.
my broken code:
import lazyLoading from './lazyLoading'
// get records from api call
created() {
axios.get('http://localhost:8080/api/tools/')
.then(response => {
this.json_data = response.data
console.log(this.json_error)
})
.catch(error => {
console.log(error);
})
export default {
name: 'test',
meta: {
icon: 'fa-android',
expanded: false
},
const children = [];
json_data.forEach(item => {
const dict = {
name: item.name,
path: item.path,
meta: {
label: item.label,
link: item.link,
},
component: lazyLoading('testitem/basic'),
}
children.push(dict);
});
}
desired result:
export default {
name: 'test',
meta: {
icon: 'fa-android',
expanded: false
},
children: [
{
name: 'test',
path: 'test',
meta: {
label: 'test',
link: 'test'
},
component: lazyLoading('test/basic')
},
{
name: 'test',
path: 'test',
meta: {
label: 'test',
link: 'test'
},
component: lazyLoading('test/Basic')
},
{
name: 'test',
path: 'test',
meta: {
label: 'test',
link: 'test'
},
component: lazyLoading('test/Basic')
}
]
(obviously 'test' would be replaced what is returned in the api). The main problem is I don't know how to dynamically create the dictionarys. I also have no idea how to view/troubleshoot my axios request. I assumed console.log would spit out the object into the chrome dev tools under console section but I don't see the object there. I'm completely new to javascript so maybe I'm not looking in the correct spot.
Also I'm getting this error:
Module build failed: SyntaxError: 'import' and 'export' may only appear at the top level
So where do I put my api request if I cannot put it at the top?

Categories