jquery fancytree load and check all child nodes on check - javascript

I'm using the fancytree plugin to create a treeview. All of my data is loaded on the expand via ajax and json. I also have code in to load child nodes if a user checks a parent node as they aren't technically loaded if that node isn't expanded first.
My problem is, if you have a parent child relationship 3 levels deep, and expand to level 2, the third level doesn't get checked.
In essence I have something like this
Parent1
-->Child1
---->Child1-1
---->Child1-2
---->Child1-3
-->Child2
Now, if you never expand parent and check it, the nodes all get loaded and checked. However, if you expand parent 1 to show child 1 and 2, then check parent1 the child nodes 1-1 through 3 never get loaded or checked. Here is the code I have to load the child nodes on check, what am I missing.
select: function (event, data) { //here's where I need to load the children for that node, if it has them, so they can be set to checked
var node = data.node;
//alert(node.key);
if (node.isSelected()) {
if (node.isUndefined()) {
// Load and select all child nodes
node.load().done(function () {
node.visit(function (childNode) {
childNode.setSelected(true);
});
});
} else {
// Select all child nodes
node.visit(function (childNode) {
childNode.setSelected(true);
});
}
// Get a list of all selected nodes, and convert to a key array:
var selKeys = $.map(data.tree.getSelectedNodes(), function (node) {
treeHash[node.data.treeItemType + node.key] = node.key;
});
}
Here is my full JS just for reference
if ($("entityTree") != null) {
$(function () {
// Create the tree inside the <div id="tree"> element.
$("#entityTree").fancytree({
source: { url: "/Home/GetTreeViewData", cache: true }
, checkbox: true
, icons: false
, cache: true
, lazyLoad: function (event, data) {
var node = data.node;
data.result = {
url: "/Home/GetTreeViewData/" + node.key.replace(node.data.idPrefix, "")
, data: { mode: "children", parent: node.key }
, cache: true
};
}
, renderNode: function (event, data) {
// Optionally tweak data.node.span
var node = data.node;
var $span = $(node.span);
if (node.key != "_statusNode") {
$span.find("> span.fancytree-expander").css({
borderLeft: node.data.customLeftBorder
//borderLeft: "1px solid orange"
});
}
}
, selectMode: 3
, select: function (event, data) { //here's where I need to load the children for that node, if it has them, so they can be set to checked
var node = data.node;
//alert(node.key);
if (node.isSelected()) {
if (node.isUndefined()) {
// Load and select all child nodes
node.load().done(function () {
node.visit(function (childNode) {
childNode.setSelected(true);
});
});
} else {
// Select all child nodes
node.visit(function (childNode) {
childNode.setSelected(true);
});
}
// Get a list of all selected nodes, and convert to a key array:
var selKeys = $.map(data.tree.getSelectedNodes(), function (node) {
treeHash[node.data.treeItemType + node.key] = node.key;
});
}
else {
delete treeHash[node.data.treeItemType + node.key];
//alert("remove " + node.key);
}
for (var i in treeHash) {
alert(treeHash[i]);
}
}
, strings: {
loading: "Grabbing places and events…",
loadError: "Load error!"
},
})
});
}

Try this and let me know if works. We need to load the undefined child of child nodes which are loaded already.
if (node.isSelected()) {
if (node.isUndefined()) {
// Load and select all child nodes
node.load().done(function () {
node.visit(function (childNode) {
childNode.setSelected(true);
});
});
} else {
// Select all child nodes
node.visit(function (childNode) {
childNode.setSelected(true);
if (childNode.isUndefined()) {
// Load and select all child nodes
childNode.load().done(function () {
childNode.visit(function (itschildNode) {
itschildNode.setSelected(true);
});
});
}
});
}
}

Related

How to run 2 js functions

I have 2 function that I am trying to run, one after another. For some reason they both run at the same time, but the second one does not load properly. Is there a way to run the first function wait then run the second function?:
//run this first
$('#abc').click(function() {
$('.test1').show();
return false;
});
//run this second
(function ($) {
"use strict";
// A nice closure for our definitions
function getjQueryObject(string) {
// Make string a vaild jQuery thing
var jqObj = $("");
try {
jqObj = $(string)
.clone();
} catch (e) {
jqObj = $("<span />")
.html(string);
}
return jqObj;
}
function printFrame(frameWindow, content, options) {
// Print the selected window/iframe
var def = $.Deferred();
try {
frameWindow = frameWindow.contentWindow || frameWindow.contentDocument || frameWindow;
var wdoc = frameWindow.document || frameWindow.contentDocument || frameWindow;
if(options.doctype) {
wdoc.write(options.doctype);
}
wdoc.write(content);
wdoc.close();
var printed = false;
var callPrint = function () {
if(printed) {
return;
}
// Fix for IE : Allow it to render the iframe
frameWindow.focus();
try {
// Fix for IE11 - printng the whole page instead of the iframe content
if (!frameWindow.document.execCommand('print', false, null)) {
// document.execCommand returns false if it failed -http://stackoverflow.com/a/21336448/937891
frameWindow.print();
}
// focus body as it is losing focus in iPad and content not getting printed
$('body').focus();
} catch (e) {
frameWindow.print();
}
frameWindow.close();
printed = true;
def.resolve();
}
// Print once the frame window loads - seems to work for the new-window option but unreliable for the iframe
$(frameWindow).on("load", callPrint);
// Fallback to printing directly if the frame doesn't fire the load event for whatever reason
setTimeout(callPrint, options.timeout);
} catch (err) {
def.reject(err);
}
return def;
}
function printContentInIFrame(content, options) {
var $iframe = $(options.iframe + "");
var iframeCount = $iframe.length;
if (iframeCount === 0) {
// Create a new iFrame if none is given
$iframe = $('<iframe height="0" width="0" border="0" wmode="Opaque"/>')
.prependTo('body')
.css({
"position": "absolute",
"top": -999,
"left": -999
});
}
var frameWindow = $iframe.get(0);
return printFrame(frameWindow, content, options)
.done(function () {
// Success
setTimeout(function () {
// Wait for IE
if (iframeCount === 0) {
// Destroy the iframe if created here
$iframe.remove();
}
}, 1000);
})
.fail(function (err) {
// Use the pop-up method if iframe fails for some reason
console.error("Failed to print from iframe", err);
printContentInNewWindow(content, options);
})
.always(function () {
try {
options.deferred.resolve();
} catch (err) {
console.warn('Error notifying deferred', err);
}
});
}
function printContentInNewWindow(content, options) {
// Open a new window and print selected content
var frameWindow = window.open();
return printFrame(frameWindow, content, options)
.always(function () {
try {
options.deferred.resolve();
} catch (err) {
console.warn('Error notifying deferred', err);
}
});
}
function isNode(o) {
/* http://stackoverflow.com/a/384380/937891 */
return !!(typeof Node === "object" ? o instanceof Node : o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName === "string");
}
$.print = $.fn.print = function () {
// Print a given set of elements
var options, $this, self = this;
// console.log("Printing", this, arguments);
if (self instanceof $) {
// Get the node if it is a jQuery object
self = self.get(0);
}
if (isNode(self)) {
// If `this` is a HTML element, i.e. for
// $(selector).print()
$this = $(self);
if (arguments.length > 0) {
options = arguments[0];
}
} else {
if (arguments.length > 0) {
// $.print(selector,options)
$this = $(arguments[0]);
if (isNode($this[0])) {
if (arguments.length > 1) {
options = arguments[1];
}
} else {
// $.print(options)
options = arguments[0];
$this = $("html");
}
} else {
// $.print()
$this = $("html");
}
}
// Default options
var defaults = {
globalStyles: true,
mediaPrint: false,
stylesheet: null,
noPrintSelector: ".no-print",
iframe: true,
append: null,
prepend: null,
manuallyCopyFormValues: true,
deferred: $.Deferred(),
timeout: 750,
title: null,
doctype: '<!doctype html>'
};
// Merge with user-options
options = $.extend({}, defaults, (options || {}));
var $styles = $("");
if (options.globalStyles) {
// Apply the stlyes from the current sheet to the printed page
$styles = $("style, link, meta, base, title");
} else if (options.mediaPrint) {
// Apply the media-print stylesheet
$styles = $("link[media=print]");
}
if (options.stylesheet) {
// Add a custom stylesheet if given
$styles = $.merge($styles, $('<link rel="stylesheet" href="' + options.stylesheet + '">'));
}
// Create a copy of the element to print
var copy = $this.clone();
// Wrap it in a span to get the HTML markup string
copy = $("<span/>")
.append(copy);
// Remove unwanted elements
copy.find(options.noPrintSelector)
.remove();
// Add in the styles
copy.append($styles.clone());
// Update title
if (options.title) {
var title = $("title", copy);
if (title.length === 0) {
title = $("<title />");
copy.append(title);
}
title.text(options.title);
}
// Appedned content
copy.append(getjQueryObject(options.append));
// Prepended content
copy.prepend(getjQueryObject(options.prepend));
if (options.manuallyCopyFormValues) {
// Manually copy form values into the HTML for printing user-modified input fields
// http://stackoverflow.com/a/26707753
copy.find("input")
.each(function () {
var $field = $(this);
if ($field.is("[type='radio']") || $field.is("[type='checkbox']")) {
if ($field.prop("checked")) {
$field.attr("checked", "checked");
}
} else {
$field.attr("value", $field.val());
}
});
copy.find("select").each(function () {
var $field = $(this);
$field.find(":selected").attr("selected", "selected");
});
copy.find("textarea").each(function () {
// Fix for https://github.com/DoersGuild/jQuery.print/issues/18#issuecomment-96451589
var $field = $(this);
$field.text($field.val());
});
}
// Get the HTML markup string
var content = copy.html();
// Notify with generated markup & cloned elements - useful for logging, etc
try {
options.deferred.notify('generated_markup', content, copy);
} catch (err) {
console.warn('Error notifying deferred', err);
}
// Destroy the copy
copy.remove();
if (options.iframe) {
// Use an iframe for printing
try {
printContentInIFrame(content, options);
} catch (e) {
// Use the pop-up method if iframe fails for some reason
console.error("Failed to print from iframe", e.stack, e.message);
printContentInNewWindow(content, options);
}
} else {
// Use a new window for printing
printContentInNewWindow(content, options);
}
return this;
};
})(jQuery);
How would I run the first one wait 5 or so seconds and then run the jquery print? I'm having a hard time with this. So the id would run first and then the print would run adter the id="abc" Here is an example of the code in use:
<div id="test">
<button id="abc" class="btn" onclick="jQuery.print(#test1)"></button>
</div>
If I understand your problem correctly, you want the jQuery click function to be run first, making a div with id="test1" visible and then, once it's visible, you want to run the onclick code which calls jQuery.print.
The very first thing I will suggest is that you don't have two different places where you are handling the click implementation, that can make your code hard to follow.
I would replace your $('#abc').click with the following:
function printDiv(selector) {
$(selector).show();
window.setTimeout(function () {
jQuery.print(selector);
}, 1);
}
This function, when called, will call jQuery.show on the passed selector, wait 1ms and then call jQuery.print. If you need the timeout to be longer, just change the 1 to whatever you need. To use the function, update your example html to the following:
<div id="test">
<button id="abc" class="btn" onclick="printDiv('#test1')"</button>
</div>
When the button is clicked, it will now call the previously mentioned function and pass it the ID of the object that you want to print.
As far as your second function goes, where you have the comment **//run this second**, you should leave that alone. All it does is extend you jQuery object with the print functionality. You need it to run straight away and it currently does.

jquery: can find element, but unable to remove it

I've got stuck here with jQuery remove method. Here's a piece of code ...
clearTemplate : function(contentContainer) {
var container = $('body').find('#' + contentContainer);
if (container.length !== 0) {
console.log(container.length);
container.remove();
console.log(container.length);
}
}
Script can easily find '#'+contentContainer in DOM but is unable to remove it. It has no problem with removing children elements of container object as well.
Console.log returns (obviously) : 1 and 1
Container is also dynamically loaded into DOM.
Here's a bigger piece ...
var TemplateClass = {
mainDiv : $('<div>').attr('id',contentContainer),
setData : function(result, images, template, link) {
this.result = result;
this.images = images;
this.template = template;
this.link = link;
},
readyTemplate : function() {
var that = this;
$.each(this.result, function () {
data = that.result[0];
$(that.mainDiv).loadTemplate(that.template, data, {
append: true
});
});
return this.mainDiv;
},
clearTemplate : function(contentContainer) {
var container = $('body').find('#' + contentContainer);
if (container.length !== 0) {
console.log(container.length);
container.remove();
console.log(container.length);
}
}
}
I'm able to live without removing this object, but it's just not right.

Jstree lazyloading and expand all issue

We are experiencing an issue when using the lazyloading feature and open_all feature together.
The contents of the tree are loaded using lazy loading feature.
When we select a node and click on expand all button, all the child nodes of that node will be fetched using jstree ajax call and opened using the open_all function, when clicking collapse all button, we are using close_all function. This works perfectly for the first time.
But on second time, when we click on expand all on the same node, same ajax url is hit on recursively. ( We think, the url is hit on every time on opening a node using open_all). The intended behaviour is not to call the url(as data is already loaded), only the open_all function should be executed.
Could you please clarify how to fix the issue
//code to load the tree
$("#TreePanel").jstree({
"xml_data" : {
"ajax" : {
"url" : "/ajax/loadTree",
"type" : "post",
"data" : function(node) {
var data = {};
data.dunsNumber = ${dunsNumber};
if (node == -1) {
//set duns number to data
} else {
data.selectedNodeId = node.attr("id");
data.expandAll = isExpandAll;
}
return data;
},
"success" : function(data) {
if ($(data).attr('id') == 'error') {
$("#overlayContent").empty();
} else {
return data;
}
}
},
"xsl" : "nest"
},
"plugins" : [ "themes", "xml_data" ]
});
//code to expand all nodes
$("#ufvExpandAll").bind("click", function() {
isExpandAll= true;
$("#TreePanel").jstree("open_all", selectedNode);
isExpandAll= false;
});
//code to collapse all nodes
$("#ufvCollapseAll").bind("click", function() {
$("#TreePanel").jstree("close_all", selectedNode);
});
//code to get the node and set on a variable on clicking a node
var selectedNode;
$("#TreePanel").delegate("a", "click", function(e, data) {
var node = $(e.target).closest("li");
if (selectedNode != undefined && selectedNode != null) {
$("#" + selectedNode.id + " > a").removeClass("jstree-default- selected-node");
}
selectedNode = node[0];
$("#" + selectedNode.id + " > a").addClass("jstree-default-selected- node");
$("#ufvExpandAll").attr("disabled", false);
$("#ufvCollapseAll").attr("disabled", false);
return false;
});
Thanks In Advance
Regards
Hari
Try to inspect the node to see if it has any pre-existing children before executing logic to load it with children.
.bind("open_node.jstree", function (event, data) {
var node = $(data.rslt.obj);
var children = $.jstree._reference(node)._get_children(node);
if (children.length==0){
//node is empty, so do node open logic here
}
})

How to drop a node on an empty dynatree

I have two dynatrees on the same page. Tree 1 has various number of nodes, tree 2 is empty. I would like to drag a node from tree 1 and drop it on tree 2. Everything works fine while tree 2 is not empty. If tree 2 is empty, however, it is not working at all. Here is my code:
$('#tree1').dynatree({
dnd: {
onDragStart: function (node) {
return true;
}
}
});
$('#tree2').dynatree({
dnd: {
onDragEnter: function (node, sourceNode) {
return true;
},
onDrop: function (node, sourceNode, hitMode, ui, draggable) {
var copyNode = sourceNode.toDict(true, function (dict) {
delete dict.key;
});
node.addChild(copyNode);
}
}
});
Can anyone tell me how to solve this problem? Thanx in advance.
After reading the dynatree source code (version 1.2.0), I found that it is not possible to add a node to an empty tree without the modification of the source code. This is because when you try to drop a source node to a target tree, it is actually drop a source node to/before/after a target node instead of target tree. Hence, dynatree will not work if there is no node in a tree.
I did something like this:
var tree1Dragging = false,
tree2Over = false;
$('#tree1').dynatree({
dnd : {
onDragStart : function(node) {
tree1Dragging = true;
return true;
},
onDragStop : function(node) {
critDrag = false;
if (tree2Over) {
//TODO do your drop to tree2 process here (get the root and append the "node" to it for example)
}
}
}
});
$('#tree2').dynatree({
dnd : {
onDragEnter : function(node, sourceNode) {
return true;
},
onDrop : function(node, sourceNode, hitMode, ui, draggable) {
var copyNode = sourceNode.toDict(true, function(dict) {
delete dict.key;
});
node.addChild(copyNode);
}
}
});
// Add mouse events to know when we are above the tree2 div
$("#tree2").mouseenter(function() {
if (tree1Dragging) {
tree2Over = true;
}
}).mouseleave(function(){
tree2Over = false;
});

Detect target treenode when dragging items to the TreePanel instance

I have GridPanel and TreePanel instances. Elements from GridPanel could be dragged into the treepanel. But I can't detect which tree node receives these dragged items.
I initialize tree panel DD with the following code (method of class derived from Ext.tree.TreePanel):
initDD: function() {
var treePanelDropTargetEl = this.getEl();
var treePanelDropTarget = new Ext.dd.DropTarget(treePanelDropTargetEl, {
ddGroup: 'ddgroup-1',
notifyDrop: function(ddSource, e, data) {
// do something with data, here I need to know target tree node
return true;
}
});
}
So how can I find out which tree node received dragged items in the "notifyDrop" handler. I can take e.getTarget() and calculate the node but I don't like that method.
If you use a TreeDropZone (instead of DropTarget) you'll have more tree-specific options and events like onNodeDrop. Note that there are many ways to do DnD with Ext JS.
Here is some kind of solution
MyTreePanel = Ext.extend(Ext.tree.TreePanel, {
listeners: {
nodedragover: function(e) {
// remember node
this.targetDropNode = e.target;
}
}
initComponent: function() {
// other initialization steps
this.targetDropNode = false;
var config = {
// ...
dropConfig: {
ddGroup: 'mygroupdd',
notifyDrop: function(ddSource, e, data) {
// process here using treepanel.targetDropNode
}
}
}
// ...
};
// other initialization steps
}
});

Categories