kendoui angular grid selection event - javascript

I am trying to handle a selection event from a KendoUI Grid in AngularJS.
I have got my code working as per below. However it feels like a really nasty way of having to get the data for the selected row. Especially using _data. Is there a better way of doing this? Have I got the wrong approach?
<div kendo-grid k-data-source="recipes" k-selectable="true" k-sortable="true" k-pageable="{'refresh': true, 'pageSizes': true}"
k-columns='[{field: "name", title: "Name", filterable: false, sortable: true},
{field: "style", title: "Style", filterable: true, sortable: true}]' k-on-change="onSelection(kendoEvent)">
</div>
$scope.onSelection = function(e) {
console.log(e.sender._data[0].id);
}

please try the following:
$scope.onSelection = function(kendoEvent) {
var grid = kendoEvent.sender;
var selectedData = grid.dataItem(grid.select());
console.log(selectedData.id);
}

Joining the party rather late, there is a direct way to do it without reaching for the grid object:
on the markup:
k-on-change="onSelection(data)"
in the code:
$scope.onSelection = function(data) {
// no need to reach the for the sender
}
note that you may still send selected, dataItem, kendoEvent or columns if needed.
consult this link for more details.

Directive for two-way binding to selected row. Should be put on the same element
as kendo-grid directive.
Typescript version:
interface KendoGridSelectedRowsScope extends ng.IScope {
row: any[];
}
// Directive is registered as gridSelectedRow
export function kendoGridSelectedRowsDirective(): ng.IDirective {
return {
link($scope: KendoGridSelectedRowsScope, element: ng.IAugmentedJQuery) {
var unregister = $scope.$parent.$on("kendoWidgetCreated", (event, grid) => {
if (unregister)
unregister();
// Set selected rows on selection
grid.bind("change", function (e) {
var selectedRows = this.select();
var selectedDataItems = [];
for (var i = 0; i < selectedRows.length; i++) {
var dataItem = this.dataItem(selectedRows[i]);
selectedDataItems.push(dataItem);
}
if ($scope.row != selectedDataItems[0]) {
$scope.row = selectedDataItems[0];
$scope.$root.$$phase || $scope.$root.$digest();
}
});
// Reset selection on page change
grid.bind("dataBound", () => {
$scope.row = null;
$scope.$root.$$phase || $scope.$root.$digest();
});
$scope.$watch(
() => $scope.row,
(newValue, oldValue) => {
if (newValue !== undefined && newValue != oldValue) {
if (newValue == null)
grid.clearSelection();
else {
var index = grid.dataSource.indexOf(newValue);
if (index >= 0)
grid.select(grid.element.find("tr:eq(" + (index + 1) + ")"));
else
grid.clearSelection();
}
}
});
});
},
scope: {
row: "=gridSelectedRow"
}
};
}
Javascript version
function kendoGridSelectedRowsDirective() {
return {
link: function ($scope, element) {
var unregister = $scope.$parent.$on("kendoWidgetCreated", function (event, grid) {
if (unregister)
unregister();
// Set selected rows on selection
grid.bind("change", function (e) {
var selectedRows = this.select();
var selectedDataItems = [];
for (var i = 0; i < selectedRows.length; i++) {
var dataItem = this.dataItem(selectedRows[i]);
selectedDataItems.push(dataItem);
}
if ($scope.row != selectedDataItems[0]) {
$scope.row = selectedDataItems[0];
$scope.$root.$$phase || $scope.$root.$digest();
}
});
// Reset selection on page change
grid.bind("dataBound", function () {
$scope.row = null;
$scope.$root.$$phase || $scope.$root.$digest();
});
$scope.$watch(function () { return $scope.row; }, function (newValue, oldValue) {
if (newValue !== undefined && newValue != oldValue) {
if (newValue == null)
grid.clearSelection();
else {
var index = grid.dataSource.indexOf(newValue);
if (index >= 0)
grid.select(grid.element.find("tr:eq(" + (index + 1) + ")"));
else
grid.clearSelection();
}
}
});
});
},
scope: {
row: "=gridSelectedRow"
}
};
}

