jstree open_node not working on child nodes - javascript

When my checkbox jstree has finished loading, I wish to pre open the last opened nodes (not select_node). The open_node function only seems to work on the top most parent level nodes. I even tried iterating through the node and calling open_node and it still doesn't work. I have the following:
// Create instance for checkbox jstree.
$(function () {
$('#myTree').jstree({
"core": {
"themes": {
'name': 'default',
"variant": "small",
"icons": false
},
},
"checkbox": {
"keep_selected_style": false,
"three_state": false,
},
"plugins": ["checkbox"]
});
});
$("#myTree").bind('ready.jstree', function (event, data) {
var $tree = $(this);
$($tree.jstree().get_json($tree, {
"flat": true
})).each(function (index, value) {
// lastOpenedNode.value contains the id of the last opened node
if ( nodeWasLastOpened(this.id) == true)
// ONLY OPENS TOP MOST PARENT NODES
$("#myTree").jstree().open_node(this.id);
})
});
Please help.

There is a private method you could use for that, _open_to, that will open all nodes down to the one you want to be shown. Check code below and demo - Fiddle.
$("#myTree").jstree()._open_to( lastOpenedNode.value );
or
if ( nodeWasLastOpened(this.id) )
$("#myTree").jstree()._open_to( this.id );
})

Related

Fancytree activateKey setting focus then scrolling to top dependant on key

I am finding that activateKey is not setting focus correctly depending on the key/node used. It is first setting focus but then scrolling back up to the top of the tree with the active/selected node not in view.
The fiddle below works as expected:
http://jsfiddle.net/op5ga0zf/2/
This fiddle scrolls back up to the top of the tree, the only difference is the key value being used in activateKey:
http://jsfiddle.net/op5ga0zf/3/
I get the same behaviour in all browsers, am I doing something wrong or is this a bug?
$(function() {
var $orgtree = $('#orgtree');
$orgtree.fancytree({
autoScroll: true,
activate: function(event, data) {
data.node.setSelected(!data.node.isSelected())
},
checkbox: true,
extensions: ['filter'],
filter: {
autoExpand: true,
counter: false,
mode: "hide"
},
icon: false,
init: function(event, data) {
var key = $('#OrganisationID').val();
if (key !== '') {
data.tree.activateKey(key);
}
},
select: function(event, data) {
if (data.node.isSelected()) {
$('#OrganisationID').val(data.node.key);
} else {
$('#OrganisationID').val("");
}
},
selectMode: 1,
source: treeData,
tabindex: ""
});
$('#filter-orgtree').keyup(function() {
$orgtree.fancytree("getTree").filterNodes($(this).val());
});
});
This was a bug and will be fixed with release 2.30.

Treeview with nested inputs with VueJS

I'm trying to build a treeview component comporting inputs in order to change my source json.
The binding part seems to work fine but the hide/show action on branches is broken :
HTML :
<div id="app">
<tree :data="json" :link="json"></tree>
<p>Outside component :</p>
<pre>{{json}}</pre>
</div>
JS :
let json = {
nodeA: {
nodeA1 : "valueA1",
nodeA2 : "valueA2"
},
nodeB: "valueB",
nodeC: {
nodeC1 : "valueC1",
nodeC2 : "valueC2"
}
};
Vue.component('tree', {
name: 'treeview',
props: [
'data',
'link'
],
template: `<ul>
<li v-for="(val, key) in data">
<input type="text" v-if="isLeaf(val)" v-model=link[key]>
<span #click="toggle">{{key}}</span>
<tree v-if="!isLeaf(val)" v-show="show" :data="val" :link="link[key]">
</tree>
</li>
</ul>`,
data: function() {
return {
show: false
};
},
methods: {
isLeaf: function(node) {
return typeof node != 'object';
},
toggle: function() {
this.show = !this.show;
}
}
});
new Vue({
el: '#app',
data: {
json: json
}
});
https://codepen.io/anon/pen/EZKBwL
As you can see, a click on the first branch ("nodeA") activate both the first and the third branches...
I think the problem comes from the click that occurs on the parent component but I can't find a way to fix my code.
Your all the branches are hiding/showing together as you are using single variable show to hide and show both of those, You have to use different variable for each on node.
It will be impractical to have as many variables as number of nodes, but you can have a hash like following:
data: function() {
return {
show: {}
};
},
and change the toggle method to set the variable for each node by creating a key in this show hash for that node. You can use vm.$set for this which sets a property on an object. If the object is reactive, ensure the property is created as a reactive property and trigger view updates.
toggle: function(node) {
if(this.show[node]){
this.$set(this.show, node, false)
} else {
this.$set(this.show, node, true)
}
}
You need to do corresponding changes in HTML as well, which can be viewed in the working codepen here.
It happens because you bind all elements at the same parameter.
To toggle visibility individually for each element you need to store element states at it's own place like object's field or array.
But i guess better solution is toggle class on a target element by click and control visibility by css via class.
You may need a show field for each node to toggle their visibility separately, in my improved example, I'm using a data structure like this:
{
"nodeA": {
"value": {
"nodeA1": {
"value": "valueA1",
"show": false
},
"nodeA2": {
"value": "valueA2",
"show": false
}
},
"show": true
},
"nodeB": {
"value": "valueB",
"show": true
}
}
my template:
<ul>
<li v-for="(val, key) in data" v-show='val.show'>
<input type="text" v-if="isLeaf(val)" v-model='link[key].value'>
<span #click="toggle(val.value)">{{key}}</span>
<tree v-if="!isLeaf(val)" :data="val.value" :link="val.value">
</tree>
</li>
</ul>
methods:
{
isLeaf: function(node) {
return typeof node.value != 'object';
},
toggle: function(value) {
for (const nodeName in value) {
value[nodeName].show = !value[nodeName].show;
}
}
}

