Kendo TreeView default selected node doesn't trigger onSelect - javascript

My php code creates a hierarchical json dataset for the hierarchicalDataSource used by a treeview.
In this php generating function, i set the very first leaf as selected=true... so when the treeview appears, the first leaf is automatically selected.
The problem is that when the user clicks any leaf, an event (onSelect) is triggered but it is not triggered for this automatic selection of the very first node that occurs when the treeview appears at UI creation time.
How can i fix this ?
UPDATE:
Made a Demo: http://jsbin.com/abapid/2/edit

If you want to programmatically trigger a select event, you should do:
$("#treeview").data("kendoTreeView").trigger("select");

For the benefits of others.... and thanks to OnaBai !... Here is the solution !
Kendo UI lacks many basic features other UI SDK always provide. One of them is an "onDisplay" kind of even that gets triggered once the widget gets painted. This would allow the application to react to specific case like in mine where the dataSource loaded a node containing a "selected = true" field.
The Kendo TreeView reacts by showing the node as selected but in most real world scenarios, the application would also need to react. That is why it would need to be called upon widget initialization to check if a node is selected and react accordingly.
The only hack we (OnaBai) found is to use the "DataBound" event as an "onDisplay" kind of event. The hic is that this event is fired each time a parent node as a child that got modified somehow. So it is called multiple times.
So here is the code to go around this limitation!
http://jsbin.com/ejabul/4/edit (Note that you must click the "Run with JS" to simulate a page reload)
$("#treeview").kendoTreeView({
dataSource:data,
dataTextField: "text",
select: onSelect,
dataBound: function (e) {
var uid = undefined;
var now = this.select();
if (now) {
var data = this.dataItem(now);
uid = data.uid;
if (uid && uid !== this.old_selected) {
alert("Bingo !");
}
console.log("data", data.uid);
}
this.old_selected = uid;
}
});
Explanation by OnaBai
Store in uid the Unique ID of the selected item. This ID is introduced by Kendo UI for most node, items, … that it creates. Initially I set it to undefined (just in case there is nothing selected)
"now" contains current selected node (if any).
If there is an element selected (now !== undefined) then I get the data item for the selected node and then get the UID for this node.
If there is a UID and if different than the UID of the previous selected node (that I store in a field that I just extended in the tree_view and I called old_selected) then I alert.
finally I save the UID of the selected node for the next time.
Basically what I try is to control that I do not alert two consecutive times for the same node and for controlling it I save the UID of the selected node from one iteration to the next.

Related

Material Table not reflecting changes on datasource

