I try to create sidebar component in React and I have a data structure like bellow
const links = [
{
label: 'Item-1',
url: '/item-1',
},
{
label: 'Item-2',
children: [
{
label: 'Item-2-1',
url: '/item-2-1',
},
{
label: 'Item-2-2',
url: '/item-2-2',
},
{
label: 'Item-2-3',
url: '/item-2-3',
},
],
},
{
label: 'Item-3',
children: [
{
label: 'Item-3-1',
url: '/item-3-1',
},
{
label: 'Item-3-2',
url: '/item-3-2',
},
],
},
];
So the problem is let's say the user changed URL and URL something like that http://localhost:90/item-2-3.
And I need to activate this sidebar item from the sidebar and it can be nested. So firstly I need to deactivate all other sidebar items (I don't want multiple selected items in the sidebar) and after that activate the selected sidebar item.
Because that (if I'm correct) I need update all tree items let's say I add active:false field to all JSON and after that I need to find the correct tree item from tree (URL === item-2-3) add active:true and also update all parent json to active:true (for there are look selected/opened)
So my question is am I correct if I correct how can write this code optimal way? :/
Actually, I want to create a function and when calling this function like that selectItemFromTree(links, '/item-2-2') I get results like in bellow.
const links = [
{
label: 'Item-1',
url: '/item-1',
active: false
},
{
label: 'Item-2',
active: true,
children: [
{
label: 'Item-2-1',
url: '/item-2-1',
active: false
},
{
label: 'Item-2-2',
url: '/item-2-2',
active: true,
},
{
label: 'Item-2-3',
url: '/item-2-3',
active: false
},
],
},
{
label: 'Item-3',
active: false,
children: [
{
label: 'Item-3-1',
url: '/item-3-1',
active: false
},
{
label: 'Item-3-2',
url: '/item-3-2',
active: false
},
],
},
];
You have to traverse the tree recursively and update all the active statuses.
const links=[{label:"Item-1",url:"/item-1"},{label:"Item-2",children:[{label:"Item-2-1",url:"/item-2-1"},{label:"Item-2-2",url:"/item-2-2"},{label:"Item-2-3",url:"/item-2-3"}]},{label:"Item-3",children:[{label:"Item-3-1",url:"/item-3-1"},{label:"Item-3-2",url:"/item-3-2"}]}];
const updateActiveLink = (links, url, parent = null) => {
for (link of links) {
link.active = false;
if (link?.children) {
updateActiveLink(link.children, url, link)
}
if (link.url === url) {
link.active = true;
if (!!parent) parent.active = true;
}
}
}
updateActiveLink(links, '/item-2-3');
console.log(links);
.as-console-wrapper {min-height: 100%!important; top: 0}
Note that this method will mutate the links array.
Related
This fiddle attempts to access the modified tree data and display it to console when a node is moved and/or dropped :
The tree in question is based on jqTree : https://mbraak.github.io/jqTree/
http://jsfiddle.net/adrianfiddleuser/ywo3z1rb/3/
But the alert is not firing.
fiddle src :
HTML :
<div id="tree1"></div>
javascript + jQuery :
var data = [
{
label: 'node1',
children: [
{ label: 'child1' },
{ label: 'child2' }
]
},
{
label: 'node2',
children: [
{ label: 'child3' }
]
}
];
$('#tree1').tree({
data: data,
autoOpen: true,
dragAndDrop: true,
"check_callback" : true
}).on("copy_node.jstree", function () {
alert("copy_node fires");
console.log(data)
}).on("move_node.jstree", function () {
alert("move_node fires");
console.log(data)
});
How to fire the alert and access the modified tree json structure ? I'm not using on correctly ?
I am trying to use the Inspector's when parameter in a list item. In this particular case, I want to hide the body text field of an item when the user uses a toggle button:
inputs: {
mylist: {
type: 'list',
label: 'List of items',
item: {
type: 'object',
properties: {
attrs: {
text: {
title: {
type: 'text',
label: 'Title',
index: 1
},
body: {
type: 'textarea',
label: 'Body',
index: 2,
when: { eq: {'????': false} } // what path should be used here?
}
},
toggles: {
toggleBody: {
defaultValue: false,
type: 'text',
label: 'Hide body',
index: 3
},
}
} // attrs
} // properties
} // item
}
}
I have verified through renderFieldContent:
renderFieldContent: function(options, path, value) {
if(path.endsWith('/attrs/text/body')){
console.log(path);
}
} // 1 item in the list outputs: mylist/0/attrs/toggles/toggleBody
That the list items paths follow the pattern `mylist/${i}/attrs/toggles/toggleBody` where ${i} is the item number...
I tried to reference mylist/0/attrs/toggles/toggleBody in the expression (so it would always reference the first item), but this also does not seem to work... Is there a way to reference a list item property's path?
From Rappid v3.2, one can place wildcards within the path.
'mylist/${index}/attrs/toggles/toggleBody'
A wildcard ${index} will be dynamically substituted for the actual index of the item inside which the when clause is being evaluated.
inputs: {
mylist: {
type: 'list',
label: 'List of items',
item: {
type: 'object',
properties: {
attrs: {
text: {
title: {
type: 'text',
label: 'Title',
index: 1
},
body: {
type: 'textarea',
label: 'Body',
index: 2,
when: {
eq: {
'mylist/${index}/attrs/toggles/toggleBody': false
},
// otherwise: { unset: true }
// Uncomment above to clear the body text
// from the model when the body is hidden
}
}
},
toggles: {
toggleBody: {
defaultValue: false,
// Changed to built-in toggle
type: 'toggle',
label: 'Hide body',
index: 3
}
}
}
}
}
}
}
I have these two array of objects
todos: [
{
id: 1,
name: 'customerReport',
label: 'Report send to customer'
},
{
id: 2,
name: 'handover',
label: 'Handover (in CRM)'
},
]
And:
todosMoreDetails: [
{
id: 1,
checked: false,
link: {
type: 'url',
content: 'http://something.com'
},
notes: []
},
{
id: 2,
checked: false,
link: {
type: 'url',
content: 'http://something.com'
},
notes: []
}
]
So that the final array of objects will be a combination of the two, based on the object ID, like below:
FinalTodos: [
{
id: 1,
checked: false,
link: {
type: 'url',
content: 'http://something.com'
},
notes: [],
name: 'customerReport',
label: 'Report send to customer'
},
{
id: 2,
checked: false,
link: {
type: 'url',
content: 'http://something.com'
},
notes: [],
name: 'handover',
label: 'Handover (in CRM)'
}
]
I tried with merge mergeAll and mergeWithKey but I am probably missing something
You can achieve this with an intermediate groupBy:
Transform the todosMoreDetails array into an object keyed by todo property ID using groupBy:
var moreDetailsById = R.groupBy(R.prop('id'), todosMoreDetails);
moreDetailsById is an object where the key is id, and the value is an array of todos. If the id is unique, this will be a singleton array:
{
1: [{
id: 1,
checked: false,
link: {
type: 'url',
content: 'http://something.com'
},
notes: []
}]
}
Now transform the todos array by merging each todo to it's details you retrieve from the grouped view:
var finalTodos = R.map(todo => R.merge(todo, moreDetailsById[todo.id][0]), todos);
An alternate more detailed way:
function mergeTodo(todo) {
var details = moreDetailsById[todo.id][0]; // this is not null safe
var finalTodo = R.merge(todo, details);
return finalTodo;
}
var moreDetailsById = R.groupBy(R.prop('id'), todosMoreDetails);
var finalTodos = todos.map(mergeTodo);
I guess merge is only used for arrays. Have a search for object "extend". Maybe storing the todo details not in seperate objects is the better solution.
Using jQuery? https://api.jquery.com/jquery.extend/
Using underscore? http://underscorejs.org/#extend
Native approach? https://gomakethings.com/vanilla-javascript-version-of-jquery-extend/
Using underscore:
var result = [];
var entry = {};
_.each(todos, function(todo) {
_.each(todosMoreDetails, function(detail) {
if (todo.id == detail.id) {
entry = _.extend(todo, detail);
result.push(entry);
}
}
});
return result;
I'm making an angular menu which is pretty complex (or will be later on), I've done most of the structuring but I'm stuck on this one thing. As you can see I have specified different menu options for different people depending on who's currently logged in. Right now I just want to show the menu for the "admin". I've put an ng-if that checks for an admin property, if it find one it should loop out it's contents as a main li item along with an ul containing the dropdown list items. However the way I've done it it shows everything excepts the admin content.. I've been trying different solutions for hours but I just can't get it right. What am I doing wrong?
I apologize for a lot of code but it's necessary to get a good overview of the structure I have.
<header>
<ul id="main-menu">
<li data-ng-repeat="menu in menus" id="{{menu.id}}">{{menu.title}}
<ul data-ng-repeat="submenu in menu" data-ng-if="menu.admin"> <--if admin
<li data-ng-repeat="subitem in submenu.items">{{subitem.title}}</li>
</ul>
</li>
</ul>
</header>
This is my controller containing all the information:
onlinePlatform.controller('onlinePlatformCtrl', function ($scope) {
$scope.menus = [
{
title: 'Startsida',
URL: 'home'
},
{
title: 'Nyheter',
URL: 'news'
},
{
title: 'Meddelanden',
items: [
{
title: 'Inkorg',
URL: 'inbox'
},
{
title: 'Skickade meddelanden',
URL: 'sentmsg'
},
{
title: 'Borttagna meddelanden',
URL: 'removedmsg'
}
],
URL: 'messages',
id: 'msg'
},
{
admin: {
title: 'Administration',
items: [
{
title: 'Hantera utbildningar',
URL: 'mngprograms'
},
{
title: 'Hantera kurser',
URL: 'mngcourses'
},
{
title: 'Hantera lärare',
URL: 'mngteachers'
},
{
title: 'Hantera studenter',
URL: 'mngstudents'
}
],
URL: 'administration',
id: 'administration'
},
teacher: {
title: 'Utbildning',
items: [
{
title: 'Kurser överblick',
URL: 'coursesoverview'
},
{
title: 'Sätt eller ändra betyg',
URL: 'editgrades'
},
{
title: 'Boka eller avboka lokaler',
URL: 'classroombooking'
}
],
URL: 'utbildning',
id: 'utbildning'
},
student: {
title: 'Mina sidor',
items: [
{
title: 'Mina kurser',
URL: 'mycourses'
},
{
title: 'Mitt schema',
URL: 'myschedule'
},
{
title: 'Mina betyg',
URL: 'mygrades'
},
{
title: 'Mina kontaktuppgifter',
URL: 'mycontactinfo'
},
{
title: 'Klasslista',
URL: 'classlist'
},
{
title: 'Anteckningar',
URL: 'notes'
}
],
URL: 'mypages',
id: 'mypages'
}
},
{
title: 'Forum',
URL: 'forum'
},
{
title: 'Kalender',
URL: 'calendar'
},
{
admin: {
title: 'Enkäter',
items: [
{
title: 'Skapa enkät',
URL: 'createsurvey'
},
{
title: 'Visa tidigare enkäter',
URL: 'previussurveys'
}
],
URL: 'surveys',
id: 'surveys'
},
teacher: {
title: 'Kontaktuppgifter',
items: [
{
title: 'Profil',
URL: 'profile'
},
{
title: 'Redigera kontaktuppgifter',
URL: 'editcontactinfo'
}
],
URL: 'contactinfo',
id: 'contactinfo'
},
student: {
title: 'Verktyg',
items: [
{
title: 'Ladda ner Dreamspark produkter',
URL: 'dreamspark'
}
],
URL: 'tools',
id: 'tools'
}
},
{
title: 'Hjälp',
items: [
{
title: 'Vanliga frågor',
URL: 'faq'
},
{
title: 'JENSEN kontaktuppgifter',
URL: 'contactinfo'
}
],
URL: 'help',
id: 'help'
}
]
});
Use this, ng-show=menu.admin. However, it's advised to use a method rather than just an assignment to check the authorization to show/hide elements, as the method will wait until Angular resolves it's value but the variable just consider it's current status to show/hide the element.
I have the following object
{ join: {} }
I'd like to find it's default object from the array below
[
{ login: { label: 'Login', url: '#login' } },
{ join: { label: 'Join', url: '#join', theme: 'a' } },
{ home: { label: 'none', icon: 'home', url: '#', theme: 'a' } }
]
I'd like to loop through the array and match the key, in this case 'join'.
This is what I have so far:
var butt_to_find = { join: {} }
var all_buttons = 'array above'
var matching = _.find(all_buttons, function(default_button){
return if default_butt key # 1 is the same as butt_to_find key # 1;
});
This is the first time I've used underscore after hearing so much about it.
Any help, more than welcome
var buttons = [
{ login: { label: 'Login', url: '#login' } },
{ join: { label: 'Join', url: '#join', theme: 'a' } },
{ home: { label: 'none', icon: 'home', url: '#', theme: 'a' } }
]
_.find(buttons, function (button) { return 'join' in button })
The problem is that you're using a suboptimal data structure. This would make more sense, and produce simpler code:
var buttons = {
login: {label: 'Login', url: '#login'},
join: {label: 'Join', url: '#join', theme: 'a'},
home: {label: 'none', icon: 'home', url: '#', theme: 'a'}
}
buttons.join // equivalent to the `_.find` line in the first example (but much simpler)
Perhaps you're using an array because the order of the buttons is important. In this case, I'd use an array of arrays:
var buttons = [
['login', {label: 'Login', url: '#login'}],
['join', {label: 'Join', url: '#join', theme: 'a'}],
['home', {label: 'none', icon: 'home', url: '#', theme: 'a'}]
]
_.find(buttons, function (button) { return button[0] === 'join' })
var matching =
( _.find
( all_buttons,
function (button)
{ return _.keys(butt_to_find)[0] in button;
}
)
);
where _.keys(butt_to_find) evaluates to ['join'] (an array containing the keys of butt_to_find), _.keys(butt_to_find)[0] evaluates to 'join' (the first element of said array), and _.keys(butt_to_find)[0] in button evaluates to either true or false, depending whether button contains 'join' as a key. (The in operator is a regular JavaScript operator, not something added by underscore.js.)
var def = {join: {}}
var defs = [
{ login: { label: 'Login', url: '#login' } },
{ join: { label: 'Join', url: '#join', theme: 'a' } },
{ home: { label: 'none', icon: 'home', url: '#', theme: 'a' } }
]
_.find(defs,function(item,key){
return _.has(item,_.keys(def)[0])
})
You can also switch to the lodash library (a drop in version of underscore) and do this
_.compact(_.pluck(defs,_.keys(def)[0]))