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"}, ...]
Related
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.
I have the following code base to share with you guys to list the pages that fetch using the query builder via AJAX call. We have to pass the URL and the parameters to fetch the child pages from the URL that we provide.
I have put some console.log to track the values of each states.
replace the with your project.
<featurearticles
jcr:primaryType="cq:Widget"
fieldLabel="Article Pages"
itemId="selectauthorId"
name="./authorselect"
type="select"
xtype="selection">
<options jcr:primaryType="cq:WidgetCollection"/>
<listeners
jcr:primaryType="nt:unstructured"
loadcontent="function(box,value) {
CQ.Ext.Ajax.request({
url: '/bin/querybuilder.json',
success: function(response, opts) {
console.log('Response from the ajax');
var resTexts = $.parseJSON(response.responseText);
var selectopts = [];
console.log(resTexts);
$.each(resTexts.hits, function(key, page) {
console.log(page);
selectopts.push({value: page['path'], text:page['name']});
});
console.log(selectopts);
box.setOptions(selectopts);
},
params: {
'type' :'cq:Page',
'group.1_path' : '/content/<PROJECT_NAME>/Feature_Articles'
}
});
}"
selectionchanged="function(box,value) {
var panel = this.findParentByType('panel');
var articleTitle = panel.getComponent('articleTitleId');
CQ.Ext.Ajax.request({
url: value + '/_jcr_content/par/featurearticleintro.json',
success: function(response, opts) {
console.log('success now');
var resTexts = $.parseJSON(response.responseText);
console.log(resTexts);
},
failure: function(response, opts) {
console.log('server-side failure with status code ' + response.status);
}
});
}"/>
</featurearticles>
If you have any better idea than this, I would like to know about that.
Cheers,
Another alternative is to use the "options" attribute of the selection xtype to fetch the dropdown list options from an AJAX call via a servlet or sling selector. The widgets api (http://dev.day.com/docs/en/cq/5-6/widgets-api/index.html - search for "selection") says this for the options attribute:
If options is a string it is assumed to be an URL pointing to a JSON
resource that returns the options (same structure as above applies).
This should be either an absolute URL or it can use the path of the
content resource being edited using a placeholder
(Selection.PATH_PLACEHOLDER = "$PATH"), for example:
$PATH.options.json.
So it may be a cleaner approach to build a servlet that will respond with JSON to an AJAX request, then put this servlet as the "options" attribute. For example, the attribute might be something like options="/libs/myServlet" or something like options="$PATH.options.json". That may make the dialog cleaner (no listener required), and it uses built-in CQ capability to fetch options via AJAX.
We can use the dynamic dropdown as below:
<item
jcr:primaryType="cq:Widget"
fieldLabel="Item"
name="./item"
optionsRoot="root"
options="/bin/service/item.json"
type="select"
xtype="selection"/>
options: the url will return the json format for the selection xtype
optionsRoot: the name of the root item of the json
optionsTextField: the name of text field (default value: "text")
optionsValueField: the name of the value field (default value: "value")
Example of json: {"root":[{"text":"Item 1", "value":"Value 1"},{"text":"Item 2", "value":"Value 2"},{"text":"Item 3", "value":"Value 3"}]}
Selection xtype
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();
},
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.
I am using Ext.ux.tree.TreeGrid for tree grid panel. All is working fine except sort.
I have 3 level of hierarchy in it like - parent - child - grand child. I want to make sorting based on text of parent only. But I am getting random result every time. :(
This is my code -
var tree_grid = new Ext.ux.tree.TreeGrid({
title : 'Requirements',
height : 415,
enableDD : true,
enableHdMenu : true,
id : 'req_tree',
columns : [ {
header : 'Entity',
dataIndex : 'text',
width : 200,
sortable: true,
sortType : 'asText'
}, {
header : 'Text',
width : 50,
dataIndex : 'temp',
align : 'center',
// sortType : 'asFloat',
sortable: false
}],
dataUrl : 'my_page.php'
});
For sorting I have tried this -
1) var myTreeSorter = new Ext.tree.TreeSorter(tree_grid, {});
myTreeSorter.doSort(tree_grid.getRootNode());
2) new Ext.ux.tree.TreeGridSorter(tree_grid, {
folderSort: true,
dir: "desc",
sortType: function(node) {
// sort by a custom, typed attribute:
return parseInt(node.id, 10);
}
});
3) Used Attributes like - sortType, sortable, sortInfo.
None of the above helped me however. Please help.
var mySortStore = Ext.create("Ext.data.Store", {
model: "MyTreeStore",
data: []
});
Then when you go to add the node to the tree, use this function instead:
function addNodeSorted(node, childToBeAdded){
//clear the store from previous additions
mySortStore.removeAll();
//remove old sorters if they are dynamically changing
mySortStore.sort();
//add the node into the tree
node.appendChild(childToBeAdded);
var cn = node.childNodes,
n;
//remove all nodes from their parent and add them to the store
while ((n = cn[0])) {
mySortStore.add(node.removeChild(n));
}
//then sort the store like you would normally do in extjs, what kind of sorting you want is up to you
mySortStore.sort('height', 'ASC');//this is just an example
//now add them back into the tree in correct order
mySortStore.each(function(record){
node.appendChild(record);
});
}
Did you tried to use the config folderSort on the store:
TreeGrid Example: sencha TreeGrid
var store = Ext.create('Ext.data.TreeStore', {
model: 'Task',
proxy: {
type: 'ajax',
//the store will get the content from the .json file
url: 'treegrid.json'
},
**folderSort: true**
});
--
Other solution, can be used a sort filter on the store:
Store Sort: sencha sort
//sort by a single field
myStore.sort('myField', 'DESC');
//sorting by multiple fields
myStore.sort([
{
property : 'age',
direction: 'ASC'
},
{
property : 'name',
direction: 'DESC'
}
]);