Fancytree Lazyload: Make Ajax call each time it expands - lazy-evaluation

I have a tree folder structure that I am displaying in a webpage using Fancytree and using the lazyLoad option to display the contents of the folder ondemand. This works fine for the first time, but if there are no items under a folder and when expanded, since there are no items, the icon to expand/collapse disappears. When i create some folders in at empty folder, I dont have a way to fire an ajax call again to display the new contents. Any idea how this can be achieved?
$("#officialTreeView").fancytree({
extensions: ["table"],
aria: true,
source: {
url: "myurl/jsonoutput",
data: {key: "1" },
cache: false
},
lazyLoad: function(event,data) {
var node = data.node;
//data.node.load(true);
// Issue an ajax request to load child nodes
data.result = { cache:false, url: "myurl/jsonoutput", data: {key: node.key } }
},
renderColumns: function(event, data) {
var node = data.node,
$tdList = $(node.tr).find(">td");
//console.log(node);
$tdList.eq(1).text(node.data.childcount);
}
});

You can call node.resetLazy() or node.load(true) (for example when a node is collapsed).
See http://wwwendt.de/tech/fancytree/doc/jsdoc/FancytreeNode.html for a complete list of methods.

i.e just add an event handler for collapse to your config
collapse: function(event, data){
data.node.resetLazy();
},

Related

Ideal way to ajax load a view in Drupal 8 with contextual filters

I have a taxonomy called category.
I have a menu with links to each of these taxonomy items.
The Taxonomy page for each of these items has that menu and also contains a view which uses a contextual filter from the URL to filter the content of the view to content with that taxonomy term.
I wanted to Ajax load the view content when one of these menu items is clicked.
I've been able to achieve the desired result by enabling ajax on the view and using the following JavaScript.
(function ($) {
Drupal.behaviors.course_browser = {
attach: function (context, settings) {
// should only be one menu, but guard against 0, and avoid the if statement.
context.querySelectorAll(".menu--categories").forEach((menu) => {
// for each link in the menu
menu.querySelectorAll(".nav-link").forEach((link) => {
// on click
link.addEventListener("click", (event) => {
event.preventDefault();
// fetch the taxonomy term id from menu link
let tid = event.target.dataset.drupalLinkSystemPath.replace(
"taxonomy/term/",
""
);
// make the ajax call
$.ajax({
url: "/views/ajax",
type: "post",
data: {
view_name: "course_browser",
view_display_id: "block_1",
view_args: tid,
},
success: (response) => {
response.forEach((action) => {
// the response contains a number of commands; I'm not sure
if (
action.command === "insert" &&
action.method === "replaceWith"
) {
let viewElement = document.querySelector(VIEW_SELECTOR);
// update the html of the course browser
viewElement.innerHTML = action.data;
// update the url in the browser
window.history.pushState("", "", event.target.href);
// seperate function to adjust my page title
updatePageTitle(event.target.textContent);
// call drupal behaviours passing context to ensure all the other js code gets a chance to manipulate the new content
Drupal.attachBehaviors(viewElement);
}
});
},
error: function (data) {
console("An error occured fetching the course browser");
},
});
});
});
});
},
};
})(jQuery);
I'm looking for feedback on my approach here; my main concern at the moment is the way I handle the response. When I look at the response I receive something like that shown below:
0: {command: "settings", settings: {…}, merge: true}
1: {command: "add_css", data: "<link rel="stylesheet" media="all" href="/core/modules/views/css/views.module.css?qcog4i" />↵"}
2: {command: "insert", method: "append", selector: "body", data: "<script src="/core/assets/vendor/jquery/jquery.min…/js/modules/views/ajax_view.js?qcog4i"></script>↵", settings: null}
3: {command: "insert", method: "replaceWith", selector: ".js-view-dom-id-", data: "The HTML"}
As you can see, I'm manually handling the response by cherry picking the part I want and replacing the HTML of view. Based on what I've seen around Drupal, I think there should be something I can pass this response to that handles it automatically. When I look at the window object of the browser, I can see Drupal.AjaxCommands which looks like it was designed to handle this, but I'm not sure how I should be using this.
I also note that in the case I can simply pass this response to something to have those AjaxCommands executed, the selector ".js-view-dom-id-" isn't right. So I could tweak the response before I pass it, or if someone knows a way to adjust the ajax request to perhaps get the right selector, that would be ideal.
Sorry if this info is readily available somewhere...there are quiet a few resources around related to Drupal and Ajax but I haven't been able to find examples of exactly what I'm doing here, the circumstances always seem to differ enough that I can't use them.
Thanks for any help.

Extjs Treegrid Drop Drag loading grid as children

I have a grid, a tree grid and a drop drag functionality in extjs4.
The tree grid is populated by an ajax json request but when I drop and drag a record from the grid to the tree, the parent node that is added to the tree grid acts asynchronously and fires an ajax request when clicked. The problem is the parameters on the ajax request do not indicate the parent node.
Does anyone have a solution to pass parameters correctly on a asynchronous node or to convert an asynchronous node to a static tree node.
I have created the below js fiddle to replicate the issue.
http://jsfiddle.net/mgill21/3HJXX/2/
Ext.create('Ext.data.Store', {
model: 'Apps.demo.model.Resource',
autoLoad: true,
proxy: {
type: 'ajax',
url: '/echo/json/',
actionMethods: {
read: 'POST'
},
extraParams: {
json: Ext.JSON.encode(myData)
},
delay: 0
}
});
Try in your data drag item with:
var myData = [
{
name: "Rec 0",
type: "0",
children : []
},
...
];
it's work to me.
Thanks for the help. For info, i fixed this issue by setting the dragged nodes to loaded:true.
Nodes are loaded if their flag "loaded" is not true and if "leaf" is not true. Leaf property is public but "loaded" not. I set "loaded" to true on drops where needed.