This is my first question in Stack Overflow. I'll try to be specific but I don't know how to keep this short, so this is going to be a long post. Sorry about that. I promise I searched and tried a lot of stuff before asking, but I'm kind of lost now.
I'm developing a simple app in Angular 6 to keep track of software requisites and the tests associated to those requisites.
I have a component, called RequisiteList, whose HTML part consists in a mat-table with an Array of my own Requisite model class as [dataSource]. This array is received as an #Input parameter, and it also has an #Output parameter which is an EventEmitter that notifies and passes to the parent component every time a Requisite on the list is clicked.
I make use of RequisiteList inside of ReqListMain, which is a component consisting on the list and a hierarchical tree for filtering. This component is working fine, showing, and filtering requisites as intended. This component also captures the #Output event of the list and passes it as an #Output to its parent.
Finally (for what it's related to this question), I have a TestView component that has both an instance of RequisiteList to show the requisites currently associated to current test, and an instance of ReqListMain to add new requisites to current test (like a "browser"). This TestView has an instance of the model class Pectest corresponding to the test that is being currently visualized, which has an array of Requisite.
The idea in this last component was that whenever a requisite of the "browser" list was clicked, it was added to the current test's list. In order to do that, in the callback method associated to the #Output event of the browser list, I tried to add the Requisite received as a parameter:
addrequisite(requisite: Requisite) {
this.currentTest.requisites.push(requisite);
console.log('Current test: ');
console.log(this.currentTest);
}
In the HTML part of TestView, I inserted the RequisiteList component like this:
<app-requisitelist [requisites]="currentTest.requisites" ngModel name="reqlistview"></app-requisitelist>
(The ngModel property is part of the things I've been trying, I'm not sure it's necessary).
The result is:
The clicked requisite is not shown in the list.
In the console output I can see the content of currentTest object, and I verify that clicked requisites are in fact added to the requisites array of that object, so the event fires and the object is passed upwards by the children components.
I'm not sure if my problem is that data binding is made by value (I don't think so, as I bind an Array, which is an object AFAIK), or the table is not detecting data changes (I've tried to force data change detection with ChangeDetector), or anything else.
You pass a array to the app-requisitelist component. This component waits this array changes to update the content. When you do this.currentTest.requisites.push(requisite), the array this.currentTest.requisites doesn't change, I mean, if you do
const tmp = this.currentTest.requisites;
this.currentTest.requisites.push(requisite)
if (tmp === this.currentTest.requisites) {
console.log('The arrays are the same');
}
You will get the log printed. So, I suggest do something like that:
addrequisite(requisite: Requisite) {
this.currentTest.requisites.push(requisite);
this.currentTest.requisites = this.currentTest.requisites.map(item => item);
console.log('Current test: ');
console.log(this.currentTest);
}
The inserted line forces this.currentTest.requisites to be a new array with the same content.

SharePoint listitem properties get lost after update via JS & CSOM

On my SharePoint there is a website where a single list-item is loaded based on a user's selection with JavaScript and CSOM. This list item has a total of ~60 properties defined in it's list definition.
In HTML input fields the user can modify most of the properties after jQuery filled in the loaded properties to their corresponding input fields. When the "save" button is pressed, the properties are collected from the inputs via jQuery and put into a simple JS-object (itemProps):
var itemprops = {
'foo': $('#foo-input').val(),
'bar': $('#bar-input').val()
}
Then, the following function gets called:
function updateListItem(itemProps, onItemAdded, onItemError) {
var list = web.get_lists().getByTitle('ListTitle');
var listItem = list.getItemById(id);
for (var propName in itemProps) {
if (itemProps.hasOwnProperty(propName)) {
listItem.set_item(propName, itemProps[propName]);
}
}
listItem.update();
context.executeQueryAsync(
function() {
onItemAdded(listItem);
},
onItemError
);
}
Debugging shows me, that the data in itemProps are valid. But sometimes (I can't reproduce that effect deterministically) some properties get lost and when I look at the list item in the list on the SharePoint some of the properties are empty, as if itemProps had null or "" associated to that property. When I first tried to debug this I simply created an item and saved it (correctly, with all properties) and then loaded and saved it again without modification but some properties got lost.
Other properties get updated correctly and sometimes this doesn't happen at all.
Is there any way to make sure this effect does't occur or at least to detect it and retry updating the data, before the user's inputs get lost?
I notice you're not invoking context.load(listItem) before calling context.executeQueryAsync() which might cause issues with the listItem object's values being stale or dehydrated.
The code in your question looks like it should still be setting the specified values correctly on the list item, although there may be some code later on (such as in the onItemAdded function) that's running into false assumptions or subtle data differences from the inadequately loaded list item.

How to avoid a full refresh of a widget on data change?

In icCube Reporting V6, I created a template widget based on iccube's example at https://www.iccube.com/your-first-html-widget/. So doing, i've put all the JS code in the After render event.
I'd like to have some variables defined and defaulted at widget creation time which could be changed during the use of the whole report and be used as parameters in this widget.
The problem is that at each data change, the widget is fully refreshed and reconstructed, Resetting my variables to default as well...
Is it missing a init widget event in which we could initialize the widget and the variables ? then the on Data Received and After render events would just be aimed to manage data manipulations and rendering ? (with regard to these variables)
Or… am I just doing it a wrong way ?
After Render Event is called everytime we get new data or one of the property of the widget has change. The latest is rare but possible if some of the properties have been defined using an event.
What we need is to add a status on this method to check if the widget has been already rendered. This can be done in two way, checking with jquery for the dom has changed or adding a javascript object.
The first one is easy with jquery and should be use if there is no need to cache javascript object. For the second one we've two solutions :
1) We can use jquery to bind a javascript object to a html element ( data ) :
var state = $node.data("widgetState");
if ( state ) {
// it's not the first time
state.times = state.times+1;
$node.html("It's the " + state.times + " time you clicked " );
}
else
{
// it's the first time
$node.html("It's the first time you clicked, don't be shy" );
state = { times : 1 } ;
$node.data("widgetState", state);
}
2) Since icCube 6.0.4 you can also use the context to get a local state object, context.widgetState()
You can check a working example here :

meteor.js & mongodb - how to pass _id of current element in spacebars each

CONTEXT
I'm trying to update an object in Venue collection to contain the _id of an object in Event collection. The current page displays one Event, so this.params._id yields the _id of the current Event... I can manipulate that object.
WHAT I NEED
I'm using Spacebars' each to loop through venues on the front end. Next to each Venue item is an "Add Venue to Event" button - I would like this button to add the _id of the corresponding Venue to the Event displayed on the current page.
PROBLEM
this refers to the Event on the current page, not the venue currently being looped through in Spacebars. I'm not sure how to pass the _id of the venue in the current stage of each so that I can add it to event being displayed on current page.
HELPER CODE
//ADD SUGGESTED VENUE TO EVENT
'click .suggest-venue': function(event) {
var currentEventId = this.params._id;
Meteor.call("updateEvent", currentEventId, {suggestedVenues: [Venues._id]})
toastr.success("Venue Suggestion Added");
}
This code yields suggestedVenues: null when you console.log the relevant Venue.
As you've identified, you need to pass two key pieces of data into your method: the Event ID and the Venue ID.
The meteor docs say this about event handlers:
The handler also receives some additional context data in this, depending on the context of the current element handling the event. In a template, an element's context is the data context where that element occurs, which is set by block helpers such as #with and #each.
So, your event handler should look something like this:
'click .suggest-venue': function() {
var eventId = Router.current().params._id; // Assuming you're using iron:router
var venueId = this._id; // 'this' is the current context within your template at the target element of this event handler
Meteor.call('updateEvent', eventId, venueId);
}
I'll leave the method itself as an exercise for the reader ;)
Hope that helps.
The problem (solved by rglover on TheMeteorChef Slack Channel) was that because I'm using the Differential Boiler Plate for Meteor, the design patterns led me to put template helpers within the template's controller. This meant that this was referring to the route instance.
SOLUTION
I moved the event handler to the js file accompanying each template, therefore this now refers to the template data, allowing me to get the _id of the element being clicked. As for the _id of the Event displayed on the page, Router.current().params._id allowed me to access it.
Hope this helps anyone else who may have gone down this path!

Meteor avoid double refreshes when third party widget changes it's own reactive datasource

I have a jsTree which I am trying to bi-directionally "connect" to a Meteor collection. Right now I automatically trigger a jsTree.refresh() whenever the collection updates with the help of .observeChanges:
FileTree.find().observeChanges({
added: function() {
$.jstree.reference('#fileTree').refresh();
},
changed: function() {
$.jstree.reference('#fileTree').refresh();
},
removed: function() {
$.jstree.reference('#fileTree').refresh();
}
});
I want to allow editing of the database by dragging things around in jsTree. Here's how it would look:
User drags element to new location.
jsTree updates the location of the element in the tree.
jsTree calls event handler
Event handler updates database
My problem is, if I understand everything correctly, is that the database update would trigger the observeChanges event that I set up earlier. That would cause yet another refresh of the tree. That causes an annoying flicker which would interrupt users. (i.e. the file browser would be unusable for about 0.75s after every drag/drop operation.)
How can I prevent this unneeded update, or is there a better way to structure my interface with jsTree that would prevent this problem.
Have you seen this?
https://github.com/tobkle/meteor-jstree-example
https://github.com/tobkle/meteor-jstree
It looks like he/she is using autorun vs. observeChanges but is mostly the same concept as yours:
Template.showtree.rendered = function(){
var self = this;
var nodes = [];
Nodes1 = $('#tree1').tree();
Nodes2 = $('#tree2').tree();
if (!self.handle){
self.handle = Meteor.autorun(function(){
// refresh trees on new data
nodes = Nodes.find();
Nodes1.tree('option', 'data', 'Projects');
Nodes2.tree('option', 'data', 'Contexts');
}); //self.handle
} // if (!self.handle)
}
If this is something only a single user would be editing (at a time) then perhaps you simply don't refresh it based on DB updates, and leave the tree as an interface to author, only.
If it needs to refresh based on DB updates (from other users, or just to be in sync), you might consider changing your workflow/logic:
the changed.jstree event, updates the database locally, blocking
the autorun triggers the refresh in jstree
which should, in theory, only result in 1 refresh vs. 2 or 3
Or if you really want to limit re-draws... you could find the old parent node and the new parent node and only us refresh_node(obj) to refresh just those changes nodes:
refresh_node (obj)
refreshes a node in the tree (reload its children) all opened nodes
inside that node are reloaded with calls to load_node.

Categories

Resources