Handling itemclick event on tree panel Extjs 4 - events

what I am trying to do is to get different reaction on a different tree LEAF click!
var myTree = Ext.create('Ext.tree.Panel',
store: store,
rootVisible: false,
border: false,
listeners: {
itemclick: function(index) {
var record = store.getAt(index);
alert(record);
}
}
});
I tried with index, to get the index of leaf, nothing.
I can get a reaction on a node click, but how to get a specific reaction on every leaf?
I also tried giving ID to the leafs, no luck???
Maybe a simple example of
itemclick: function(Ext.view.View this, Ext.data.Model record, HTMLElement item, Number index, Ext.EventObject e) {
}
Pleeasse help!!

The itemclick event listener's function param "index" does not point to your tree node's index. Like you mentioned in end of your question the syntax for the itemclick event is:
function(Ext.view.View this, Ext.data.Model record, HTMLElement item, Number index, Ext.EventObject e) {
}
Here is an example:
itemclick : function(view,rec,item,index,eventObj) {
// You can access your node information using the record object
// For example: record.get('id') or record.get('some-param')
if(r.get('id')=='SP') {
// I do my necessary logic here.. may be open a perticular window, grid etc..
}
if(r.get('id')=='CO') {
// I do my necessary logic here.. may be open a perticular window, grid etc..
}
}
And here is an example of my tree node's data:
{ text: 'SP Reports', id: 'SP', leaf: true},
{ text: 'CO Reports', id: 'CO', leaf: true},

Itemclick handler already gives you everyting you need:
itemclick(view, record, item, index, e ) {
var id = record.get('id');
// do something depending on the record data.
// console.log(record);
}

