Unable to call a function when a node is checked/unchecked in JStree - jstree

I have a requirement to perform a recursive action whenever a node is checked/unchecked in JStree.
I tried to do the following, but the function is never invoked. How should I call a function from JStree on checking/unchecking a node?
$('#jstree').on("uncheck_node.jstree", function (e, data)
{
subfunction(selectednodetouncheck, data);
};
function subfunction(para1, para2)
{
//some operation;
};
The subfunction is never called.... Please let me know how to call this function.

You should use select_node and deselect_node as below. If you want to check and select a node separately, you need to set the tie_selection param of checkbox plugin in the tree config as false and select/deselect a node manually.
Check demo - Fiddle Demo
$("#jstree")
.jstree({
core: {
data: coredata,
check_callback: true
},
plugins: ["checkbox"],
checkbox: {
tie_selection: false
}
})
.on("select_node.jstree deselect_node.jstree", function(e, data) {
subfunction(data);
})
.on("check_node.jstree uncheck_node.jstree", function(e, data) {
subfunction2(data);
});
function subfunction(data) {
//some operation;
alert('You got me selected: ' + data.node.state.selected)
};
function subfunction2(data) {
//some operation;
alert('You got me checked: ' + data.node.state.checked);
// now you need to decide what you want to select or not
// and do it manually, e.g. like below
var selectFlag = true;
if (selectFlag) {
var action = data.node.state.checked ? 'select_node' : 'deselect_node';
$("#jstree").jstree(action, data.node);
}
};

Related

Mapbox GL JS: Style is not done loading

I have a map wher we can classically switch from one style to another, streets to satellite for example.
I want to be informed that the style is loaded to then add a layer.
According to the doc, I tried to wait that the style being loaded to add a layer based on a GEOJson dataset.
That works perfectly when the page is loaded which fires map.on('load') but I get an error when I just change the style, so when adding layer from map.on('styledataloading'), and I even get memory problems in Firefox.
My code is:
mapboxgl.accessToken = 'pk.token';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v10',
center: [5,45.5],
zoom: 7
});
map.on('load', function () {
loadRegionMask();
});
map.on('styledataloading', function (styledata) {
if (map.isStyleLoaded()) {
loadRegionMask();
}
});
$('#typeMap').on('click', function switchLayer(layer) {
var layerId = layer.target.control.id;
switch (layerId) {
case 'streets':
map.setStyle('mapbox://styles/mapbox/' + layerId + '-v10');
break;
case 'satellite':
map.setStyle('mapbox://styles/mapbox/satellite-streets-v9');
break;
}
});
function loadJSON(callback) {
var xobj = new XMLHttpRequest();
xobj.overrideMimeType("application/json");
xobj.open('GET', 'regions.json', true);
xobj.onreadystatechange = function () {
if (xobj.readyState == 4 && xobj.status == "200") {
callback(xobj.responseText);
}
};
xobj.send(null);
}
function loadRegionMask() {
loadJSON(function(response) {
var geoPoints_JSON = JSON.parse(response);
map.addSource("region-boundaries", {
'type': 'geojson',
'data': geoPoints_JSON,
});
map.addLayer({
'id': 'region-fill',
'type': 'fill',
'source': "region-boundaries",
'layout': {},
'paint': {
'fill-color': '#C4633F',
'fill-opacity': 0.5
},
"filter": ["==", "$type", "Polygon"]
});
});
}
And the error is:
Uncaught Error: Style is not done loading
at t._checkLoaded (mapbox-gl.js:308)
at t.addSource (mapbox-gl.js:308)
at e.addSource (mapbox-gl.js:390)
at map.js:92 (map.addSource("region-boundaries",...)
at XMLHttpRequest.xobj.onreadystatechange (map.js:63)
Why do I get this error whereas I call loadRegionMask() after testing that the style is loaded?
1. Listen styledata event to solve your problem
You may need to listen styledata event in your project, since this is the only standard event mentioned in mapbox-gl-js documents, see https://docs.mapbox.com/mapbox-gl-js/api/#map.event:styledata.
You can use it in this way:
map.on('styledata', function() {
addLayer();
});
2. Reasons why you shouldn't use other methods mentioned above
setTimeout may work but is not a recommend way to solve the problem, and you would got unexpected result if your render work is heavy;
style.load is a private event in mapbox, as discussed in issue https://github.com/mapbox/mapbox-gl-js/issues/7579, so we shouldn't listen to it apparently;
.isStyleLoaded() works but can't be called all the time until style is full loaded, you need a listener rather than a judgement method;
Ok, this mapbox issue sucks, but I have a solution
myMap.on('styledata', () => {
const waiting = () => {
if (!myMap.isStyleLoaded()) {
setTimeout(waiting, 200);
} else {
loadMyLayers();
}
};
waiting();
});
I mix both solutions.
I was facing a similar issue and ended up with this solution:
I created a small function that would check if the style was done loading:
// Check if the Mapbox-GL style is loaded.
function checkIfMapboxStyleIsLoaded() {
if (map.isStyleLoaded()) {
return true; // When it is safe to manipulate layers
} else {
return false; // When it is not safe to manipulate layers
}
}
Then whenever I swap or otherwise modify layers in the app I use the function like this:
function swapLayer() {
var check = checkIfMapboxStyleIsLoaded();
if (!check) {
// It's not safe to manipulate layers yet, so wait 200ms and then check again
setTimeout(function() {
swapLayer();
}, 200);
return;
}
// Whew, now it's safe to manipulate layers!
the rest of the swapLayer logic goes here...
}
Use the style.load event. It will trigger once each time a new style loads.
map.on('style.load', function() {
addLayer();
});
My working example:
when I change style
map.setStyle()
I get error Uncaught Error: Style is not done loading
This solved my problem
Do not use map.on("load", loadTiles);
instead use
map.on('styledata', function() {
addLayer();
});
when you change style, map.setStyle(), you must wait for setStyle() finished, then to add other layers.
so far map.setStyle('xxx', callback) Does not allowed. To wait until callback, work around is use map.on("styledata"
map.on("load" not work, if you change map.setStyle(). you will get error: Uncaught Error: Style is not done loading
The current style event structure is broken (at least as of Mapbox GL v1.3.0). If you check map.isStyleLoaded() in the styledata event handler, it always resolves to false:
map.on('styledata', function (e) {
if (map.isStyleLoaded()){
// This never happens...
}
}
My solution is to create a new event called "style_finally_loaded" that gets fired only once, and only when the style has actually loaded:
var checking_style_status = false;
map.on('styledata', function (e) {
if (checking_style_status){
// If already checking style status, bail out
// (important because styledata event may fire multiple times)
return;
} else {
checking_style_status = true;
check_style_status();
}
});
function check_style_status() {
if (map.isStyleLoaded()) {
checking_style_status = false;
map._container.trigger('map_style_finally_loaded');
} else {
// If not yet loaded, repeat check after delay:
setTimeout(function() {check_style_status();}, 200);
return;
}
}
I had the same problem, when adding real estate markers to the map. For the first time addding the markers I wait till the map turns idle. After it was added once I save this in realEstateWasInitialLoaded and just add it afterwards without any waiting. But make sure to reset realEstateWasInitialLoaded to false when changing the base map or something similar.
checkIfRealEstateLayerCanBeAddedAndAdd() {
/* The map must exist and real estates must be ready */
if (this.map && this.realEstates) {
this.map.once('idle', () => {
if (!this.realEstateWasInitialLoaded) {
this.addRealEstatesLayer();
this.realEstateWasInitialLoaded = true
}
})
if(this.realEstateWasInitialLoaded) {
this.addRealEstatesLayer();
}
}
},
I ended up with :
map.once("idle", ()=>{ ... some function here});
In case you have a bunch of stuff you want to do , i would do something like this =>
add them to an array which looks like [{func: function, param: params}], then you have another function which does this:
executeActions(actions) {
actions.forEach((action) => {
action.func(action.params);
});
And at the end you have
this.map.once("idle", () => {
this.executeActions(actionsArray);
});
I have created simple solution. Give 1 second for mapbox to load the style after you set the style and you can draw the layer
map.setStyle(styleUrl);
setTimeout(function(){
reDrawMapSourceAndLayer(); /// your function layer
}, 1000);
when you use map.on('styledataloading') it will trigger couple of time when you changes the style
map.on('styledataloading', () => {
const waiting = () => {
if (!myMap.isStyleLoaded()) {
setTimeout(waiting, 200);
} else {
loadMyLayers();
}
};
waiting();
});

kendo-ui autocomplete extend

I'm trying to extend the kendo-ui autocomplete control: I want the search start when te user hit enter, so basically I've to check the user input on keydown event.
I've tried to catch the keydown event with this code:
(function($) {
ui = kendo.ui,
Widget = ui.Widget
var ClienteText = ui.AutoComplete.extend({
init: function(element,options) {
var that=this;
ui.AutoComplete.fn.init.call(this, element, options);
$(this).bind('keydown',function(e){ console.log(1,e); });
$(element).bind('keydown',function(e){ console.log(2,e); });
},
options: {
[...list of my options...]
},
_keydown: function(e) {
console.log(3,e);
kendo.ui.AutoComplete.fn._keydown(e);
}
});
ui.plugin(ClienteText);
})(jQuery);
None of the binded events gets called, only the _keydown, and then I'm doing something wrong and cannot call the autocomplete "normal" keydown event.
I've seen a lot of examples that extend the base widget and then create a composite widget, but I'm not interested in doing that, I only want to add a functionality to an existing widget.
Can someone show me what I'm doing wrong?
Thank you!
What about avoiding the extend and take advantage of build in options and methods on the existing control : http://jsfiddle.net/vojtiik/Vttyq/1/
//create AutoComplete UI component
var complete = $("#countries").kendoAutoComplete({
dataSource: data,
filter: "startswith",
placeholder: "Select country...",
separator: ", ",
minLength: 50 // this is to be longer than your longest char
}).data("kendoAutoComplete");
$("#countries").keypress(function (e) {
if (e.which == 13) {
complete.options.minLength = 1; // allow search
complete.search($("#countries").val());
complete.options.minLength = 50; // stop the search again
}
});
This code actually work:
(function($) {
ui = kendo.ui,
ClienteText = ui.AutoComplete.extend({
init: function(element,options) {
ui.AutoComplete.fn.init.call(this, element, options);
$(element).bind('keydown',function(e){
var kcontrol=$(this).data('kendoClienteText');
if (e.which === 13) {
kcontrol.setDataSource(datasource_clientes);
kcontrol.search($(this).val());
} else {
kcontrol.setDataSource(null);
}
});
},
options: {
name: 'ClienteText',
}
});
ui.plugin(ClienteText);
})(jQuery);
but I don't know if it's the correct way to do it.

AngularJS: Move to next form input element after successful validation

I have written a custom directive for validation of my form fields. When certain criteria are met (i.e. it is dirty and valid), I want to set the focus automatically to the next input element. This is a requirement from my users, such that they can move through the forms most efficiently.
The simplified directive looks like this:
directive('custom', ['$parse', function($parse) {
return {
restrict: 'A',
require: ['ngModel', '^ngController'],
link: function(scope, element, attrs, ctrls) {
var model=ctrls[0], form=ctrls[1];
scope.next = function(){
return model.$valid
}
scope.$watch(scope.next, function(newValue, oldValue){
if (newValue && model.$dirty){
???
}
})
Now my question is: how can I identify
- the next input element (which is the next sibling) or possibly via the tabindex
- and focus on it
without using Jquery?
For me, it is currently not clear, how to get to the next input element from the available "scope" or "element" attributes without Jquery; and JQlite does nothave a "focus" method. Basically, I need a working substitute for ??? in my code.
Any help is highly appreciated. Thanks
Juergen
You can use [0] to get the underlying input element (which has a focus() function) from the angular/jqLite object (which doesn't).
app.directive('custom', ['$parse', function($parse) {
return {
restrict: 'A',
require: ['ngModel'],
link: function(scope, element, attrs, ctrls) {
var model=ctrls[0], form=ctrls[1];
scope.next = function(){
return model.$valid;
}
scope.$watch(scope.next, function(newValue, oldValue){
if (newValue && model.$dirty)
{
var nextinput = element.next('input');
if (nextinput.length === 1)
{
nextinput[0].focus();
}
}
})
}
}
}])
http://jsfiddle.net/Y2XLA/
element.next().focus() might not work if you have a complex form and input are nested into different divs.
I ended writing this directive (here I move the focus on Enter, but can be adapted to whatever event):
.directive('enterToTab', function($timeout) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var procAttr = 'data-ett-processed';
$timeout(function() { // Use $timeout to run the directive when the DOM is fully rendered
var formElements = element[0].querySelectorAll('input:not([' + procAttr + '="true"]), select:not([' + procAttr + '="true"]), textarea:not([' + procAttr + '="true"])');
// Run through all elements in form
var formElementsLength = formElements.length;
for (var i = 0; i < formElementsLength; i++) { // Add tabindex attribute
formElements[i].setAttribute('tabindex', i + 1);
// Go to next element on Enter key press
formElements[i].addEventListener('keypress', function(event) {
if (event.keyCode === 13) { // Enter
// Prevent Angular from validating all the fields and submitting
if (event.target.tagName !== 'TEXTAREA') { // Not on textarea, otherwise not possible to add new line
event.stopPropagation();
event.preventDefault();
}
var nextIndex = parseInt(event.target.getAttribute('tabindex')) + 1;
// Move focus to next element
// TODO: find next visible element
var nextElem = element[0].querySelector('[tabIndex="' + (nextIndex) + '"]');
if (nextElem) {
nextElem.focus();
}
}
});
formElements[i].setAttribute(procAttr, true); // Add attribute to prevent adding 2 listeners on same element
}
});
}
};
});
Event should be in HTML component (keypress) = "keyFocus($event)"
Method shoulb be like .ts file.
keyFocus(input1){
input1.srcElement.nextElementSibling.focus();
}
AngularJS already contains a light version of jQuery so you can as well use it...
http://docs.angularjs.org/api/angular.element
You could try something like this:
element.next().focus()

Codemirror remote autcompletion

Codemirror has a nice example for autocompletion : link.
The idea is to have server side autocompletion (e.g. Ajax service that autocompletes Java). Does somebody has an example of a remote autocompletion with codemirror ?
I've been able to get async completions working with CodeMirror 5.3's show-hint.js by using the following (es6 flavoured, so for es3, replace let with var and the => with function)
While there's no actual ajax, it's hopefully obvious how to hook that in, just invoke callback in your ajax calls completion handler.
CodeMirror.registerHelper('hint', 'ajax', (mirror, callback) => {
let words = ['foo', 'bar', 'baz'];
let cur = mirror.getCursor();
let range = mirror.findWordAt(cur);
let fragment = mirror.getRange(range.anchor, range.head);
callback({
list: words.filter(w => w.indexOf(fragment) === 0),
from: range.anchor,
to: range.head
});
});
CodeMirror.hint.ajax.async = true;
CodeMirror.commands.autocomplete = function(mirror) {
mirror.showHint({ hint: CodeMirror.hint.ajax });
};
Key is to set the async property as the docs tells you to:
It is possible to set the async property on a hinting function to
true, in which case it will be called with arguments (cm, callback,
?options), and the completion interface will only be popped up when
the hinting function calls the callback
// javascript code
var editor;
function createEditor (data) {
editor = CodeMirror.fromTextArea(myTextarea, {
mode: "text/x-sql",
extraKeys: {"Ctrl-Q": "autocomplete"},
hint: CodeMirror.hint.sql,
hintOptions: {
tables: data ? data : {}
}
})
}
(function createEditorWithRemoteData () {
$.ajax({
type:'POST',
dataType:'json',
url:'data.json',
success:createEditor,
error:function () {}
})
})();
// data.json
{
"table1": [ "col_A", "col_B", "col_C" ],
"table2": [ "other_columns1", "other_columns2" ]
}

How to access old and new values before submitting with jeditable

I have a field being updated by jeditable. I want to output a warning message before submitting updates if the value is being reduced (which would result in data being lost), but not if it's being increased.
This seems a good candidate for jeditable's onsubmit function, which I can trigger happily. I can get the new value from $('input', this).val(), but how do I get the original value to which to compare it in this context?
...
Since posting the above explanation / question, I've come up with a solution of sorts. By changing the invokation in jquery.ready from
$('#foo').editable(...);
to
$('#foo').hover(function(){
var old_value = $(this).text();
$(this).editable('ajax.php', {
submitdata {'old_value':old_value}
});
});
I can use settings.submitdata.old_value in the onsubmit method.
But there surely has to be a better way? jeditable must still have the old value tucked away somewhere in order to be able to revert it. So the question becomes how can I access that from the onsubmit function?
Many thanks in advance for any suggestions.
A much easier solution would be to add this line to your submitdata variable
"submitdata": function (value, settings) {
return {
"origValue": this.revert
};
}
Here is my editable (it is using the submitEdit function):
$(function () {
$('.editable').editable(submitEdit, {
indicator: '<img src="content/images/busy.gif">',
tooltip: '#Html.Resource("Strings,edit")',
cancel: '#Html.Resource("Strings,cancel")',
submit: '#Html.Resource("Strings,ok")',
event: 'edit'
});
/* Find and trigger "edit" event on correct Jeditable instance. */
$(".edit_trigger").bind("click", function () {
$(this).parent().prev().trigger("edit");
});
});
In submitEdit origvalue is the original value before the edit
function submitEdit(value, settings) {
var edits = new Object();
var origvalue = this.revert;
var textbox = this;
var result = value;
// sb experiment
var form = $(this).parents('form:first');
// end experiment
edits["field"] = form.find('input[name="field"]').val();
edits["value"] = value;
var returned = $.ajax({
url: '#Url.Action("AjaxUpdate")',
type: "POST",
data: edits,
dataType: "json",
complete: function (xhr, textStatus) {
// sever returned error?
// ajax failed?
if (textStatus != "success") {
$(textbox).html(origvalue);
alert('Request failed');
return;
}
var obj = jQuery.parseJSON(xhr.responseText);
if (obj != null && obj.responseText != null) {
alert(obj.responseText);
$(textbox).html(origvalue);
}
}
});
return (result);
}