I can't figure out what is going on here. I'm trying to make a custom directive for grids and will use element attributes to customize a given instance. In doing so i've made two files
grid-controller.js
app.controller('gridController', ['$scope', function ($scope ) {
//Initilization code
$scope.gridOptions = {
//Setup options
};
$scope.detailOptions = function (e) {
console.log('winning');
return {
dataSource: {
transport: {
read: {
url: "/detail" + e.OrderNumber + ".json",
dataType: 'json'
}
},
error: function (e) {
console.log(e);
},
pageSize: true,
serverPaging: false,
serverFiltering: false,
serverSorting: false,
},
columns: [
{
field: "ItemCode",
label: "lblItemCode",
title: ""
}, {
field: "ItemDesc",
label: "lblItemDesc",
title: ""
}, {
field: "QuantityOrdered",
label: "lblQuantityOrdered",
title: ""
}
],
scrollable: false,
sortable: true
};
}
}]);
grid-directive.js
app.directive('grid', function () {
return {
// Restrict E for element
restrict: 'E',
// Here we setup the template for the code we want to replace our directive
template: "<div> \n\
<div kendo-grid='grid' \n\
k-options='gridOptions'\n\
k-data-source='dataSource'>\n\
</div> \n\
<script type='text/x-kendo-template'\n\
id='details'>\n\
<div kendo-grid >\n\
</div>\n\
</script>\n\
</div>",
replace: true,
scope: {
},
controller: "gridController",
link: function (scope, element, attrs) {
//Gather some attribute data and set it to the gridOptions object
if(scope.$eval(attrs.detailsGrid))
{
scope.gridOptions.detailTemplate = kendo.template($("#details").html());
scope.gridOptions.detailInit = scope.detailOptions;
}
//More customization code
scope.dataSource = new kendo.data.DataSource({
//Setup dataSource options for main grid
});
}
};
});
For sake of brevity i've excluded a lot of the extra code.
My problem is whenever I try to open the details of a row the row opens...closes...and the grid appears to refresh. It almost looks like something is crashing and the main grid is refreshing as a result.
Here is the associated plunkr with the commented portions fleshed out.
So the day after I posted the question angular-kendo released an update that addressed this issue. After updating the library and fixing up my code a bit the details grid works as expected!
Related
I am having trouble making a select required in ExtJS.
I tried with allowBlank: false, even afterredner methods but nothing works, I can submit the form with no value in that select.
The code to generate the field is as follows:
OldType: function () {
return {
xtype: 'xEasyGridCombo',
allowBlank: false,
required: true,
valueField: 'fe_code',
displayField: 'fe_name',
dropPanelConfig: {
width: 480,
height: 200
},
searchFieldList: ['fe_code', 'fe_name'],
gridConfig: {
table: 'type_store',
idColumn: 'fe_id',
hasBottomBar: true,
rowLimit: 40,
tools: [],
conditions: [],
forceColumns: ['fe_code', 'fe_name'],
forceSelectFieldsQuery: ' DISTINCT fe_code, CONCAT(fe_name, \'[\', fe_code, \']\') fe_name ',
xColumns: [],
storeBaseParams: {}
},
listeners: {
afterrender: function () {
let oGrid,
sDivision = this.findParentByType('panel').find('KeyNr1', 'Divsion')[0].getValue();
// making sure the grid is rendered
this.getGridList();
oGrid = this.gridList.findByType('uxgrid')[0];
oGrid.store.baseParams.filter = Ext.util.JSON.encode([{
"field": "fe_tip",
"value": (sDivision === '02') ? 'F-GAZ' : 'F-ELEC'
}]);
//store from server try
oGrid.store.reload();
}
}
};
},
What am I doing wrong ? I can't even make it have a default value.
Thanks in advance and happy Holidays !
In the modern toolkit, the formpanel has a beforesubmit event. you can check if the value is selected or not. If you return a false from this event the form will not be submitted.
in the classic toolkit the event is beforeaction.
I have setup a kendo grid. The gird has a column containing a hyperlink. When I click on the link, I need to call a function and then I need to redirect to a new page. I know it sounds simple. I have a stand alone example which is doing same.
But when I try to use same logic in kendo grid, I am unable to get the desired result. Please help. Here is a link to Kendo Grid with a column containing hyperlink.
$('#one').kendoGrid({
dataSource: dataOne,
columns: [{
field: 'a',
template: "<a onclick=return doWork() href='/home/again/${a}'>${a}</a>"
}, {
command: 'destroy'
}],
editable: {
confirmation: false
}
});
try this soution
Add this function before grid settings:
function showFoo() {
alert('I am foo!');
return true;
}
Add following code at the end (after grid settings).
var el = document.getElementById('foo');
el.onclick = showFoo;
You need prevent the default click by event.preventDefault() and then redirect.
$('#one').kendoGrid({
dataSource: dataOne,
columns: [{
field: 'a',
template: "<a onclick=\"doWork(event, '/home/again/${a}')\" href=''>${a}</a>"
}, {
command: 'destroy'
}],
editable: {
confirmation: false
}
});
function doWork(ev, url){
ev.preventDefault();
alert("redirecting");
//... do your works
window.location.href = url;
}
I am trying to integrate Kendo UI into AngularJS. I've come across an issue which I can't seem to solve. I am fairly new to AngularJS and am trying to follow the guidelines as best as possible.
The issue at hand is that I am unable to obtain a reference to my Kendo UI grid. I've already gone through a couple of similar SO questions, but none seem to resolve the issue. The main difference in my code is that I'm using controllerAs instead of $scope.
Datagrid Controller:
'use strict';
angular
.module('ayotaWebFeApp.datagrid')
.controller('DataGridController', DataGridController);
DataGridController.$inject = ['$scope','$routeParams', 'datagriddataservice'];
function DataGridController($scope, $routeParams, dataservice) {
console.info('DataGridController instantiated.');
var vm = this;
vm.grid = {};
vm.dataTitle = $routeParams.datatype;
activate();
$scope.$on('kendoWidgetCreated', function(event, widget) {
console.log('kendowidgetcreated event: ', event);
console.log('kendowidgetcreated widget: ', widget);
$scope.$apply();
});
function activate() {
console.info('Activate was called.');
return getDataFields().then(function() {
console.info('Loaded data fields for data type :'+$routeParams.datatype);
});
}
function initGridOptions(columnSpec) {
var restURL = '/api/data';
if($routeParams.ups) {
restURL += '/UPS/' + $routeParams.ups;
} else {
restURL += '/adm';
}
restURL += '/' + $routeParams.historical;
restURL += '/' + $routeParams.datatype;
console.info('Data REST url : ', restURL);
console.info('fields: ', columnSpec);
vm.dataGridOptions = {
name: 'grid',
columns : columnSpec['columns'],
dataSource: {
type: 'json',
transport: {
read: 'http://localhost:57713'+restURL
},
schema: {
model: {
fields : columnSpec['fields']
}
}
},
sortable: true,
filterable: true,
scrollable: true,
reorderable: true,
resizable: true,
selectable: 'single row',
columnMenu: true,
pageable: {
refresh: true,
buttonCount: 5
},
dataBound: onDataBound,
height: '100%'
};
console.info('vm.dataGridOptions : ', vm.dataGridOptions);
console.info('scope after datagridoptionsloaded: ', $scope);
console.info('vm.grid: ', vm.grid);
console.info('scope.vm.grid: ', $scope.vm.grid);
}
function getDataFields() {
return dataservice.getDataFields($routeParams.datatype)
.then(function(data) {
initGridOptions(data);
});
}
function onDataBound(ev) {
console.log('DATABOUND !');
//vm.grid = ev.sender;
var gh = $('.datagrid-grid').find('.k-grid-header');
var gc = $('.datagrid-grid').find('.k-grid-content');
//var rightPaddingHeader = gh.css("padding-right");
gh.hide();
gh.css('padding-right', '0px');
gc.css('overflow-y', 'auto');
gh.show();
//resizeGrid();
//unbind event
vm.grid.unbind('dataBound');
}
function resizeGrid() {
console.log('vm.grid.dataSource: ', vm.grid.dataSource);
// trigger layout readjustment
vm.grid.resize();
// force layout readjustment
vm.grid.resize(true);
//set new pagesize according to content height to avoid inner scrollbars
var gridContentWidget = $('.datagrid-grid .k-grid-content');
console.info('gridContentWidget.height(): ', gridContentWidget.height());
var rowHeight = $('.datagrid-grid .k-grid-content tr').first().height();
var newPageSize = Math.floor(gridContentWidget.height() / rowHeight);
console.info('Setting dataSource to new pageSize: ', newPageSize);
if(newPageSize != vm.grid.dataSource.pageSize())
vm.grid.dataSource.pageSize(newPageSize);
console.info('DataSource pageSize after set: ', vm.grid.dataSource.pageSize);
}
}
And the corresponding html
<div class="datagrid-container">
<h2>{{vm.dataTitle}}</h2>
<div class="datagrid-grid">
<div kendo-grid="vm.grid"
k-options="vm.dataGridOptions"
k-ng-delay="vm.dataGridOptions"
datagrid-resize />
</div>
</div>
Only in the onDataBound function can I get a reference through the event sender.
Any suggestions are highly appreciated.
try this:
vm.gridReference = $('div[kendo-grid]').data('kendoGrid');
I haven't yet figured out a decent way of getting a grid reference without throwing JQuery around but this will do for a quick and dirty.
I have a service that will return my some config options for an ng-grid. The getGridOptions function takes the name of the controller it's used on and returns the correct set of options (only one shown here for brevity).
service for ng-grid options:
angular.module('services').service('GridOptionsService',function(){
var documents = {
data: 'myData',
enablePaging: true,
showFooter:true,
totalServerItems: 'totalServerItems',
pagingOptions: {
pageSizes: [50,100,200],
pageSize: 50,
currentPage: 1
},
filterOptions: {
filterText: '',
useExternalFilter: false
},
enableCellEdit: false,
enableColumnReordering: true,
enablePinning: false,
showGroupPanel: false,
groupsCollapsedByDefault: true,
enableColumnResize: true,
showSelectionCheckbox: true,
selectWithCheckboxOnly: true,
columnDefs: [
{field:'docId', displayName:'Document ID', cellTemplate: NgGridDomUtil.toLink('#/documents/{{row.getProperty(col.field)}}')},
{field:'docTags', displayName:'Tags'},
{field:'lastSaveDate', displayName:'Last saved'},
{field:'documentVersion', displayName:'Version', width: 120},
{field:'busDocId', displayName:'Customer Doc ID'},
{field:'markedForDelete', displayName:'Deleted', width: 120, cellTemplate: NgGridDomUtil.toCheckbox('{{row.getProperty(col.field)}}')}]
};
var gridOptionManager = {
documents: documents
}
return {
getGridOptions: function(controllerName){
return gridOptionManager[controllerName];
}
}
})
The NgGridDomUtil class just makes it easier to style things on the grid:
var NgGridDomUtil = (function(){
var toLink = function(href){
var html = '<div class="ngCellText" ng-class="col.colIndex()"><a ng-href= "'+href+'" class="ngCellLink"><span ng-cell-text>{{row.getProperty(col.field)}}</span></a></div>'
return html;
}
var toCheckbox = function(_selected){
var html = '<div class="ngCellText" ng-class="col.colIndex()"><input type="checkbox" ng-change="console.log('+"TEST"+')" ng-model="COL_FIELD" ng-input="COL_FIELD"' + (_selected ? 'selected' : '') + ' /></div>'
return html
}
return {
toLink: toLink,
toCheckbox: toCheckbox
}
})();
My problem is what when I use the GridOptionsService to retrieve the data, the data is still presented to the grid correctly, but the text filtering no longer works and the paging is broken. However, the selectedFilterOption still works.
controller:
angular.module('controllers').controller('Repository', ['$scope', 'DataContext','GridOptionsService','$http', function($scope, DataContext,GridOptionsService,$http) {
$scope.filterOptions = {
filterText: '',
useExternalFilter: false
};
$scope.totalServerItems =0;
$scope.pagingOptions ={
pageSizes: [5,10,100],
pageSize: 5,
currentPage: 1
}
//filter!
$scope.dropdownOptions = [{
name: 'Show all'
},{
name: 'Show active'
},{
name: 'Show trash'
}];
//default choice for filtering is 'show active'
$scope.selectedFilterOption = $scope.dropdownOptions[1];
//three stage bool filter
$scope.customFilter = function(data){
var tempData = [];
angular.forEach(data,function(item){
if($scope.selectedFilterOption.name === 'Show all'){
tempData.push(item);
}
else if($scope.selectedFilterOption.name ==='Show active' && !item.markedForDelete){
tempData.push(item);
}
else if($scope.selectedFilterOption.name ==='Show trash' && item.markedForDelete){
tempData.push(item);
}
});
return tempData;
}
//grabbing data
$scope.getPagedDataAsync = function(pageSize, page, searchText){
var data;
if(searchText){
var ft = searchText.toLowerCase();
DataContext.getDocuments().success(function(largeLoad){
//filter the data when searching
data = $scope.customFilter(largeLoad).filter(function(item){
return JSON.stringify(item).toLowerCase().indexOf(ft) != -1;
})
$scope.setPagingData($scope.customFilter(data),page,pageSize);
})
}
else{
DataContext.getDocuments().success(function(largeLoad){
var testLargeLoad = $scope.customFilter(largeLoad);
//filter the data on initial page load when no search text has been entered
$scope.setPagingData(testLargeLoad,page,pageSize);
})
}
};
//paging
$scope.setPagingData = function(data, page, pageSize){
var pagedData = data.slice((page -1) * pageSize, page * pageSize);
//filter the data for paging
$scope.myData = $scope.customFilter(pagedData);
$scope.myData = pagedData;
$scope.totalServerItems = data.length;
// if(!$scope.$$phase){
// $scope.$apply();
// }
}
//watch for filter option change, set the data property of gridOptions to the newly filtered data
$scope.$watch('selectedFilterOption',function(){
var data = $scope.customFilter($scope.myData);
$scope.myData = data;
$scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage);
$scope.setPagingData($scope.myData,$scope.pagingOptions.currentPage,$scope.pagingOptions.pageSize);
})
$scope.$watch('pagingOptions',function(newVal, oldVal){
$scope.getPagedDataAsync($scope.pagingOptions.pageSize,$scope.pagingOptions.currentPage,$scope.filterOptions.filterText);
$scope.setPagingData($scope.myData,$scope.pagingOptions.currentPage,$scope.pagingOptions.pageSize);
},true)
$scope.message ="This is a message";
$scope.gridOptions = {
data: 'myData',
enablePaging: true,
showFooter:true,
totalServerItems: 'totalServerItems',
pagingOptions: $scope.pagingOptions,
filterOptions: $scope.filterOptions,
enableCellEdit: true,
enableColumnReordering: true,
enablePinning: true,
showGroupPanel: true,
groupsCollapsedByDefault: true,
enableColumnResize: true
}
$scope.gridOptions = GridOptionsService.getGridOptions('documents');
//get the data on page load
$scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage);
}]);
The grid options that I have hard coded into the controller are the same as the ones returned from the service. What I don't understand is why the grid renders, the dropdown filter works, but the paging is broken, only when the options for the grid come from a service? But it works as expected if it's hard coded into the controller.
EDIT: If someone can more eloquently state my problem, feel free to edit the title.
I don't really know how the ngGrid is implemented, but a common pitfall I do know that exist in many directives, is they expect their configurations to be ready as soon as they're initialized. Meaning that instead of watching the configuration object, they assume it exists and use it directly in the link\controller functions which runs as soon as they're created.
If this is indeed the case, a quick workaround to the problem is initializing the directive only when you have the configuration object. Let's say you pass on the configuration object through the variable 'options' on your scope, you'll then write something like:
<!-- If options exists on your scope, it means you fetched it from the server -->
<div ng-if="options">
<div ng-grid ng-grid-options="options"></div>
</div>
Again, I'm not familiar with ngGrid or its usage, this is just an educated guess, take the conclusions and apply them on the correct API.
I haven't tested this, but one possible issue is that you are overwriting an object that is on the $scope. This can break the two way binding. For a quick test try
$scope.grid = {
Options: {
data: 'myData',
enablePaging: true,
showFooter:true,
totalServerItems: 'totalServerItems',
pagingOptions: $scope.pagingOptions,
filterOptions: $scope.filterOptions,
enableCellEdit: true,
enableColumnReordering: true,
enablePinning: true,
showGroupPanel: true,
groupsCollapsedByDefault: true,
enableColumnResize: true
}
}
$scope.grid.Options = GridOptionsService.getGridOptions('documents');
You would need to update the grid options in the directives attribute as well of course.
The problem is that the controller functions for filtering and paging use the options defined on the controller $scope, but the ng-grid UI is not bound to those objects.
The controller methods for filtering and paging use $scope.pagingOptions as the data source. However, the ng-grid UI is bound to $scope.gridOptions.pagingOptions. When you create the $scope.gridOptions explicitly in the controller, $scope.gridOptions.pagingOptions refers to the same object as $scope.gridOptions, so making changes in the UI will change the value used in the controller functions.
However, when $scope.gridOptions is assigned using the service, the service is creating new pagingOptions, so there is no connection between $scope.gridOptions.pagingOptions and $scope.pagingOptions. Making changes in the UI will not change the values used in the controller functions.
The same is true for filterOptions.
One way to resolve the issue is
$scope.gridOptions = GridOptionsService.getGridOptions('documents');
$scope.pagingOptions = $scope.gridOptions.pagingOptions
$scope.filterOptions = $scope.gridOptions.filterOptions
i have a kendoui grid which list claims. one of the columns is lenders which is a foreign key reference to the lenders table. what i want is to be able to display the lender name in the grid instead of its id reference.
ive setup the lenders datasource as follows
var dsLenders = new kendo.data.DataSource({
transport: {
read: {
url: "../data/lenders/",
dataType: "jsonp"
},
parameterMap: function(options, operation) {
if (operation === "read") {
return options;
}
}
}
});
and the grid looks like this
$("#gridClaims").kendoGrid({
dataSource: claimData,
autoSync:true,
batch: true,
pageable: {
refresh: true,
pageSizes: true
},
filterable: true,
sortable: true,
selectable: "true",
editable: {
mode: "popup",
confirmation: "Are you sure you want to delete this record?",
template: $("#claimFormPopup").html()
},
navigable: true, // enables keyboard navigation in the grid
toolbar: ["create"], // adds insert buttons
columns: [
{ field:"id_clm", title:"Ref", width: "80px;" },
{ field:"status_clm", title:"Status", width: "80px;" },
{ field:"idldr_clm", title:"Lender", values: dsLenders },
{ field:"type_clm", title:"Claim Type"},
{ field:"value_clm", title:"Value", width: "80px;", format:"{0:c2}", attributes:{style:"text-align:right;"}},
{ field:"created", title:"Created", width: "80px;", format: "{0:dd/MM/yyyy}"},
{ field:"updated", title:"Updated", width: "80px;", format: "{0:dd/MM/yyyy}"},
{ field:"user", title:"User" , width: "100px;"},
{ command: [
{text: "Details", className: "claim-details"},
"destroy"
],
title: " ",
width: "160px"
}
]
});
however its still displaying the id in the lenders column. Ive tried creating a local datasource and that works fine so i now is something to do with me using a remote datasource.
any help would be great
thanks
Short answer is that you can't. Not directly anyway. See here and here.
You can (as the response in the above linked post mentions) pre-load the data into a var, which can then be used as data for the column definition.
I use something like this:-
function getLookupData(type, callback) {
return $.ajax({
dataType: 'json',
url: '/lookup/' + type,
success: function (data) {
callback(data);
}
});
}
Which I then use like this:-
var countryLookupData;
getLookupData('country', function (data) { countryLookupData = data; });
I use it in a JQuery deferred to ensure that all my lookups are loaded before I bind to the grid:-
$.when(
getLookupData('country', function (data) { countryLookupData = data; }),
getLookupData('state', function (data) { stateLookupData = data; }),
getLookupData('company', function (data) { companyLookupData = data; })
)
.then(function () {
bindGrid();
}).fail(function () {
alert('Error loading lookup data');
});
You can then use countryLookupData for your values.
You could also use a custom grid editor, however you'll probably find that you still need to load the data into a var (as opposed to using a datasource with a DropDownList) and ensure that the data is loaded before the grid, because you'll most likely need to have a lookup for a column template so that you're newly selected value is displayed in the grid.
I couldn't quite get ForeignKey working in any useful way, so I ended up using custom editors as you have much more control over them.
One more gotcha: make sure you have loaded your lookup data BEFORE you define the column. I was using a column array that was defined in a variable I was then attaching to the grid definition... even if the lookup data is loaded before you use the grid, if it's defined after the column definition it will not work.
Although this post past 2 years, I still share my solution
1) Assume the api url (http://localhost/api/term) will return:
{
"odata.metadata":"http://localhost/api/$metadata#term","value":[
{
"value":2,"text":"2016-2020"
},{
"value":1,"text":"2012-2016"
}
]
}
please note that the attribute name must be "text" and "value"
2) show term name (text) from the foreign table instead of term_id (value).
See the grid column "term_id", the dropdownlist will be created if added "values: data_term"
<script>
$.when($.getJSON("http://localhost/api/term")).then(function () {
bind_grid(arguments[0].value);
});
function bind_grid(data_term) {
$("#grid").kendoGrid({
dataSource: ds_proposer,
filterable: true,
sortable: true,
pageable: true,
selectable: "row",
columns: [
{ field: "user_type", title: "User type" },
{ field: "user_name", title: "User name" },
{ field: "term_id", title: "Term", values: data_term }
],
editable: {
mode: "popup",
}
});
}
</script>
For those stumbling across this now, this functionality is supported:
https://demos.telerik.com/aspnet-mvc/grid/foreignkeycolumnbinding