FineUploader uploads the same file multiple times at the same time

Say I want to upload several files at once, which is something I can do when setting the multiple option to true:
var myUploader = new qq.FineUploader({
element: $('#test')[0],
multiple: true,
request: { endpoint: 'path/to/master/server/php/' },
autoUpload: false,
});
Now, let's say I have a button that will allow me to select the files I want to upload. If I click said button and select, say, test.txt file, test.txt will be added to the list of files that will be uploaded. So far so good. Now, my problem is that, if I click the button again, and select test.txt file again, it will be added to the list even though it's already in the list.
Is there any way to prevent FineUploader from letting me do this?
Thanks in advance
I'd be careful declaring a file a duplicate simply based on the name. You should also take size into account, at least. Although, this is not possible in IE9 and older since we can't determine file size client-side in those browsers. Just for the purposes of simplicity, let's use the file name exclusively...
One way is to maintain an array of file names submitted to the uploader. You can add to this list in your an onSubmitted handler. The, you can contribute an onValidate handler that will reject the file if it already exists in the array. Your code would look something like this:
var filenames = [];
var myUploader = new qq.FineUploader({
element: $('#test')[0],
multiple: true,
request: { endpoint: 'path/to/master/server/php/' },
autoUpload: false,
callbacks: {
onSubmitted: function(id, name) {
filenames.push(name);
},
onValidate: function(fileData) {
return qq.indexOf(filenames, fileData.name) < 0;
}
}
});
Also, just for kicks, why not just use Fine Uploader's jQuery plug-in, since you seems to already be using jQuery in your project? The above example is rewritten using the jQuery plug-in below:
var filenames = [];
$('#test').fineUploader({
multiple: true,
request: { endpoint: 'path/to/master/server/php/' },
autoUpload: false
})
.on("submitted", function(event, id, name) {
filenames.push(name);
})
.on("validate", function(event, fileData) {
return $.inArray(fileData.name, filenames) < 0;
});

Dynatree initial load with children

I have a lazy loading DynaTree in the lastest version 1.2.4 and get the challange, to provide the path to the current selected node.
I do a loop over the root nodes to build the first level of the tree. After clicking one of the root nodes, the tree expand and select via lazy loading the next elements.
Now the element in the 3rd dimension of the tree is known, but how i provide the affected nodes in the JSON structure?
A simple add to the end will also add the elements in the root tree.
$(function(){
// Attach the dynatree widget to an existing <div id="tree"> element
// and pass the tree options as an argument to the dynatree() function:
$("##tree").dynatree({
clickFolderMode: 3,
persist: false,
autoCollapse: true,
onActivate: function(node) {
// A DynaTreeNode object is passed to the activation handler
// Note: we also get this event, if persistence is on, and the page is reloaded.
if( node.data.href ){
// use href and target attributes:
window.location.href = node.data.href;
}
},
onLazyRead: function(node){
node.appendAjax({url: "#intranetpath#remote/lopTreeLoader.cfc?method=getNodes",
data: {"key": node.data.key, // Optional url arguments
"mode": "all"
},
// (Optional) use JSONP to allow cross-site-requests
// (must be supported by the server):
// dataType: "jsonp",
success: function(node) {
// Called after nodes have been created and the waiting icon was removed.
// 'this' is the options for this Ajax request
},
error: function(node, XMLHttpRequest, textStatus, errorThrown) {
// Called on error, after error icon was created.
},
cache: false // Append random '_' argument to url to prevent caching.
});
},
children: [ // Pass an array of nodes.
<cfloop query="qDivision">
{ key:"#qDivision.obj_uuid#" ,
title: "#qDivision.f_division_name#",
activate:false,
expand:#qDivision.expand#,
select:false,
isLazy:true,
<cfif qDivision.clickable>
addClass:"boldText",
href: "/program.helios/lop/index.cfm?obj_uuid=#qDivision.obj_uuid#",
</cfif>
icon:"../../skin/icons/idivision.gif"}
<cfif qDivision.currentrow NEQ qDivision.recordcount>,</cfif>
</cfloop>
]
});
});
How I can initialize the tree with all nodes in the direct path to the selected node?
A child object may in turn contain a children property:
children: [ // Pass an array of nodes.
<cfloop query="qDivision">
{ key:"#qDivision.obj_uuid#" ,
title: "#qDivision.f_division_name#",
activate:false,
children: [{title: "foo"}, ...]

Extjs How to access the name of the chart being used by store

I have a store being used by several charts. I get my data remotely with an ajax call. In the php script that I link it too, I'm just going to change the parameters of my query to adjust for the different charts.
Here's my idea: I pass the title of the chart's panel as a parameter to my php script. That'll tell me which chart it is.
How do I access the title of whatever chart the store is being used by?
var my_store = Ext.create('Ext.data.JsonStore', {
fields: ['project', 'accepted', 'rejected', 'deleted', 'undefined'],
proxy: {
type: 'ajax',
url: 'generate_proj.php',
extraParams: {foo: **chart.id**},
reader: {
type: 'json'
}
},
autoLoad: true,
listeners: {
beforeload: function(store,operation) {
//operation.params.foo = this.idname;
},
load: function(obj,records) {
var text = Ext.decode(obj.responseText);
Ext.each(records,function(rec) {
});
}
}
});
Here's what I've done so far. Getting the name of a single chart/panel is no problem. I want the store to be able to dynamically read the name of what's using it. How?
Somewhere, you have some code that switches between the various charts. During that code, you could do something like
activeChart.getChartStore().proxy.extraParams.foo = activeChart.getId();
where activeChart is whatever reference you have to the chart you are about to show. Then when you load the store, it'll send the correct parameter.

Resources