Enabling custom button (disabled by default) when row is selected

I have a DataTable displaying data for Branches with two custom buttons defined: Add and Update. They are initialized at the top of the Javascript section
var buttons;
var tblBranch;
$.fn.dataTable.ext.buttons.add = {
className: 'button-add',
text: "Add Branch",
action: function (dt) {
onBtnAddClicked()
}
};
$.fn.dataTable.ext.buttons.update = {
className: 'button-update',
text: "Update",
action: function (dt) {
onBtnUpdateClicked()
}
};
I'd like to disable the Edit button on page load and only enable it to be clickable when a row has been selected. Problem is, I'm using custom buttons and I can't find anything on datatables.net about how to enable/disable them depending on conditions. So far what I've tried is this:
tblBranch = $("#tblBranches").DataTable({
dom: 'Blfrtip',
buttons: {
buttons :[
'add', 'update'
]
}
select: true;
})
$("#tblBranches tbody").on('click', 'tr', function () {
if (tblBranch.row(this).hasClass('selected')) {
$('button-update').removeClass("DTTT_disabled");
}
else {
table.$('tr.selected').removeClass('selected');
$('button-update').addClass("DTTT_disabled");
}
});
But I don't know what the code to disable the Edit button when the page loads should be like, and I've looked here, here, here and here.
Thanks for any guidance.
The last link you are referring to hold the solution you are looking for. But the documentation is a little bit vague, guess it need a solid example. It is not clear how you create the buttons (you show both methods) but below is an inline example, it would work with constructor as well. Simply give the button a class, like .edit and set it to disabled from start :
var table = $('#example').DataTable( {
dom: 'Bfrtip',
select: true,
buttons: [
{
text: 'Edit',
className: 'edit',
enabled: false,
action: function (e, dt, node, config) {
//do something
}
},
{
text: 'Add',
action: function (e, dt, node, config) {
//do something
}
}
]
})
Then use the Select plugins select and deselect events to update the enabled status of the .edit button :
$('#example').on('select.dt deselect.dt', function() {
table.buttons( ['.edit'] ).enable(
table.rows( { selected: true } ).indexes().length === 0 ? false : true
)
})
demo -> https://jsfiddle.net/pmee6w2L/

Kendo Grid Child -> using CRUD toolbar