A quick example of how to do this with an angular directive.
Note here that I'm getting the reference to the underlying kendo grid through the click event and the DOM handle.
//this is a custom directive to bind a kendo grid's row selection to a model
var lgSelectedRow = MainController.directive('lgSelectedRow', function () {
return {
scope: {
//optional isolate scope aka one way binding
rowData: "=?"
},
link: function (scope, element, attributes) {
//binds the click event and the row data of the selected grid to our isolate scope
element.bind("click", function(e) {
scope.$apply(function () {
//get the grid from the click handler in the DOM
var grid = $(e.target).closest("div").parent().data("kendoGrid");
var selectedData = grid.dataItem(grid.select());
scope.rowData = selectedData;
});
});
}
};
});

I would suggest to use like this, I was also getting undefined when I upgraded my application from angular 7 to 15. Now I get event details like this
public selectedRowChangeAction(event:any): void {
console.log(event.selectedRows[0].dataItem.Id); }
event has selected Row at its 0 index and you can have dataItem as first object and then you can have all object details whatever you have for example Id, Name,Product details whatever you want to select, Something like you can see in picture

Related

Modified code is not correct for the getvalue and setvalue

My code was working fine but they wanted to change my code....
they wanted to attach setValue and getValue added directly to
footballPanel instead of sports grid,
but after adding it the code is not working fine...
can you tell me why its not working....
providing my modified code below...
the UI action here I am performing is there are two radio buttons,
when I click each radio button two different grids open
in one of the grid we add value, when i switch back to another radio
button the values in another grid disappears but it should not
disappear...
after I modified the code the values disappear, can you tell me why?
Only part of modified code here
else {
this.setDisabled(true);
this.addCls("sports-item-disabled");
if (sportsGrid.store.getCount() > 0) {
var footballPanel = sportsGrid.up('panel');
footballPanel.holdValue = footballPanel.getValue();
footballPanel.setValue();
sportsGrid.addCls("sports-item-disabled");
}
}
Whole modified code:
sportsContainerHandler: function(radioGroup, newValue, oldValue, options) {
var sportsCustomParams = options.sportsCustomParams;
var uiPage = this.up('football-ux-sports-ui-page');
var SportsDefinition = metamodelsHelper.getSportsDefinition(
uiPage, sportsCustomParams.SportsHandlerDefinitionId);
var sportsFieldParam = SportsDefinition.params['sportsMultiFieldName'];
var sportsGrid = uiPage.queryById(sportsFieldParam.defaultValue).grid;
if (newValue[radioGroup.name] == 'sportss') {
this.setDisabled(false);
this.removeCls("sports-item-disabled");
if (sportsGrid.holdValue) {
var footballPanel = sportsGrid.up('panel');
footballPanel.setValue(sportsGrid.holdValue);
}
} else {
this.setDisabled(true);
this.addCls("sports-item-disabled");
**if (sportsGrid.store.getCount() > 0) {
var footballPanel = sportsGrid.up('panel');
footballPanel.holdValue = footballPanel.getValue();
footballPanel.setValue();
sportsGrid.addCls("sports-item-disabled");
}**
}
},
Working code without modification
sportsContainerHandler: function(radioGroup, newValue, oldValue, options) {
var sportsCustomParams = options.sportsCustomParams;
var uiPage = this.up('football-ux-sports-ui-page');
var SportsDefinition = metamodelsHelper.getSportsDefinition(
uiPage, sportsCustomParams.SportsHandlerDefinitionId);
var sportsFieldParam = SportsDefinition.params['sportsMultiFieldName'];
var sportsGrid = uiPage.queryById(sportsFieldParam.defaultValue).grid;
if (newValue[radioGroup.name] == 'sportss') {
this.setDisabled(false);
this.removeCls("sports-item-disabled");
if (sportsGrid.holdValue) {
var footballPanel = sportsGrid.up('panel');
footballPanel.setValue(sportsGrid.holdValue);
}
} else {
this.setDisabled(true);
this.addCls("sports-item-disabled");
if (sportsGrid.store.getCount() > 0) {
sportsGrid.holdValue = sportsGrid.store.data.items;
sportsGrid.store.loadData([]);
sportsGrid.addCls("sports-item-disabled");
}
}
},
getValue() is not a method of ExtJS Panel class.
The change in your code, from sportsGrid (Ext.grid.Panel) to footbalPanel (Ext.panel.Panel) won't work, because they are from different classes and therefore have different properties and methods.
If you want this code to work, you'll need to implement getValue() and setValue(). For example, something like:
On FootballPanel class:
getValue: function () {
return this.down('grid').store.data.items;
},
setValue: function (newValue) {
if (!newValue)
newValue = new Array();
this.down('grid').store.loadData(newValue);
},
And use your modified code:
sportsContainerHandler: function(radioGroup, newValue, oldValue, options) {
var sportsCustomParams = options.sportsCustomParams;
var uiPage = this.up('football-ux-sports-ui-page');
var SportsDefinition = metamodelsHelper.getSportsDefinition(
uiPage, sportsCustomParams.SportsHandlerDefinitionId);
var sportsFieldParam = SportsDefinition.params['sportsMultiFieldName'];
var sportsGrid = uiPage.queryById(sportsFieldParam.defaultValue).grid;
if (newValue[radioGroup.name] == 'sportss') {
this.setDisabled(false);
this.removeCls("sports-item-disabled");
if (sportsGrid.holdValue) {
var footballPanel = sportsGrid.up('panel');
footballPanel.setValue(sportsGrid.holdValue);
}
} else {
this.setDisabled(true);
this.addCls("sports-item-disabled");
if (sportsGrid.store.getCount() > 0) {
var footballPanel = sportsGrid.up('panel');
footballPanel.holdValue = footballPanel.getValue();
footballPanel.setValue([]);
sportsGrid.addCls("sports-item-disabled");
}
}
},