I was trying to do a generic treepanel's item click handler and to be able to get a custom field I added to the node object. This helped me out. I dunno if this is the standard and compatible ExtJs 4 way:
(Some Panels Here),
items: [{
xtype: 'treepanel',
listeners: {
itemclick: {
fn: function (view, record, item, index, e) {
console.log(record.raw.userData);
}
(removed...)

Related

Kendo listview - clicking elements that have been moved between listviews yields a different sender object structure

I'm seeing an issue where if I add new options to my listbox, the event.sender no longer has the same object structure when I click those newly moved options in their new listview.
I use an ajax event to bind data to the Kendo listview (this is in a method that gets triggered on document ready):
var myListBoxId = 'myListBoxHtmlId';
$.post('myUrl/myController/controllerMethod', dataToPost)
.done(function (response, status, jqxhr) {
$('#' + myListBoxId').kendoListBox({
dataSource: response.myListProperty,
connectWith: theOtherListBoxId,
dropSources: [theOtherListBoxId],
toolbar: {
position: "right",
tools: ["transferAllFrom", "transferAllTo",
"transferFrom", "transferTo"]
},
change: function (event) {
myJavascriptMethod(event);
},
dataTextField: 'propertyNameInObjectInMyPropertyList',
dataValueField: 'anotherPropertyNameInObjectInMyPropertyList'
});
You can see that it binds the 'myJavascriptMethod(event)' as the change event handler.
Here is how I'm accessing the event data in myJavascriptMethod(event):
myJavascriptMethod(event){
var selectedText = event.sender._target[0].innerHTML;
}
The problem is that if I modify the options (I'm using the 'transferFrom' and 'transferTo' to transfer options between two kendo listviews), the event.sender._target is null. I'm having difficulty figuring out what I should key onto that would work in all cases.
In addition to the example code above, I found this, which has more docs on listviews for .net-core:
https://github.com/telerik/kendo-ui-core/blob/master/docs/api/javascript/ui/listbox.md
When changing the return object in the C# List I was binding the datasource to in the response to the AJAX method, I also noticed that it doesn't really matter what type it is, as long as the property names match the listview's dataTextField and dataValueField.
The solution to properly getting the selected item from the listview that would work with both the originally bound options and options that have been moved between listviews was this (no changes required to the listview as shown in the question):
//reformatted this as Javascript
//for typescript it was of this format:
//static myTypeScriptEvent(event){
function myJavascriptEvent(event){
//This line was the fix / is key to the question:
var dataItem = event.sender.dataItem(event.sender.select());
}
Here's a minimum example of the AJAX method that binds the listview. Include a call to this method in the document.ready function (thingId is the id of some object that will have sub objects in a list which will then be bound to the listview). In my case, I'm using typescript, you may have to convert some of it to basic javascript as your needs require (it's pretty close, but it may need some slight changes - as indicated by the '$' characters, you'll also need to include jquery for this):
function bindListView( id: thingId ) {
var dataToPost = {
id: id
};
$.post('myUrl/myController/controllerMethod', dataToPost)
.done(function (response, status, jqxhr) {
$('#' + myListBoxId').kendoListBox({
dataSource: response.myList,
connectWith: theOtherListBoxId,
dropSources: [theOtherListBoxId],
toolbar: {
position: "right",
tools: ["transferAllFrom", "transferAllTo",
"transferFrom", "transferTo"]
},
change: function (event) {
myJavascriptMethod(event);
},
dataTextField: 'Id',
dataValueField: 'Name'
}); //end of listView bind
}); //end of $.post().done() function
} //end of bindListView() function
And finally, here's what your controller method should be like for the above:
I'll include a pseudo class and fill it with data. The return object is the "response" variable, and whatever your list names are is accessed like this in the datasource: response.listname. Finally, whatever the object types are in those lists, the property names on those objects just have to match the dataTextField and dataValueField of the listview.
//This will be the type of object I'm putting into the list that's
//going into the object I'm returning
public MyClass {
public int Id {get; set;} //so we change the listview's dataValueField to Id
public string Name {get; set;} //we change the listview's dataTextField to Name
}
//And this will be the overall type of object that will hold the list, will get
//returned, becoming the "response" in the AJAX .done:
public class MyReturnObject {
public List MyList {get; set;} //so we change the ListView's datasource to response.MyList
//It may become myList, so you may need to look at the response object in the debugger.
}
[HttpPost]
public JsonResult MyControllerMethod(int id)
{
//Create the return object, give it a new list, add things to the list
//so the ListView can be bound:
var myReturnObject = new MyReturnObject();
myReturnObject.Mylist = new List();
var object1 = new MyClass { Id = 1, Name = "Hello World" };
var object2 = new MyClass { Id = 2, Name = "Hello again" };
myReturnObject.MyList.Add(object1);
myReturnObject.MyList.Add(object2);
//Convert the return object and everything in it to Json and return it to
//The AJAX method:
return Json(myReturnObject);
}

Kendo UI - Tree List on rebind lost exapnd and collapse functionality

I am using a Kendo UI in angular Js and binded my TreeList with Json, where I have set my parent and child properties as given.
schema: {
model: {
id: "Id",
fields: {
parentId: { field: "ParentId", nullable: true }
}
}
}
And then I have a filter function on a button click which gets the required data from the json.
$scope.getFilteredData = function (id) {
var filterData = _.filter($scope.bookSource, (item) => { return item.BookId == id; });
if (filterData.length > 0) {
$scope.filteredDataSource = filterData;
$scope.ktlBookTreeList.setDataSource({
data: $scope.filteredDataSource
});
}
}
Although the data I get after the filter is correct , I dont have the expand collapse function any more. In one of the result set I got the parent record and two child records, even then the tree displayed it as separate rows, rather than with in the Expanded / collapsed rows.
Can you please guide me to understand what I am doing wrong here..
Probably you have to add the parent child model again, when you are resetting the data source.

kendo-datasource autoSync causes navigation placeholder to return to upper left corner of the grid

I'm using kendo-ui with angularJS 1.5 and I have a simple kendo-grid bound to a datasource with transport configured using functions as follows:
private buildDataSource() {
this.dataSource = new kendo.data.DataSource({
autoSync: true,
change: this.dataSourceChangeHandler.bind(this),
error: this.dataSourceErrorHandler.bind(this),
transport: {
read: this.dataSourceRead.bind(this),
create: this.dataSourceCreate.bind(this),
update: this.dataSourceUpdate.bind(this),
destroy: this.dataSourceDestroy.bind(this)
},
[...]
});
}
private dataSourceUpdate(e: kendo.data.DataSourceTransportUpdate) {
var updatedItem: KendoCosto = e.data;
[...]
e.success(updatedItem, undefined, undefined);
}
The grid options looks like this:
this.gridOptions = {
dataSource: this.dataSource,
change: this.gridChangeHandler.bind(this),
editable: {
mode: "incell",
confirmation: false
},
navigatable: true,
selectable: "multiple, cell",
allowCopy: true,
toolbar: [
"create"
],
[...]
The grid works fine and the read, create, update, destroy behave as expected.
My problem is that whenever I change a value in a grid's cell and hit enter, I would like to have keyboard navigation "placeholder" (the grid has navigatable: true) to remain on the edited cell, but it happens to be moved to the upper left corner cell.
This behavior happens only when dataSource's autoSync is set to true.
I've also tried to "set" the current cell via the ".current" method of the grid's api but it doesn't seem to work:
// this is bound to the grid's change event and it is supposed to
// store the currently selected cell in a property of the class
// that builds both the datasource and the grid
private gridChangeHandler(e: kendo.ui.GridNavigateEvent)
{
this.thisGrid = this.thisGrid || e.sender;
this.currentCell = e.sender.current();
}
// Then on the change event of the datasource I do
private dataSourceChangeHandler(event: kendo.data.DataSourceChangeEvent)
{
if (this.currentCell && this.thisGrid) {
this.thisGrid.select(this.currentCell);
this.currentCell = undefined;
}
}
any suggestions ?
Thanks in advance !
--- edit ---
The code I posted/pasted in the comment is absolutely unreadable so I'm repeating the code here:
To have your solution work, I had to modify my dataBound handler this way.
private gridDataBoundHandler(e: kendo.ui.GridDataBoundEvent) {
if (this.thisGrid && this.currentCell) {
setTimeout((() => {
// this.thisGrid.editCell(this.currentCell);
this.thisGrid.current(this.currentCell);
}).bind(this)
, 10);
}
}
without the timeout, the navigation placeholde was still resetting back to the upper left corner.
First, I think the grid change event is the wrong event to attach to as it only fires when the user selects a row/cell with the mouse...it will not fire on tab events.
So, I would use the grid save event, which fires after you make an edit and "commit" the change through enter, tab, mouse off, etc.
Second, the e.sender.current() includes the current identifying information like "grid_active_cell" and "k-state-focused" and "k-dirty-cell", etc. By the time you get to the dataSource change event, the cell has actually lost all that decoration and your this.currentCell is essentially pointing at a non-existent selector. So, you need to grab a more "permanent" identifier.
So, using the grid save event:
save: function (e) {
var row = $(e.sender.current()).closest("tr");
var colIdx = $("td", row).index(e.sender.current());
var model = e.sender.dataItem(row);
currentCell = "tr[data-uid='" + model.uid + "'] td:eq(" + colIdx + ")";
}
And then in the grid DATABOUND event(as the dataSource change event is still followed by events that change the cell focus to the top-left, but grid.dataBound is further in the chain and seems to work better):
dataBound: function (e) {
if (currentCell) {
grid.editCell(currentCell);
grid.current(currentCell);
}
}
Demo(with variable changes as I do not have your whole class, based on a kendo grid demo): http://dojo.telerik.com/#Stephen/OjAsU
Note that this solution(not my implementation, but your technique in general) will break tabbing from cell to cell, i.e. tabbing will commit the edit but the dataSource change event will always put the focus back on the just-edited cell instead of moving to the tabbed-to cell. This breaks user expectation of what tab does. So, you should consider trying to capture the enter key press only instead of relying on the grid events(which fire regardless of tab or enter).

ExtJS Menu parameter to pass

I am creating an ExtJS MVC application with a dynamic menu. I have several menu elements which will call the same screen (set an active card in a layout) but need to pass a different value in as a parameter each time. So that one card can display different data depending on the parameter which will act as a filter.
my menu elements have a click listener:
listeners: {
itemclick: function(view, record, item, index, e){
Ext.getCmp('contentpanel').layout.setActiveItem(record.getId());
}
}
the record Id will be the card I am setting as active.
How can I pass along a parameter to the card?
You could just add another attribute to the Panel if you want. For example:
Ext.define('YourApp.view.YourCardPanel' ,{
extend: 'Ext.panel.Panel',
id: 'contentpanel',
alias : 'widget.yourcardpanel',
layout: 'card',
newParam: null, // Just to know that this attribute exists but there is no need to declare it.
items: [],
changeLayout: function(){
if (this.newParam == 'new value') {
// Do your stuff
}
}
});
And then you could change the value of that parameter.
listeners: {
itemclick: function(view, record, item, index, e){
var cardPanel = Ext.getCmp('contentpanel');
cardPanel.newParam = 'new value';
cardPanel.changeLayout();
cardPanel.layout.setActiveItem(record.getId());
}
}

Kendo AutoComplete - force users to make valid selection

I've got a few Kendo AutoComplete fields linked to remote data (hundreds of possibilities, so DropDownList is not an option).
How can I force users to make a selection from the displayed list?
I'm also retrieving the additional data that's returned from the data source, e.g.
$("#station").kendoAutoComplete({
dataSource: stationData,
minLength: 2,
dataTextField: 'name',
select: function(e){
var dataItem = this.dataItem(e.item.index());
console.dir(dataItem);
}
});
I'm doing additional stuff with the data in dataItem and it needs to be a valid selection.
Thanks
SOLVED:
I think I was possibly over-complicating things. The answer is pretty simple and is posted below.
var valid;
$("#staton").kendoAutoComplete({
minLength: 2,
dataTextField: "name",
open: function(e) {
valid = false;
},
select: function(e){
valid = true;
},
close: function(e){
// if no valid selection - clear input
if (!valid) this.value('');
},
dataSource: datasource
});
This method allows users to type whatever they like into the AutoComplete if list not opens. There are two corrections to fix this:
initialize variable valid as false:
var valid = false;
Check if no valid selection in change event, but not in close:
...
change: function(e){ if (!valid) this.value(''); }
The answer the OP posted, in addition to the answer suggested by #Rock'n'muse are definitely good propositions, but both miss an important and desired functional aspect.
When utilizing the solution given by #Mat and implementing the change-vice-close suggestion from #Rock'n'muse, the typed-in value indeed clears from the widget if no selection is made from the filtered data source. This is great; however, if the user types in something valid and selects a value from the filtered list, then places the cursor at the end of the value and types something which now invalidates the value (doesn't return any valid selections from the data source), the typed-in value is not cleared from the widget.
What's happening is that the isValid value remains true if the previously typed-in (and valid) value should be altered. The solution to this is to set isValid to false as soon as the filtering event is triggered. When the user changes the typed-in value, the widget attempts to filter the data source in search of the typed-in value. Setting isValid to false as soon as the filter event is triggered ensures a "clean slate" for the change event as suggested by the solution from #Rock'n'muse.
Because we're setting isValid to false as soon as the filtering event is triggered, we do not need to do so in the open event (as data source filtering must occur before the user will ever see an option to select). Because of this, the open event binding was removed from #Mat's solution. This also means the initial assignment of false upon declaration of isValid is superfluous, but variable assignment upon declaration is always a good idea.
Below is the solution from #Mat along with the suggestions from #Rock'n'muse and with the filtering implementation applied:
var isValid = false;
$("#staton").kendoAutoComplete({
minLength: 2,
dataTextField: "name",
select: function () {
valid = true;
},
change: function (e) {
// if no valid selection - clear input
if (!valid) {
e.sender.value("");
}
},
filtering: function () {
valid = false;
},
dataSource: datasource
});
As an addendum, using the select event binding to set and evaluate a simple boolean value as #Mat proposes is much cleaner, simpler and faster than using a jQuery $.each(...) on the data source in order to make sure the typed-in value matches an actual item of the data source within the change event. This was my first thought at working a solution before I found the solution from #Mat (this page) and such is my reasoning for up-voting his solution and his question.
Perhaps, can you make your own validation by using the blur event :
$("#station").blur(function() {
var data = stationData,
nbData = data.length,
found = false;
for(var iData = 0; iData < nbData; iData++) {
if(this.value === data[iData].yourfieldname) // replace "yourfieldname" by the corresponding one if needed
found = true;
}
console.log(found);
});
You can check this fiddle.
You most likely need some custom logic which to intercept each key stroke after the min length of two symbols is surpassed, and prevent the option to enter a character which makes the user string not match any of the items from the autocomplete list.
For this purpose intercept the change event of the Kendo autocomplete and compare the current input value from the user against the items from the filtered list.
Hope this helps,
$("#autocomplete_id").val("");
$("#autocomplete").kendoAutoComplete({
dataSource: datasource,
minLength: 1,
dataTextField: "catname",
dataValueField:"id",
select: function(e) {
var dataItem = this.dataItem(e.item.index());
$("#autocomplete_id").val(dataItem.id);
},
dataBound: function(e){
$("#autocomplete_id").val("");
}
});
FYI, autocomplete_id is a hidden field to store the value of the autocomplete. - Sometimes, we would like to have the dataValueField other than dataTextField. So, it serves its purpose.
In this you can get the value of the autocomplete "id" from the element autocomplete_id - which is dataValueField from serverside.
In databound, its values is set to null, and on select, it is assigned the "id" value.
Although the accepted answer works, it is not the best solution.
The solution provided doesn't take into account if the user enters a value before even the kendo AutoComplete widget triggers the open event. In result, the value entered is not forced and therefore, the entry/selection is invalid.
My approach assumes that the application is running in MVC and the array is passed in ViewData. But this can be changed to suit your environment.
My approach:
var validSelect, isSelected;
$("#staton").kendoAutoComplete({
minLength: 2,
filter: "startswith",
dataTextField: "name",
filtering: function(e) {
validSelect = false;
dataArr = #Html.Raw(Json.Encode(ViewData["allStatons"]));
// for loop within the ViewData array to find for matching ID
for (var i = 0; i < dataArr .length; i++){
if (dataArr[i].ID.toString().match("^" + $("#staton").val())) {
validSelect = true;
break;
}
}
// if value entered was not found in array - clear input
if (!validSelect) $("#staton").val("");
},
select: function(e){
isSelected = true;
},
close: function(e){
// if selection is invalid or not selected from the list - clear input
if (!validSelect || !isSelected) $("#staton").val("");
},
dataSource: datasource
});
As you can see, this approach requires the array from the server side on load so that it can match during the filtering event of the widget.
I found this on Telerik's site by just using the change event. For me this works best. I added the "value === ''" check. This will catch when the user "clears" the selection.
Here's the link to the full article.
$("#countries").kendoAutoComplete({
dataSource: data,
filter: "startswith",
placeholder: "Select country...",
change: function() {
var value = this.value();
if (value === '') return;
var found = false;
var data = this.dataSource.view();
for(var idx = 0, length = data.length; idx < length; idx++) {
if (data[idx] === value) {
found = true;
break;
}
}
if (!found) {
this.value("");
alert("Custom values are not allowed");
}
}
});
In my AutoComplete Change event I check for a selected item and clear the cell if not selected.
function myAutoComplete_OnChange(e)
{
if (this.dataItem())
{
// Don't use filtered value as display, instead use this value.
e.sender.element[0].value = this.dataItem().WidgetNumber;
}
else
{
var grid = $("#grid").data("kendoGrid");
grid.dataItems()[grid.select().index()].WidgetKey = null;
grid.dataItems()[grid.select().index()].WidgetNumber = null;
}
}

Resources