I have set up my fancytree to open via lazy loading, and it works very nicely.
$("#tree").fancytree({
selectMode: 1, quicksearch:true, minExpandLevel:2,
autoScroll: true,
source: [{
title: "Root",
key: "1",
lazy: true,
folder: true
}],
lazyLoad: function(event, data) {
var node = data.node;
data.result = {
url: "getTreeData.jsp?parent=" + node.key,
data: {
mode: "children",
parent: node.key
},
cache: false
};
}
});
However, if a user has previously selected a point on the tree, I would like the tree to open to that point.
I have a variable called hierarchy which looks like "1/5/10/11/200" and holds the sequence of keys to that certain point.
The following will not work:
$("#tree").fancytree("getTree").getNodeByKey("1").setExpanded();
$("#tree").fancytree("getTree").getNodeByKey("5").setExpanded();
$("#tree").fancytree("getTree").getNodeByKey("10").setExpanded();
$("#tree").fancytree("getTree").getNodeByKey("11").setExpanded();
$("#tree").fancytree("getTree").getNodeByKey("200").setExpanded();
The reason why it will not work, apparently, is because there needs to be some delay between one statement and the next.
The following code works, however it is in my mind messy:
function openNode(item) {
$("#tree").fancytree("getTree").getNodeByKey(String(item)).setExpanded();
}
function expandTree(hierarchy) {
var i=0;
hierarchy.split("/").forEach(function (item) {
if (item!="") {
i++;
window.setTimeout(openNode, i*100,item);
}
});
Is there any neater way of opening to a specific point on the tree?
The following code seems to do the trick.
It was adapted from
http://wwwendt.de/tech/fancytree/doc/jsdoc/Fancytree.html#loadKeyPath
function expandTree(hierarchy) {
$('#tree').fancytree("getTree").loadKeyPath(hierarchy).progress(function(data) {
if (data.status === "loaded") {
console.log("loaded intermediate node " + data.node);
$('#tree').fancytree("getTree").activateKey(data.node.key);
} else if (data.status === "ok") {}
}).done(function() {});
}
Related
I've just defined a combobox. Firstly it loads a countrylist and when select a value it's fire a change event which doing a ajax query to DB within searching service;
The thing; this configuration works pretty well when I click and open combobox items. But when I'm typing to combobox's field it's fires listener's store.load and because of none of country selected yet, the search query url gives not found errors of course.
{
xtype: 'countrycombo',
itemId: 'countryName',
name:'country',
afterLabelTextTpl: MyApp.Globals.required,
allowBlank: false,
flex: 1,
// forceSelection: false,
// typeAhead: true,
// typeAheadDelay: 50,
store: {
proxy: {
type: 'ajax',
// isSynchronous: true,
url: MyApp.Globals.getUrl() + '/country/list?limit=250',
// timeout: 300000,
reader: {
type: 'json',
rootProperty: 'data'
}
},
pageSize: 0,
sorters: 'description',
autoLoad: true
}
,
listeners: {
change: function (combo, countryId) {
var cityStore = Ext.getStore('cityCombo');
cityStore.getProxy()
.setUrl(MyAppp.Globals.getUrl() + '/city/view/search?query=countryid:'+ countryId);
// Ext.defer(cityStore.load, 100);
cityStore.load();
}
}
},
I've tried several things as you see in code above to set a delay/timeout for load during typing to combobox text field; Ext.defer, timeoutconfig on proxy, typeAhead config on combo but none of them worked!
I thought that Ext.defer is the best solution but it gives this error:
Uncaught TypeError: me.getAsynchronousLoad is not a function at load (ProxyStore.js?_dc=15169)
How can I set a delay/timeout to combobox to fires load function?
Instead of Ext.defer(cityStore.load, 100);
try using this :
Ext.defer(function(){
cityStore.load
}, 300);
If this doest work, try increasing your delay
or you can put a logic before loading
like this :
if(countryId.length == 5){
cityStore.load
}
This will ensure that you Entered the right values before loading
Hope this helps, and Goodluck on your project
well.. I've tried to implement #Leroy's advice but somehow Ext.defer did not fire cityStore.load. So I keep examine similar situations on google and found Ext.util.DelayedTask
So configured the listerens's change to this and it's works pretty well;
listeners: {
change: function (combo, countryId) {
var alert = new Ext.util.DelayedTask(function () {
Ext.Msg.alert('Info!', 'Please select a country');
});
var cityStore = Ext.getStore('cityCombo');
cityStore.getProxy().setUrl(MyApp.Globals.getUrl() + '/city/view/search?query=countryid:'+ countryId);
if (typeof countryId === 'number') {
cityStore.load();
} else {
alert.delay(8000);
}
}
}
I need to employ a filter function to implement a heuristic for selecting records. Simple field/value checks, alone, are inadequate for our purpose.
I'm trying to follow the examples for function filters, but for some reason, the "allowFunctions" flag keeps getting set to false.
I attempt to set the allowFunctions property to true in the storeConfig:
storeConfig: {
models: ['userstory', 'defect'],
allowFunctions: true,
filters: [{
// This did not work ...
property: 'Iteration.Name',
value: 'Sprint 3',
// Trying dynamic Filter Function. Update: Never called.
filterFn: function (item) {
console.log("Entered Filter Function!");
var iter = item.get("Iteration");
console.log("Iteration field: ", iter);
if (iter !== null && iter !== undefined) {
return (iter.name === "Sprint 3");
} else {
return false;
}
}
}]
},
After the grid view renders, I inspect it the store configuration and its filters:
listeners: {
afterrender: {
fn: function (_myVar, eOpts) {
console.log("Arg to afterrender: ", _myVar, " and ", eOpts);
var _myStore = _myVar.getStore();
console.log("Store filters: ", _myStore.filters);
}
}
},
What I find is that the allowFunctions property has been set back to false and I see that the filter function I specified never fired.
Console Screen Shot
So either I am setting allowFunctions to true in the wrong place, or something built into the Rally Grid View and its data store prohibits filter functions and flips the flag back to false.
OR there's a third option betraying how badly off my theory of operation is.
Oh, wise veterans, please advise.
Here's the entire Apps.js file:
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function () {
//Write app code here
console.log("Overall App Launch function entered");
//API Docs: https://help.rallydev.com/apps/2.1/doc/
}
});
Rally.onReady(function () {
Ext.define('BOA.AdoptedWork.MultiArtifactGrid', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function () {
console.log("onReady Launch function entered");
this.theGrid = {
xtype: 'rallygrid',
showPagingToolbar: true,
showRowActionsColumn: false,
editable: false,
columnCfgs: [
'FormattedID',
'Name',
'ScheduleState',
'Iteration',
'Release',
'PlanEstimate',
'TaskEstimateTotal',
'TaskActualTotal', // For some reason this does not display ?? :o( ??
'TaskRemainingTotal'
],
listeners: {
afterrender: {
fn: function (_myVar, eOpts) {
console.log("Arg to afterrender: ", _myVar, " and ", eOpts);
var _myStore = _myVar.getStore();
console.log("Store filters: ", _myStore.filters);
}
}
},
storeConfig: {
models: ['userstory', 'defect'],
allowFunctions: true,
filters: [{
// This did not work ...
property: 'Iteration.Name',
value: 'Sprint 3',
// Trying dynamic Filter Function. Update: Never called.
filterFn: function (item) {
console.log("Entered Filter Function!");
var iter = item.get("Iteration");
console.log("Iteration field: ", iter);
if (iter !== null && iter !== undefined) {
return (iter.name === "Sprint 3");
} else {
return false;
}
}
}]
},
context: this.getContext(),
scope: this
};
this.add(this.theGrid);
console.log("The Grid Object: ", this.theGrid);
}
});
Rally.launchApp('BOA.AdoptedWork.MultiArtifactGrid', {
name: 'Multi-type Grid'
});
});
This is a tricky one since you still want your server filter to apply and then you want to further filter the data down on the client side.
Check out this example here:
https://github.com/RallyCommunity/CustomChart/blob/master/Settings.js#L98
I think you can basically add a load listener to your store and then within that handler you can do a filterBy to further filter your results on the client side.
listeners: {
load: function(store) {
store.filterBy(function(record) {
//return true to include record in store data
});
}
}
I'm not familiar with allowFunctions, but in general remoteFilter: true/false is what controls whether the filtering is occurring server side or client side. remoteFilter: true + the load handler above gives you the best of both worlds.
I am using paramquery grid component in which I am trying to use autocomplete.
Column Model for branch:
{ title: "Branch", dataIndx: "branchId", width: 150,
filter: { type: "select",
condition: 'equal',
prepend: { '': '--All--' },
listeners: ['change'],
valueIndx: "branchId",
labelIndx: "branchName",
options: branchList,
},
editor: {
type: "textbox",
init: autoCompleteEditor
//type: function (ui) { return dropdowneditor(this, ui); }
},
render: function (ui) {
for (var i = 0; i < branchList.length; i++) {
var option = branchList[i];
if (option.branchId == ui.rowData.branchId) {
return option.branchName;
}
}
}
}
autoCompleteEditorMethod:
var autoCompleteEditor = function (ui) {
var $inp = ui.$cell.find("input");
//initialize the editor
$inp.autocomplete({
source: function(request, response) {
var rows = imAutocompleteJSONParse(branchList);// this method converting my JSON object into Value and label format.
return response(rows);
},
selectItem: { on: true }, //custom option
highlightText: { on: true }, //custom option
minLength: 0,
select: function(event, ui) {
event.preventDefault();
$(this).val(ui.item.label);
},
focus: function(event, ui) {
event.preventDefault();
$("#search").val(ui.item.label);
}
}).focus(function () {
//open the autocomplete upon focus
$(this).autocomplete("search", "");
});
}
I get branch id into my grid and I have branchList JSON which have branch id & branch Name. Inside grid my render function showing branchName on UI.
But when I click on searchable dropdown I'm getting branch id.
Below snapshot may explain my issue properly.
Summary of issue: I am getting branch id in Grid. With help of render method I am able to show branch name on grid. but when I click on textbox I getting branch id.
http://jsfiddle.net/v4zx8tjc/4/
Like blackmiaool suggests in his comment, this question would be easier to answer with a live demo using something like JSFiddle.
Based on what I can see in your question, which isn't that much, there are a few areas I would take a second look at.
The Source function in JQuery.autoComplete. Where is branchList coming from? I don't see it declared anywhere and why are you not using the 'request' param?
Not sure what your custom properties are doing but it might be a good idea to verify those are not interfering with the results.
Edit 1: Looking back at the code you posted I think I see where your branchList variable is coming from. It would be very helpful to see your imAutocompleteJSONParse() method because I believe that may be where things are breaking down.
I've implemented my own Algolia PoC based of https://www.algolia.com/doc/search/auto-complete and I'm now struggling with a specific use case: how can I handle a search which does not return any hits?
Here is my code:
I've been able to identify and detect when/where no hits are returned, but I can't do anything beside just using a console.log(). I tried to get a custom return_msg but I can't call the function.
I also tried to do some tweak under suggestion: function(suggestion) but this function is never called if no hits are returned.
I also did not found any documentation about this "Templates" section on https://github.com/algolia/autocomplete.js
$('#q').autocomplete({ hint: false }, [
{
source: function(q, cb) {
index.search(q,
{ hitsPerPage: 10 },
function(error, content) {
if (error) {
cb([]);
return;
}
if (content.nbHits == 0)
{ return_msg = '<h5> Sorry, no result </h5>';
// DO something here
console.log(return_msg);
// console.log return "Sorry, no result"
}
cb(content.hits, content);
});
},
displayKey: 'game',
templates: {
suggestion: function(suggestion) {
return_msg = '<h5> '+ suggestion.MY_ATTRIBUTE + '</h5>'
return return_msg;
}
}
}
]).on('autocomplete:selected', function(event, suggestion, dataset) {
window.location = (suggestion.url);
});
Any pointers would be greatly appreciated =)
Using the templates option of your dataset you can specify the template to use when there are no results:
source: autocomplete.sources.hits(indexObj, { hitsPerPage: 2 }),
templates: {
suggestion: // ...
header: // ...
footer: // ...
empty: function(options) {
return '<div>My empty message</div>';
}
}
Full documentation here.
I'm using jsTree to show a tree with checkboxes. Each level of nodes is loaded on-demand using the json_data plugin.
If a node's descendent is checked, then that node should be in an "undetermined state" (like ACME and USA).
The problem is, the tree starts out collapsed. ACME looks unchecked but should be undetermined. When I finally expand to a checked node, jsTree realizes the ancestors should be undetermined.
So I need to be able to put a checkbox in the undetermined state without loading its children.
With jsTree you can pre-check a box by adding the jstree-checked class to the <li>. I tried adding the jstree-undetermined class, but it doesn't work. It just puts them in a checked state.
Here's my code:
$("#tree").jstree({
plugins: ["json_data", "checkbox"],
json_data: {
ajax: {
url: '/api/group/node',
success: function (groups) {
var nodes = [];
for (var i=0; i<groups.length; i++) {
var group = groups[i];
var cssClass = "";
if(group.isSelected)
cssClass = "jstree-checked";
else if(group.isDecendantSelected)
cssClass = "jstree-undetermined";
nodes.push({
data: group.name,
attr: { 'class': cssClass }
});
}
return nodes;
}
}
}
})
My Question
How do I set a node to the undetermined state?
I had the same problem and the solution I found was this one:
var tree = $("#tree").jstree({
plugins: ["json_data", "checkbox"],
json_data: {
ajax: {
url: '/api/group/node',
success: function(groups) {
var nodes = [];
for (var i = 0; i < groups.length; i++) {
var group = groups[i];
var checkedState = "false";
if (group.isSelected)
checkedState = "true";
else if (group.isDecendantSelected)
checkedState = "undetermined";
nodes.push({
data: group.name,
attr: { 'checkedNode': checkedState }
});
}
return nodes;
},
complete: function () {
$('li[checkedNode="undetermined"]', tree).each(function () {
$(this).removeClass('jstree-unchecked').removeClass('jstree-checked').addClass('jstree-undetermined');
});
$('li[checkedNode="true"]', tree).each(function () {
$(this).removeClass('jstree-unchecked').removeClass('jstree-undetermined').addClass('jstree-checked');
});
$('li[checkedNode="false"]', tree).each(function () {
$(this).removeClass('jstree-checked').removeClass('jstree-undetermined').addClass('jstree-unchecked');
});
}
}
}
});
Hope it helps you!
Maybe this changed in the meanwhile...
But now (version 3.0.0) the really simple solution works:
{
id : "string" // will be autogenerated if omitted
text : "string" // node text
icon : "string" // string for custom
state : {
opened : boolean // is the node open
disabled : boolean // is the node disabled
selected : boolean // is the node selected
undetermined : boolean // is the node undetermined <<==== HERE: JUST SET THIS
},
children : [] // array of strings or objects
li_attr : {} // attributes for the generated LI node
a_attr : {} // attributes for the generated A node
}
Learned directly from the source code at: https://github.com/vakata/jstree/blob/6507d5d71272bc754eb1d198e4a0317725d771af/src/jstree.checkbox.js#L318
Thank you guys, and I found an additional trick which makes life a little better, but it requires a code change in jstree.js. Looks like an oversight:
Look at the get_undetermined function, and scan for the keyword break. That break should be a continue.
If you make that one change, then all you need to do is provide the state (for the main object and its children), and jstree will automatically take care of cascading upwards for undetermined state. It was bailing out early from the scripting and failing to catch all the undetermined nodes properly, requiring the above ugly workarounds for styling and such.
Here's my config (no special attrs or complete() function required) using AJAX:
var tree = $('#jstree').jstree({
"core": {
"themes": {
"variant": "large"
},
'data': {
'url': function (node) {
return "{{API}}/" + node.id + "?product_id={{Product.ID}}"
},
'dataType': 'json',
'type': 'GET',
'success': function (data) {
if (data.length == 0) {
data = rootStub
}
return {
'id': data.id,
'text': data.text,
'children': data.children,
'state': data.state,
}
}
}
},
"checkbox": {
// "keep_selected_style": false,
"three_state": false,
"cascade": "undetermined"
},
"plugins": ["wholerow", "checkbox"],
});