AngularJS Masonry for Dynamically changing heights

I have divs that expand and contract when clicked on. The Masonry library has worked great for initializing the page. The problem I am experiencing is that with the absolute positioning in place from Masonry and the directive below, when divs expand they overlap with the divs below. I need to have the divs below the expanding div move down to deal with the expansion.
My sources are:
http://masonry.desandro.com/
and
https://github.com/passy/angular-masonry/blob/master/src/angular-masonry.js
/*!
* angular-masonry <%= pkg.version %>
* Pascal Hartig, weluse GmbH, http://weluse.de/
* License: MIT
*/
(function () {
'use strict';
angular.module('wu.masonry', [])
.controller('MasonryCtrl', function controller($scope, $element, $timeout) {
var bricks = {};
var schedule = [];
var destroyed = false;
var self = this;
var timeout = null;
this.preserveOrder = false;
this.loadImages = true;
this.scheduleMasonryOnce = function scheduleMasonryOnce() {
var args = arguments;
var found = schedule.filter(function filterFn(item) {
return item[0] === args[0];
}).length > 0;
if (!found) {
this.scheduleMasonry.apply(null, arguments);
}
};
// Make sure it's only executed once within a reasonable time-frame in
// case multiple elements are removed or added at once.
this.scheduleMasonry = function scheduleMasonry() {
if (timeout) {
$timeout.cancel(timeout);
}
schedule.push([].slice.call(arguments));
timeout = $timeout(function runMasonry() {
if (destroyed) {
return;
}
schedule.forEach(function scheduleForEach(args) {
$element.masonry.apply($element, args);
});
schedule = [];
}, 30);
};
function defaultLoaded($element) {
$element.addClass('loaded');
}
this.appendBrick = function appendBrick(element, id) {
if (destroyed) {
return;
}
function _append() {
if (Object.keys(bricks).length === 0) {
$element.masonry('resize');
}
if (bricks[id] === undefined) {
// Keep track of added elements.
bricks[id] = true;
defaultLoaded(element);
$element.masonry('appended', element, true);
}
}
function _layout() {
// I wanted to make this dynamic but ran into huuuge memory leaks
// that I couldn't fix. If you know how to dynamically add a
// callback so one could say <masonry loaded="callback($element)">
// please submit a pull request!
self.scheduleMasonryOnce('layout');
}
if (!self.loadImages){
_append();
_layout();
} else if (self.preserveOrder) {
_append();
element.imagesLoaded(_layout);
} else {
element.imagesLoaded(function imagesLoaded() {
_append();
_layout();
});
}
};
this.removeBrick = function removeBrick(id, element) {
if (destroyed) {
return;
}
delete bricks[id];
$element.masonry('remove', element);
this.scheduleMasonryOnce('layout');
};
this.destroy = function destroy() {
destroyed = true;
if ($element.data('masonry')) {
// Gently uninitialize if still present
$element.masonry('destroy');
}
$scope.$emit('masonry.destroyed');
bricks = [];
};
this.reload = function reload() {
$element.masonry();
$scope.$emit('masonry.reloaded');
};
}).directive('masonry', function masonryDirective() {
return {
restrict: 'AE',
controller: 'MasonryCtrl',
link: {
pre: function preLink(scope, element, attrs, ctrl) {
var attrOptions = scope.$eval(attrs.masonry || attrs.masonryOptions);
var options = angular.extend({
itemSelector: attrs.itemSelector || '.masonry-brick',
columnWidth: parseInt(attrs.columnWidth, 10) || attrs.columnWidth
}, attrOptions || {});
element.masonry(options);
var loadImages = scope.$eval(attrs.loadImages);
ctrl.loadImages = loadImages !== false;
var preserveOrder = scope.$eval(attrs.preserveOrder);
ctrl.preserveOrder = (preserveOrder !== false && attrs.preserveOrder !== undefined);
scope.$emit('masonry.created', element);
scope.$on('$destroy', ctrl.destroy);
}
}
};
}).directive('masonryBrick', function masonryBrickDirective() {
return {
restrict: 'AC',
require: '^masonry',
scope: true,
link: {
pre: function preLink(scope, element, attrs, ctrl) {
var id = scope.$id, index;
ctrl.appendBrick(element, id);
element.on('$destroy', function () {
ctrl.removeBrick(id, element);
});
scope.$on('masonry.reload', function () {
ctrl.scheduleMasonryOnce('reloadItems');
ctrl.scheduleMasonryOnce('layout');
});
scope.$watch('$index', function () {
if (index !== undefined && index !== scope.$index) {
ctrl.scheduleMasonryOnce('reloadItems');
ctrl.scheduleMasonryOnce('layout');
}
index = scope.$index;
});
}
}
};
});
}());
Like with many non-Angular libraries, it appears the answer lies in wrapping the library in an Angular directive.
I haven't tried it out but it appears that is what this person did
You can use angular's $emit, $broadcast, and $on functionality.
Inside your masonry directive link function:
scope.$on('$resizeMasonry', ctrl.scheduleMasonryOnce('layout'));
Inside your masonryBrick directive link function or any other child element:
scope.$emit('$resizeMasonry');
Use $emit to send an event up the scope tree and $broadcast to send an event down the scope tree.

