I have a structure, like this
[{
title: "Section 1",
items: [{
title: 'Dashboard',
icon: 'tachometer-alt',
route: '/dashboard',
opened: false
},
{
title: 'Appointments',
icon: 'calendar-alt',
route: '/appointments',
opened: true
},
{
title: 'Orders',
icon: 'box',
route: '/orders',
opened: false,
children: [{
title: 'Orders submenu 1',
route: '/orders/sub1',
opened: false,
children: [{
title: 'Orders submenu 1 subsubmenu 1',
route: '/orders/sub1/sub1sub1'
}]
}]
}
]
}]
These are basically sections with menu items and every menu item could contain submenus, submenus have subsubmenus, etc.
I have a toggle function, which is getting a property array. I want to negate the variable that is "marked" by this array, so when I am getting an [0, 'items', 2, 'children', 0, 'opened'] array, the expected behaviour would be that the "Orders submenu 1" has its "opened" property set to "true".
The property indexer array is alterable too, so I can tweak that a little bit, if needed.
With Ramda, i can easly get the current value with R.path([0, 'items', 1, 'opened'], menu) but how can I set it to "true"?
Jsfiddle for example: https://jsfiddle.net/hurtonypeter/1tm4wcuo/
You can make use of lenses in Ramda to achieve this.
const togglePath = (path, obj) => R.over(R.lensPath(path), R.not, obj)
togglePath([0, 'items', 1, 'opened'], menu)
Related
I have no idea how to make this,
I have a loop for product List Item, but i really don't know how to link the shop item to shop name. I have tried many method but i really don't know how to link the item to the shop.
if productList.ShopId = shopList.id then use this shopList.title
SHOP NAME = shopList.title
const getProductList = function (productItem) {
const productListRender =
$('<div>').append($('<span>', {
html: shopName() <<<< SHOP NAME
}));
$.each(data.shopList), function shopName() {
this.text(shopList.title);
};
return productListRender;
}
const $product = $('#List')
$.each(data.productList, function (index, data) {
$product.append(getProductList(data));
});
Data JS is Below:
var data = {
productList: [
{
id: "62276197-6059-4c21-9b40-c5b1d277e85d",
link: "javascript:void(0)",
imgurl: "/img/upload/png/joyacart_000001_12032019.png",
text: 'Product 001',
goldMedal: false,
newItem: true,
newShop: true,
freeDelivery: true,
ShopId: '5cfb048c-86e8-4d2d-85bf-e4e9e1effdcb'
},
{
id: "59de8216-052d-4e51-9f7d-7e96642ded62",
link: "javascript:void(0)",
imgurl: "/img/upload/png/joyacart_000002_12032019.png",
text: 'Product 002',
goldMedal: true,
newItem: false,
newShop: true,
freeDelivery: true,
ShopId: '10eb4250-47d6-41ad-a429-f65e05836f26'
},
{
id: "59de8216-052d-4e51-9f7d-7e96642ded62",
link: "javascript:void(0)",
imgurl: "/img/upload/png/joyacart_000003_12032019.png",
text: 'Product 003',
goldMedal: true,
newItem: false,
newShop: true,
freeDelivery: true,
ShopId: '10eb4250-47d6-41ad-a429-f65e05836f26'
}],
shopList: [{
id: '5cfb048c-86e8-4d2d-85bf-e4e9e1effdcb',
title: 'Shop 001'
},
{
id: '10eb4250-47d6-41ad-a429-f65e05836f26',
title: 'Test Shop'
}]
}
Refine shopName as:
function shopName(shopId) {
// find the right shop by comparing shopId with each `shop.id`
// uses optional chaining to prevent errors
// returns undefineds if no match is found
return shopList.filter(shop => shop.id === shopId)?.[0]?.title;
}
const shopList = [{
id: '5cfb048c-86e8-4d2d-85bf-e4e9e1effdcb',
title: 'Shop 001'
},
{
id: '10eb4250-47d6-41ad-a429-f65e05836f26',
title: 'Test Shop'
}]
console.log(shopName('10eb4250-47d6-41ad-a429-f65e05836f26'));
console.log(shopName('222'));
everything seems to be in order in your data. only thing that i can see is that there was some mishap in your comparison method.
have you tried comparing them not with equal sign but rather treat them as string .
you could see this link for reference..
https://www.tutorialspoint.com/What-is-the-best-way-to-compare-two-strings-in-JavaScript
I'm generating some menu buttons dynamically (not sure if that's best practice)
script
items: [
{ title: 'Dashboard', icon: 'mdi-view-dashboard', route: '/' },
{ title: 'Register', icon: 'mdi-image', route: '/register' },
{ title: 'Login', icon: 'mdi-help-box', route: '/login' },
],
html
<v-list-item v-for="(item, i) in items" :key="i" link :to="{path: item.route}">
But what I want to do is hide the dashboard button until they have signed in. The user signed in value is kept in store.
$store.state.user.signedIn // true/false
How can I programmatically hide buttons depending on signed in value? I was trying to do this
{ title: 'Dashboard', icon: 'mdi-view-dashboard', route: '/', reqAuth: true }
But not sure how to get it working smoothly in the html. I'd also like to do the reverse later on the login/register buttons, if the user IS signed in then these should hide and a Logout button will come into play.
You have two options:
A) have separate menu arrays and display one or the other based on isLoggedIn. If you have any items showing in both cases, you'll need to place them in a third array and concat one of the first two with the third
B) have a boolean property on each menu item stating whether it should show when isLoggedIn or not. If you have menu items showing on both, you'll need either two props on each item (showWhenLoggedIn, showWhenLoggedOut - change them if too long) or, alternatively, you could make the show an array of booleans: show: [true, false] - first bool controlling whether it's shown when logged out, second when logged in).
Solution A) example (separate arrays):
Vue.config.devtools = false;
Vue.config.productionTip = false;
new Vue({
el: '#app',
data: () => ({
loggedInMenuItems: [
{ title: 'Dashboard', icon: 'mdi-view-dashboard', route: '/', show: [false, true] },
],
loggedOutMenuItems: [
{ title: 'Register', icon: 'mdi-image', route: '/register', show: [true, false] },
{ title: 'Login', icon: 'mdi-help-box', route: '/login', show: [true, false] },
],
permanentMenuItems: [
{ title: 'Terms and Conditions', icon: 'mdi-whatever', route: '/terms', show: [true, true] }
],
isLoggedIn: false
}),
computed: {
menuItems() {
return (this.isLoggedIn
? this.loggedInMenuItems
: this.loggedOutMenuItems
).concat(this.permanentMenuItems)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<label><input v-model="isLoggedIn" type="checkbox"> Is logged in</label>
<pre v-html="menuItems.map(m => m.title)"></pre>
</div>
Solution B) example:
Vue.config.devtools = false;
Vue.config.productionTip = false;
new Vue({
el: '#app',
data: () => ({
items: [
{ title: 'Dashboard', icon: 'mdi-view-dashboard', route: '/', show: [false, true] },
{ title: 'Register', icon: 'mdi-image', route: '/register', show: [true, false] },
{ title: 'Login', icon: 'mdi-help-box', route: '/login', show: [true, false] },
{ title: 'Terms and Conditions', icon: 'mdi-whatever', route: '/terms', show: [true, true] }
],
isLoggedIn: false
}),
computed: {
menuItems() {
return this.items.filter(item => item.show[Number(!!this.isLoggedIn)])
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<label><input v-model="isLoggedIn" type="checkbox"> Is logged in</label>
<pre v-html="menuItems.map(m => m.title)"></pre>
</div>
I personally prefer the second one, for brevity. I also find it a tad more elegant.
However, in large teams or in projects where code complexity needs to be kept to a minimum and code readability to a maximum, the first solution is often times preferred.
Lastly, second solution allows more flexibility for menu items order, although it's not a real issue (implementing an order attribute to each item would be trivial).
Note: obviously, isLoggedIn should come from state, not from component data fn. I placed it in data so you could easily test it here.
Your items property should be defined as computed property and add shown field in each item of items and use state value as its value in dashboard link :
computed :{
items(){
const signedIn=$store.state.user.signedIn;
return [
{ title: 'Dashboard', icon: 'mdi-view-dashboard', route: '/', shown:signedIn },
{ title: 'Register', icon: 'mdi-image', route: '/register',shown:!signedIn},
{ title: 'Login', icon: 'mdi-help-box', route: '/login' , shown:!signedIn},
]
}
}
in template add
<v-list-item v-for="(item, i) in items" :key="i" link v-if="item.shown" :to="{path: item.route}">
I have a TabPanel like this:
Ext.application({
name : 'Fiddle',
launch : function() {
Ext.create('Ext.TabPanel', {
items: [
{
title: "",
reference: 'tabpanel',
itemId: 'tab1',
items: [{
itemId: 'firstTab',
xclass: 'viewClass'
}],
}, {
title: "",
reference: 'tab2',
layout: 'fit',
items: [{
xclass: "view",
}]
}
]
in the xclass component there is the path of a class where is defined the view. In the view should be a button, after tap on it, the view should refresh and show another class, for example the view should be defined by 'viewClass2' and not anymore by 'viewClass' I'm imaging a function triggered on button tap like this:
Ext.getCmp('tab1').remove('firstTab');
Ext.getCmp('tab1').add({
itemId: 'firstTab',
xclass: 'viewClass2'
})
BUT I must use itemId and not id so I cannot use "Ext.getCmp" and i should use Ext.Component.query but I don't know how to manage remove and add operations
You have to get reference of the component first, then remove it.
Ext.create('Ext.TabPanel', {
id: 'myTabPanel',
items: [
{
title: "",
reference: 'tabpanel',
itemId: 'tab1',
items: [{
itemId: 'firstTab',
xclass: 'viewClass'
}],
}, {
title: "",
reference: 'tab2',
layout: 'fit',
items: [{
xclass: "view",
}]
}
];
var tabPanel = Ext.getCmp('myTabPanel');
var tab = tabPanel.getComponent("tab1");
// remove by id
tab.remove(tab.down("[itemId='firstTab']").id);
// remove by reference
tab.remove(tab.down("[itemId='firstTab']"));
I have got a nested structure which looks like this example:
let structure = [{
section: '1',
title: 'First lvl title',
children: [{
section: '1.1',
title: 'Second lvl title',
children: []
}, {
section: '1.2',
title: 'Second lvl title',
children: [{
section: '1.2.1',
title: 'Third lvl title',
children: []
}, {
section: '1.2.2',
title: 'Third lvl title',
children: []
}]
}, {
section: '1.3',
title: 'Second lvl title',
children: []
}]
}, {
section: '2',
title: 'First lvl title',
children: [{
section: '2.1',
title: 'Second lvl title',
children: []
}, {
section: '2.2',
title: 'Second lvl title',
children: []
}]
}, ...other objects]
As you can see, structure is similar to the table of contents - I have got objects which represents units and each unit has own section number, title and array with subsections.
Data characteristic is that I never know how many sections and how many nested subsections I have. I need to assume that I can get something around 2 000 objects (maybe more) with different configuration. Also, I cannot predict maximum nested level and it can be different for specific sections.
I am trying to find the most optimized way to represent this structure as HTML page. I am thinking about using ng-repeat but I don't know if it is a good idea since I don't know how many deep levels I will have. Also, after generating my HTML page, I can remove one section (for example section 1) and I need to recalculate section numbers for each other section and subsection (section 2 is now section 1, subsection 2.1 is now subsection 1.1, and so on). What is the best way to handle this operation on such a big amount of data?
You'll want a recursive template, wherein it won't matter what the structure is nor how many levels deep the data go. You can use a recursive custom directive or recursive ngInclude:
<script type="text/ng-template" id="categoryTree">
{{ category.title }}
<ul ng-if="category.categories">
<li ng-repeat="category in category.categories" ng-include="'categoryTree'">
</li>
</ul>
</script>
http://benfoster.io/blog/angularjs-recursive-templates
First of all, Im new at ExtJS. I wanted your help to let me know the best way to obtain a tree menu with n recursive items in it.
In example:
FOLDER
..FOLDER
....ITEM
....FOLDER
......ITEM
......ITEM
....FOLDER
...
Im following the best practises proposed by Sencha. I was able to do a tree menu with one level, but when trying to do it for n levels, it fails (in fact, the app works but shows infinite nodes of 1st level). Clearly the issue is the model definition of my menu item, see:
Ext.define('Dashboard.model.MenuItem',{
extend: 'Dashboard.model.AbstractMenuElement',
fields:
[
{name: 'content', type: 'string'},
{name: 'skeletonFlag', type: 'string'},
{name: 'fatherId', type: 'int'}
],
associations:
[
{type: 'hasMany', model: 'Dashboard.model.MenuItem', name: 'children', mapping: 'items'}
]
});
So this model recursively creates infinite nodes. But... do you know how should i model my menu item in order to achieve the recursive menu?
Thanks!
To display a tree-like structure in Ext JS, I think your best bet is to use Ext.model.TreeModel, Ext.model.TreeStore in conjunction with Ext.tree.Panel.
Here is an simple example to match the structure you mentioned in the question:
Ext.create('Ext.tree.Panel', {
store: Ext.create('Ext.data.TreeStore', {
root: {
text: 'Root Folder',
expanded: true,
children: [
{text: 'Folder 1', children: [
{text: 'Item 1', leaf: true}
]},
{text: 'Folder 2', expanded: true, children: [
{text: 'Item 2', leaf: true},
{text: 'Item 3', leaf: true}
]},
{text: 'Folder 3', children: []}
]
}
}),
renderTo: Ext.getBody()
});
You can view this example on Plunker.