My problem is that I Have Hierarchical grid (Master and Child) let say I Have a Department Grid it contains List of Employee Grid, and they both use same datasource.
Here's my GridChild Code:
function detailInit (e){
var msterRow = e.sender.items().index(e.masterRow).toString();
var grid = $("<div id='childGrid"+msterRow+"'
class=childGrid'/>").appendTo(e.detailCell).kendoGrid({
data: e.data.DeptEmployees,
schema: {
model: { fields: { foo: {--skip--}, bar: {--skip--} } }
},
toolbar: ["create", "cancel", "save"],
editable: "popup",
columns: [ --skip--]
save: function(e){
ajaxUpdateDepartment(msterRow, this.dataSource.data());
}
})
As you can see i use data: e.data.DeptEmployees, as child data source to fetch data.
Now I'm stacked in how can I update the child data source?
What I have Tried:
I add child's dataSource.transport for updates, but my child grid keeps on loading.
So I end up configuring the save: function (e) and simply send all data source of the current child but popup editor didn't close at all. And I'm having difficulty to refresh the child data source.
I also attempt to convert my Master and Child Grid to ASP Razor but there was no definite example if how could I handle it in back end, and also my child grid contains drop down grid, so that would be a big re-do. And I also don't know if how can I pass customize parameter through it
I am desperate, I can't find any working reference except this one. but it's using odata, and I dont have child id to use as reference, since I am only using list which I retrieve in a user event.
Please help :'( I'm taking too much time for this one.
The solution is to define a transport properties, in order to fetch data from master, I only need to define the data and convert that to Jason.
take a look of these code:
function detailInit (e){
var msterRow = e.sender.items().index(e.masterRow).toString();
var grid = $("<div id='childGrid"+msterRow+"'
class=childGrid'/>").appendTo(e.detailCell).kendoGrid({
//data: e.data.ChildDetails,
transport: {
read: function (o) {
console.log("child read");
var data = e.data.ChildDetails.toJSON();
o.success(data);
},
update: function (o) {
console.log("child update");
var data = o.data,
arentItem = findByID(data.id);
for (var field in data) {
if(!(field.indexOf("_") === 0)){
arentItem[field] = data[field];
}
}
e.data.dirty = true;
saveChild(record, "#suffix", msterRow, "update");
o.success();
},
destroy: function (o) {
var parentItem = findByID(o.data.id);
preventBinding = true;
e.data.ChildDetails.results.remove(parentItem);
o.success();
saveChild(record, "#suffix", msterRow, "destroy");
},
create: function (o) {
console.log("child create");
var record = o.data;
record.id = index;
index++;
saveChild(record, "#suffix", msterRow, "create");
o.success(record);
}
},
schema: {
model: { fields: { foo: {--skip--}, bar: {--skip--} } }
},
toolbar: ["create", "cancel", "save"],
editable: "popup",
columns: [ --skip--]
}
Here's the working dojo snippet

Add an "All" item to kendo ui listview populated by a remote datasource

I am building a website using MVC 4, Web API, and Kendo UI controls.
On my page I am using a Kendo UI Listview to filter my grid. I'm trying to add an "ALL" option as the first item in my listview.
Here is the listview:
var jobsfilter = $("#jobfilter").kendoListView({
selectable: "single",
loadOnDemand: false,
template: "<div class='pointercursor' id=${FilterId}>${FilterName}</div>",
dataSource: filterDataSource,
change: function (e) {
var itm = this.select().index(), dataItem = this.dataSource.view()[itm];
if (dataItem.FilterId !== 0) {
var $filter = new Array();
$filter.push({ field: "JobStatusId", operator: "eq", value: dataItem.FilterId });
jgrid.dataSource.filter($filter);
} else {
jgrid.dataSource.filter({});
}
}
});
Here is my datasource:
var filterDataSource = new kendo.data.DataSource({
transport: {
read: {
url: "api/Filter"
}
},
schema: {
model: { id: "FilterId" }
}
});
I have tried a few different methods to make this happen:
I can make it work if I attach it to a button - but I need it there
when the data loads.
If I add it to the dataBound event of the listview, it causes the
databound event to go into a loop and adds the item a bunch (IE) or kills the browser (firefox). Adding preventDefault did nothing.
I've read up on adding a function to the Read paramter of the
datasource, but I think that is simply not the correct place to do
it.
Based on what I've read, I think that I should be able to do it in the dataBound event of the listview and that my implementation is incorrect. Here is the listview with dataBound event added that crashes my browser (Firefox) - or adds about 50 "All" items to the listview (IE).
var jobsfilter = $("#jobfilter").kendoListView({
selectable: "single",
loadOnDemand: false,
template: "<div class='pointercursor' id=${FilterId}>${FilterName}</div>",
dataSource: {
transport: {
read: {
url: "api/Filter"
}
}
},
dataBound: function (e) {
var dsource = $("#jobfilter").data("kendoListView").dataSource;
dsource.insert(0, { FilterId: 0, FilterName: "All" });
e.preventDefault();
},
change: function (e) {
var itm = this.select().index(), dataItem = this.dataSource.view()[itm];
if (dataItem.FilterId !== 0) {
var $filter = new Array();
$filter.push({ field: "JobStatusId", operator: "eq", value: dataItem.FilterId });
jgrid.dataSource.filter($filter);
} else {
jgrid.dataSource.filter({});
}
}
});
Any help would be greatly appreciated.
Why don't you add it server-side?
Anyway, if you want to do it in dataBound, just check whether it exists and only add if it doesn't:
dataBound: function (e) {
var dsource = this.dataSource;
if (dsource.at(0).FilterName !== "All") {
dsource.insert(0, {
FilterId: 0,
FilterName: "All"
});
}
},
As an explanation to the problem you're seeing: you're creating an infinite loop since inserting an element in the data source will trigger the change event and the list view will refresh and bind again (and thus trigger dataBound).
You could also encapsulate this in a custom widget:
(function ($, kendo) {
var ui = kendo.ui,
ListView = ui.ListView;
var CustomListView = ListView.extend({
init: function (element, options) {
// base call to widget initialization
ListView.fn.init.call(this, element, options);
this.dataSource.insert(0, {
FilterId: 0,
FilterName: "All"
});
},
options: {
name: "CustomListView"
}
});
ui.plugin(CustomListView);
})(window.jQuery, window.kendo);

Categories

Resources