Creating a kendo-grid with reusable options using AngularJS

How to create a kendo-grid with reusable options using AngularJS?
Besides the default settings, the grid must include a checkbox column dynamically with the option to select all rows . Methods to treat the selections should be part of the directive and, somehow, I should be able to access the rows selected in controller.
Another important behavior is to keep a reference to the grid :
// In the controller : $scope.grid
<div kendo-grid="grid" k-options="gridOptions"></div>
Below an initial path that I imagined, but it is not 100% working because AngularJS not compile information from checkbox column, so do not call the methods of the controller directive. At the same time I'm not sure where force $compile in this code.
myApp.directive('myApp', ['$compile', function ($compile) {
var directive = {
restrict: 'A',
replace: true,
template: '<div></div>',
scope: {
gridConfiguration: '='
},
controller: function ($scope) {
$scope.gridIds = [];
$scope.gridIdsSelected = [];
var updateSelected = function (action, id) {
if (action === 'add' && $scope.gridIdsSelected.indexOf(id) === -1) {
$scope.gridIdsSelected.push(id);
}
if (action === 'remove' && $scope.gridIdsSelected.indexOf(id) !== -1) {
$scope.gridIdsSelected.splice($scope.gridIdsSelected.indexOf(id), 1);
}
};
$scope.updateSelection = function ($event, id) {
var checkbox = $event.target;
var action = (checkbox.checked ? 'add' : 'remove');
updateSelected(action, id);
};
$scope.isSelected = function (id) {
return $scope.gridIdsSelected.indexOf(id) >= 0;
};
$scope.selectAll = function ($event) {
var checkbox = $event.target;
var action = (checkbox.checked ? 'add' : 'remove');
for (var i = 0; i < $scope.gridIds.length; i++) {
var id = $scope.gridIds[i];
updateSelected(action, id);
}
};
},
link: function ($scope, $element, $attrs) {
var baseColumns = [
{
headerTemplate: '<input type="checkbox" id="selectAll" ng-click="selectAll($event)" ng-checked="isSelectedAll()">',
template: '<input type="checkbox" name="selected" ng-checked="isSelected(#=Id#)" ng-click="updateSelection($event, #=Id#)">',
width: 28
}
];
for (var i = 0; i < $scope.gridConfiguration.columns.length; i++) {
var column = $scope.gridConfiguration.columns[i];
baseColumns.push(column);
}
var gridOptions = {...};
var grid = $element.kendoGrid(gridOptions).data("kendoGrid");;
$scope.$parent[$attrs[directive.name]] = grid;
}
};
return directive;
}]);
I've put an example directive here: http://embed.plnkr.co/fQhNUGHJ3iAYiWTGI9mn/preview
It activates on the my-grid attribute and inserts a checkbox column. The checkbox is bound to the selected property of each item (note that it's an Angular template and it uses dataItem to refer to the current item). To figure out the selected items you can do something like:
var selection = $scope.grid.dataSource.data().filter(function(item){
return item.selected;
});
The checkbox that is added in the header will toggle the selection.
HTH.
#rGiosa is right
I've tried to do that :
var options = angular.extend({}, $scope.$eval($attrs.kOptions));
options['resizable']= true;
Seems to add the attribute in options object but the grid is still not resizable why?
http://plnkr.co/edit/Lc9vGKPfD8EkDem1IP9V?p=preview
EDIT:
Apparently,Options of the Grid cannot be changed dynamically. You need to re-create the whole Grid with different options in order to disable/enable them dynamically?!
Cheers

Cannot select a dynamically added list item until it is clicked

I have written a small JQuery plugin that creates a dropdown box based on bootstrap. I have written it to where a data attribute supplies a url that produces the list items. After the ajax call, Jquery loops through the list items and inserts them into the dropdown menu. Here is what I do not understand, the plugin takes a div with the class of .combobox and appends the required html to make the combobox. It uses two functions, _create() and _listItems(). _create() actually adds the html and calls on _listItems() to make the ajax call and it returns the list items to be appended. Looks like this:
;(function ( $, window, document, undefined ) {
var Combobox = function(element,options) {
this.$element = $(element);
this.$options = $.extend({}, $.fn.combobox.defaults, options);
this.$html = {
input: $('<input type="text" placeholder="[SELECT]" />').addClass('form-control'),
button: $('<div id="test"/>').addClass('input-group-btn')
.append($('<button />')
.addClass('btn btn-default input-sm')
.append('<span class="caret"></span>'))
}
this.$list_type = this.$element.attr('data-type');
this.$url = this.$element.attr('data-url');
this.$defaultValue = this.$element.attr('data-default');
this._create();
this.$input = this.$element.find('input');
this.$button = this.$element.find('button');
this.$list = this.$element.find('ul')
this.$button.on('click',$.proxy(this._toggleList,this));
this.$element.on('click','li',$.proxy(this._itemClicked,this));
this.$element.on('mouseleave',$.proxy(this._toggleList,this));
if(this.$defaultValue) {
this.selectByValue(this.$defaultValue);
}
}
Combobox.prototype = {
constructor: Combobox,
_create: function() {
this.$element.addClass('input-group input-group-sm')
.append(this.$html.input)
.append(this._listItems())
.append(this.$html.button);
},
_itemClicked: function(e){
this.$selectedItem = $(e.target).parent();
this.$input.val(this.$selectedItem.text());
console.log(this.$element.find('[data-value="W"]'))
this._toggleList(e);
e.preventDefault();
},
_listItems: function() {
var list = $('<ul />').addClass('dropdown-menu');
$.ajax({
url: this.$url,
type: 'POST',
data: {opt: this.$list_type},
success:function(data){
$.each(data,function(key,text){
list.append($('<li class="listObjItem" data-value="'+text.id+'">'+text.value+'</li>'));
})
}
})
return list
},
selectedItem: function() {
var item = this.$selectedItem;
var data = {};
if (item) {
var txt = this.$selectedItem.text();
data = $.extend({ text: txt }, this.$selectedItem.data());
}
else {
data = { text: this.$input.val()};
}
return data;
},
selectByValue: function(value) {
var selector = '[data-value="'+value+'"]';
this.selectBySelector(selector);
},
selectBySelector: function (selector) {
var $item = this.$element.find(selector);
if (typeof $item[0] !== 'undefined') {
this.$selectedItem = $item;
this.$input.val(this.$selectedItem.text());
}
else {
this.$selectedItem = null;
}
},
enable: function () {
this.$input.removeAttr('disabled');
this.$button.children().removeClass('disabled');
this.$button.on('click',$.proxy(this._toggleList,this));
},
disable: function () {
this.$input.attr('disabled', true);
this.$button.children().addClass('disabled');
this.$button.off('click',$.proxy(this._toggleList,this));
},
_toggleList: function(e) {
if(e.type == 'mouseleave') {
if(this.$list.is(':hidden')) {
return false;
} else {
this.$list.hide();
}
} else {
this.$list.toggle();
e.preventDefault();
}
}
}
$.fn.combobox = function (option) {
return this.each(function () {
if (!$.data(this, 'combobox')) {
$.data(this, 'combobox',
new Combobox( this, option ));
}
});
};
$.fn.combobox.defaults = {};
$.fn.combobox.Constructor = Combobox;
})( jQuery, window, document );
The problem is that after the items are appended to the DOM, everything is selectable accept the list items. I currently have an .on() statement that binds the click event with the list item. To test this out I have used console.log(this.$element.find('[data-value="W"]') and it does not return an element, however if I place that same console log in the click callback of the list item it will return the element and it is selectable. Am I doing something wrong?
EDIT
I have pasted the entire plugin to save on confusion.

how to validate data on drop event of itemselecter in extjs?

we not like to add three word again in selected item.
we like to validate on drag and drop event and need to show message that u already added this record.
i try with below code but not able to fine relevant event for validate
listeners: {
added:function(obj,event){
console.log("added");
},change:function(obj,event){
console.log("change");
},removed:function(obj,event){
console.log("removed");
}, blur:function(obj,event){
console.log("blur");
}, click: function( obj) {
console.log('click');
}, select: function( obj) {
console.log('select');
}
}
please see attached image bellow.
I am Using Extjs 3.4
The change event fires when an item is selected or deselected... But at this stage, you won't be able to prevent it anymore. So, apparently, your best move is to override the onAddBtnClick method:
{
xtype: 'itemselector'
// ... config
,onAddBtnClick: function() {
var me = this,
selected = me.getSelections(me.fromField.boundList),
i, l, record;
var toStore = this.toField.boundList.getStore(),
idField = 'value', // or 'id', or whatever you want
selectedIds = Ext.pluck(Ext.pluck(toStore.getRange(), 'data'), idField),
accepted = [], rejected = [];
for (i=0, l=selected.length; i<l; i++) {
record = selected[i];
if (selectedIds.indexOf(record.get(idField)) === -1) {
accepted.push(record);
} else {
rejected.push(record);
}
}
if (rejected.length) {
// warning msg
}
me.moveRec(true, accepted);
me.toField.boundList.getSelectionModel().select(accepted);
}
}

Categories

Resources