In my project I have 3 company root nodes and test children.With Checkboxes.
- []Company1
-- []Test1
-- []Test2
- []Company2
-- []Test1
-- []Test2
-- []Test 3
- []Company3
-- []Test1
The tree is much more complex, but for question I reduced the levels. However, I want to manage, that it is forbidden to select two Companies. As sample, if Test1 from Company2 is selected and the user select Test1 from Company1, then all selections from Company1 and Company3 get unchecked.
My code for the jstree is:
$(function () {
$('#html1').on('select_node.jstree', function(e, data){
var countSelected = data.selected.length;
if (countSelected>1) {
data.instance.deselect_node( [ data.selected[0] ] );
}
});
$.ajax({
type: "get",
url: "/jstreecontent",
dataType: "json",
success: function(data) {
$('#html1').jstree({
'core' : {
'data' : data
},
"checkbox" : {
"keep_selected_style" : false
},
"plugins" : [ "checkbox", "contextmenu"],
"checkbox": {
three_state : true, // to avoid that fact that checking a node also check others
whole_node : false, // to avoid checking the box just clicking the node
tie_selection : false // for checking without selecting and selecting without checking
},
"contextmenu": {
"items": function ($node) {
var tree = $("#html1").jstree(true);
if($node.a_attr.type === 'test')
return getTestContextMenu($node, tree);
}
}
});
},
complete: function() {
// This function will be triggered always, when ajax request is completed, even it fails/returns other status code
console.log("complete");
},
error: function() {
// This will be triggered when ajax request fail.
console.log("Error");
}
});
});
If I remove the part:
"checkbox": {
three_state : true, // to avoid that fact that checking a node also check others
whole_node : false, // to avoid checking the box just clicking the node
tie_selection : false // for checking without selecting and selecting without checking
},
The selection with the root nodes works perfect, just one root node is allowed, but I cannot select a whole root, just its nodes. So I think the
$('#html1').on('select_node.jstree', function(e, data){
var countSelected = data.selected.length;
if (countSelected>1) {
data.instance.deselect_node( [ data.selected[0] ] );
}
});
Is working on all nodes, not only on the top one. Maybe this is wrong.
But it seems, I fail with it, it is the wrong way. I hope someone else had the same problem in the past and give me a hint for this.
Related
I'd like to know a way of how to update the list values of a ExtJs ComboBox. For instance, I have two comboboxs.
One Combobox determine what values the another ComboBox should have. So, after selecting some of those,
I click the drowndown list (combobox) to see the values. But i dont get reflected.
change: function (combofirst, record) {
Ext.Ajax.request({
-- -- --
-- -- --
success: function (response) {
var combosecond = Ext.getCmp('defaultPackageType');
//I am unable to update the combosecond from below snippet.
combosecond.store = Ext.create('Ext.data.Store', {
fields: ['value', 'display'],
data: [
["N", "No"],
["A", "All accounts"]
] //json response
});
},
failure: function (record, action) {}
});
});
In short, how can I change the values of a ComboBox already has with ajax only.
Hope someone can help me
Thanks
I would also agree to the comment, that creating every time a new store and bind it to the combobox is not the optimal solution. I don't know really the reason why this should be done, but nevertheless here is a working example by using bindStore:
https://fiddle.sencha.com/#view/editor&fiddle/3ci0
Ext.create('Ext.form.field.ComboBox', {
// ...
listeners: {
change: {
fn: function (cb) {
Ext.Ajax.request({
url: 'https://jsonplaceholder.typicode.com/albums',
method: 'GET',
timeout: 60000,
success: function (response) {
var jsonResp = response.responseText;
let jsonObj = Ext.JSON.decode(jsonResp, true)
var combo2 = Ext.getCmp('myCombo2');
combo2.bindStore(Ext.create('Ext.data.Store', {
fields: ['id', 'title'],
data: jsonObj
}));
}
});
}
}
}
});
For selection of value 1 the data is loaded from a different url.
But I would think about whether a new proxy call is necessary and whether you can achieve your requirements by using filters or something else.
I'm using the Zoomchart library's NetChart class to form netchart. But I'm facing issue with links. And not able to find any solution.
Below is the case which I want to achieve:-
Let suppose we have two nodes A and B. Here A is the seller and B is the buyer.
Seller A -> B
Buyer B -> A
I'm loading data dynamically. And when we initially loading A then we are getting B as Buyer of A. And this forms a link like A -> B. But when B loads its related data then it is getting A as it is buying data from it(A).
And this form two link nodes:-
{
"from": "A",
"to": "B"
}
{
"from": "B",
"to": "A"
}
It is creating a chart like this:-
But it should be a single link. Below is the code:-
this.chartObject = new NetChart({
container: document.getElementById('sellerBuyersLinkingChart'),
area: { height: null },
navigation:{
focusNodeExpansionRadius: 1,
initialNodes: ["n-1"],
mode:"focusnodes"
},
data: {
dataFunction: (nodeList, success, error) => {
$.ajax({
url:url+'?nodes='+nodeList.toString(),
success: (response, textStatus, jqXHR) => {
success(response, textStatus, jqXHR);
},
error: error
});
},
requestMaxUnits: 1
},
style: {
nodeStyleFunction: (node) => {
},
linkStyleFunction: (link) => {
let type = link.data['extra']['type'];
if(type == 'sellers') {
link['fromDecoration'] = "arrow";
link['fillColor'] = "rgba(47,195,47,0.8)";
link['direction'] = "L";
} else {
link['toDecoration'] = "arrow";
link['fillColor'] = "rgba(236,46,46,0.8)";
link['direction'] = "R";
}
},
nodeFocused: {
fillColor: 'rgba(232,189,43,1)'
}
}
});
Expectations: Is there any way that I can send to link id with URL. With that way, I can remove the same node from the link.
This chart should create nodes like this:-
1) You can use multiLinkProcessor to combine multiple links if they are not unique
2) If you add unique id to links, then you can update, remove or manipulate them individually (in fact it's recommended to always add id for links and nodes and that will prevent duplicates). Not having ids will prompt notice in console log as well.
I want to have a dropdown with a set of values but also allow the user to "select" a new value not listed there.
I see that select2 supports this if you are using it in tags mode, but is there a way to do it without using tags?
The excellent answer provided by #fmpwizard works for Select2 3.5.2 and below, but it will not work in 4.0.0.
Since very early on (but perhaps not as early as this question), Select2 has supported "tagging": where users can add in their own value if you allow them to. This can be enabled through the tags option, and you can play around with an example in the documentation.
$("select").select2({
tags: true
});
By default, this will create an option that has the same text as the search term that they have entered. You can modify the object that is used if you are looking to mark it in a special way, or create the object remotely once it is selected.
$("select").select2({
tags: true,
createTag: function (params) {
return {
id: params.term,
text: params.term,
newOption: true
}
}
});
In addition to serving as an easy to spot flag on the object passed in through the select2:select event, the extra property also allows you to render the option slightly differently in the result. So if you wanted to visually signal the fact that it is a new option by putting "(new)" next to it, you could do something like this.
$("select").select2({
tags: true,
createTag: function (params) {
return {
id: params.term,
text: params.term,
newOption: true
}
},
templateResult: function (data) {
var $result = $("<span></span>");
$result.text(data.text);
if (data.newOption) {
$result.append(" <em>(new)</em>");
}
return $result;
}
});
For version 4+ check this answer below by Kevin Brown
In Select2 3.5.2 and below, you can use something like:
$(selector).select2({
minimumInputLength:1,
"ajax": {
data:function (term, page) {
return { term:term, page:page };
},
dataType:"json",
quietMillis:100,
results: function (data, page) {
return {results: data.results};
},
"url": url
},
id: function(object) {
return object.text;
},
//Allow manually entered text in drop down.
createSearchChoice:function(term, data) {
if ( $(data).filter( function() {
return this.text.localeCompare(term)===0;
}).length===0) {
return {id:term, text:term};
}
},
});
(taken from an answer on the select2 mailing list, but cannot find the link now)
Just for the sake of keep the code alive, I'm posting #rrauenza Fiddle's code from his comment.
HTML
<input type='hidden' id='tags' style='width:300px'/>
jQuery
$("#tags").select2({
createSearchChoice:function(term, data) {
if ($(data).filter(function() {
return this.text.localeCompare(term)===0;
}).length===0)
{return {id:term, text:term};}
},
multiple: false,
data: [{id: 0, text: 'story'},{id: 1, text: 'bug'},{id: 2, text: 'task'}]
});
Since many of these answers don't work in 4.0+, if you are using ajax, you could have the server add the new value as an option. So it would work like this:
User searches for value (which makes ajax request to server)
If value found great, return the option. If not just have the server append that option like this: [{"text":" my NEW option)","id":"0"}]
When the form is submitted just check to see if that option is in the db and if not create it before saving.
There is a better solution I think now
simply set tagging to true on the select options ?
tags: true
from https://select2.org/tagging
Improvent on #fmpwizard answer:
//Allow manually entered text in drop down.
createSearchChoice:function(term, data) {
if ( $(data).filter( function() {
return term.localeCompare(this.text)===0; //even if the this.text is undefined it works
}).length===0) {
return {id:term, text:term};
}
},
//solution to this error: Uncaught TypeError: Cannot read property 'localeCompare' of undefined
Thanks for the help guys, I used the code below within Codeigniter I I am using version: 3.5.2 of select2.
var results = [];
var location_url = <?php echo json_encode(site_url('job/location')); ?>;
$('.location_select').select2({
ajax: {
url: location_url,
dataType: 'json',
quietMillis: 100,
data: function (term) {
return {
term: term
};
},
results: function (data) {
results = [];
$.each(data, function(index, item){
results.push({
id: item.location_id,
text: item.location_name
});
});
return {
results: results
};
}
},
//Allow manually entered text in drop down.
createSearchChoice:function(term, results) {
if ($(results).filter( function() {
return term.localeCompare(this.text)===0;
}).length===0) {
return {id:term, text:term + ' [New]'};
}
},
});
I just stumbled upon this from Kevin Brown.
https://stackoverflow.com/a/30019966/112680
All you have to do for v4.0.6 is use tags: true parameter.
var text = 'New York Mills';
var term = 'new york mills';
return text.localeCompare(term)===0;
In most cases we need to compare values with insensitive register. And this code will return false, which will lead to the creation of duplicate records in the database. Moreover String.prototype.localeCompare () is not supported by browser Safary and this code will not work in this browser;
return this.text.localeCompare(term)===0;
will better replace to
return this.text.toLowerCase() === term.toLowerCase();
Here's my issue. I'm using checkboxes and lazy load via ajax. However, if you were to check a parent item without expanding it, none of the child nodes have been loaded so they don't get checked. How can I load all child and nested child nodes under a parent, and check them all, when they check the parent? Thanks, and this is what I have so far
$(function () {
// Create the tree inside the <div id="tree"> element.
$("#tree").fancytree({
source: { url: "/Home/GetData", cache: true }
, checkbox: true
, icons: false
, cache: true
, lazyLoad: function (event, data) {
var node = data.node;
data.result = {
url: "/Home/GetTreeViewData/" + node.key
, data: { mode: "children", parent: node.key }
, cache: true
};
}
, selectMode: 3
, select: function (event, data) { //here's where I need to load the children and any sub children for that node, if it has them, so they can be set to checked
}
, strings: {
loading: "Grabbing places and events…",
loadError: "Load error!"
},
})
});
Update
The reason I need to pull these client side is because this is a treeview that will be loading google map markers. So if I have a structure like
Parent1
->child1-1
->->child1-1-1
->child1-2
all of those child nodes load lazy. however, if they were to check the parent node 1, then I'd need to load the markers for all of those child nodes. That's why I'm looking for a way to recursively get all the children. Because it would be really hard to keep track of what markers have/haven't been added, if I don't just load the treeview items and check the boxes. Make sense?
I think you could use select event:
select: function(event, data) {
var node = data.node;
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);
});
}
}
}
That way if the child nodes haven't been loaded they will be loaded and selected after that.
First: maybe you don't even need to load all child nodes.
In selectMode: 3 a selected parent means 'all children are selected too', so if you post the top-most selected parent nodes to your server, the backend could handle it accordingly. The stopOnParent argument of the tree.getSelectedNodes method supports this as well.
Another option would be to fix the the selection state of child nodes
after a lazy parent was loaded:
$("#tree").fancytree({
checkbox: true,
selectMode: 3,
source: { url: "/getTreeData", cache: false },
lazyLoad: function(event, data) {
data.result = {
url: "/getTreeData",
data: {mode: "children", parent: node.key},
cache: false
};
},
loadChildren: function(event, data) {
// Apply parent's state to new child nodes:
data.node.fixSelection3AfterClick();
},
Update
If you really need to load the lazy child nodes when the parent is selected, you could try in addition to the above code
(untested)
$("#tree").fancytree({
...
select: function(event, data) {
if( data.node.isUndefined() ) {
data.node.load(); // triggers lazyLoad to load children
// alternative: node.expand()
}
},
Adding the unselect option, the Andrew's code would be:
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);
});
}
}
else{
if (node.isUndefined()) {
// Load and unselect all child nodes
node.load().done(function() {
node.visit(function(childNode) {
childNode.setSelected(false);
});
});
} else {
// Select all child nodes
node.visit(function(childNode) {
childNode.setSelected(false);
});
}
}
So I've been using jstree for a while now, but still can't get a handle on it, it's pretty much a headache but well, it was decided we were gonna work with it. The data I'm using comes from HTML (no JSON involved). The issue I'm having is that I'm not sure how to set that some nodes are not folders. Every node has a class and based on that class I'm able to change it's icon but if the user tries to send any node inside these nodes that are not supposed to be folders they will be able. I need to prevent this, one way or the other but every thing I've test so far has not work at all.
$("jstree").jstree({
"core": {
"animation": 0,
"check_callback": true
},
rules: { draggable: "all" },
"dnd": {
"drop_finish": function (data) {
if (data.o.attr("rel") === "ds") {
//update chart with new data here?
//using data.o.attr("id")
}
},
"drag_check": function (data) {
if (data.r.attr("rel") != "ds") {
return false;
}
return {
after: false,
before: false,
inside: true
};
}
},
"crrm": {
"move": {
"check_move": function (data) {
// alert(data.r.attr("id"));
if (data.r.attr("id") == "999") {
return false;
}
else {
return true;
}
}
}
},
"plugins": ["dnd", "crrm"]
});
That's what I'm using to create my tree. Also, I cannot disable drag and drop since some items should be moved if the user wants but obviously the user shouldn't be able to drag something into anything that is not a folder.
Thanks in advance for the help,
Regards,
Adrian.
I accomplished this by using the Types plugin jstree plugins. There you can define types of nodes and set the valid_children variable which types are allowed to be children. That means that users also cannot DnD nodes of the restricted type into the node.
In my example below I have a type "book" that could have "folder" and "file" nodes as children. The "file" type cannot have any children at all, because valid_childrenis defined empty.
$('#' + tree_id)
.jstree({
'core' : {
'check_callback' : true, //needed to enable create, remove, rename... events
"themes" : {
"stripes" : true
}
},
"types" : {
"book" : {
"valid_children" : ["folder", "file"],
"icon" : "img/1397762742_book.png"
},
"folder" : {
"valid_children" : ["folder", "file"],
"icon" : "img/1397751666_folder.png"
},
"file" : {
"valid_children" : [],
"icon" : "img/1397752227_page.png"
}
},
"contextmenu" : {
"items" : customMenu
},
"plugins" : ["sort", "dnd", "contextmenu", "types"]
});
The type attribute can be set when adding a new node
tree.create_node("#", {
"text" : "sample node",
"type" : "file"
});
or by using the set_type function. API