I am using angular ui-grid to show list of rows from database table(Users).
I am doing a back end call and NodeJS fetches the data from DB and returns.
This data is being shown in angular ui-grid.
I want to enable or disable few html elements ., view/edit/delete based on the accessibility of the current user.
If the current user is ADMIN then all the links are enabled. If he is BASIC user then VIEW is enabled and EDIT and DELETE are disabled.
Project accessibility is also returned from the server . I just need to check this flag and disable/enable the links.
Please let me know , how to do this?
id name actions
1 AAA view edit delete
2 BBB view edit delete
3 CCC view edit delete
4 DDD view edit delete
<div class="box">
<div class="box-content box-table">
<div ui-grid="gridUsers" ui-grid-pagination>
</div>
</div>
</div>
$scope.gridUsers = {
paginationPageSizes: [15, 30, 45],
paginationPageSize: 15,
enableColumnMenus: false,
data: $scope.users,
filterOptions: $scope.filterOptions,
columnDefs: [{ field: 'id', displayName: 'Id', width: '20%'},
{ field: 'name', displayName: 'Name', width: '25%', enableFiltering: true},
{ name: 'Actions', displayName: 'Actions', width: '55%', cellTemplate:
'<div class="grid-action-cell action-btns">'+
'<span class="btn-small"><span style="color:#214c77;">view</span> </a>' +
'<a ng-click="grid.appScope.edit(row.entity.id)" class="btn-small btn-link"><span style="color:#80bb41;">edit</span> </a>' +
'<a ng-click="grid.appScope.delete(row.entity.id)" class="btn-small btn-link"> <span style="color:#e15829;">delete</span> </a>'
'</div>'}
]
};
Service.GetAllUsers(function (response) {
if (response.length != 0) {
$scope.users = response;
$scope.gridUsers.data = $scope.users;
}
});
I had the same problema.
In order to resolve it, I call a function after retrieving columns :
function updateColumnsDefs() {
columnsDefs
.forEach(function(column) {
switch (column.field) {
case 'status' :
columnVal = '<span ng-if="c.' + column.filterBy + '">{{c.' + column.filterBy + '}}</span>';
column.cellTemplate = '<div class="ui-grid-cell-contents">' + columnVal + '</span></div>';
break;
default :
break;
}
}
Look how I made a dynamic cellTemplate using ng-if.
After that, I apply the updated columnsDefs to gridOptions:
updateColumnsDefs();
vm.gridOptions = {
...
columnDefs : columnsDefs,
...
};
You should pay attention if use lazy loading or filter. In that case remember to recall updateColumnsDefs every time your data model changes.
Related
Having issues with passing in Object Property in UI Grid.
I'm attempting to pass a object property to a function by using the cellTemplete option in UI-Grid but I'm getting an undefined error.
I was able to pass the index to the function, just not the actual property of the row data.
How can I get the object property through to the deleteMember function?
vm.TeamMembersGridOptions = {
enableGridMenu: false,
enableSorting: true,
enableHorizontalScrollbar: 0, /*always hide horizontal scroll bar*/
enableVerticalScrollbar: 1, /*always show vertical scroll bar*/
rowHeight: 40,
minRowsToShow: 5,
columnDefs: [
{
enableHiding: false,
enableColumnMenu: false,
width: '10%',
field: 'edit',
cellClass: 'ui-grid-cell-contents',
cellTemplate:
'<a class="ui-action-type-mouse" ng-click="grid.appScope.vm.deleteMember(rowRenderIndex, row.entity.member)" title="Delete"><img src="resources/images/fa_remove.png"></img></a>'
},
data: vm.teamMembers
]
function deleteMember(idx, member) {
console.log(member);
var params = {
member: vm.teamMembers[idx]
};
var user = params.member.userProfile;
var current = params.member.current;
var messageText;
var headerText;
if (!current) {
messageText = "Are you sure you want to delete this user: " + user.firstName + " " + user.lastName + "(" + user.email + ")?";
headerText = "Delete Team Member?";
}
else {
messageText = "Are you sure you want to delete yourself from this team? You will no longer be able to access this project if you continue.";
headerText = "Delete Yourself?";
}
}
<div ng-if="!vm.loadingTeam" ui-grid="vm.TeamMembersGridOptions" ui-grid-auto-resize></div>
You're passing in vm.teamMembers to the grid. So each item in the vm.teamMembers array is row.entity in the cellTemplate.
Try just passing in row.entity instead of row.entity.members.
I have defined a conditional cell template for one of the column. Its displaying the data correctly but I am not able to search for the text in the cell template.
Here is my plunkr:
https://plnkr.co/edit/TDX5jtPord1hkzCVaw3L?p=preview
var template1 = '<div class="">' +
'<div class="" ng-if="COL_FIELD > 30">Greater </div> ' +
'<div class="" ng-if="COL_FIELD < 30"> Lesser </div> ' +
'</div>';
In the template I have put the condition that.. if COL_FIELD > 30 then then write Greater.. or else write Lesser. And now I should be able to search for the Greater or Lesser in Number column.
A solution could be to add a property on your data like :
$http.get('data.json').success(function(data) {
data.map(function(item) {
item.greaterLesser = item.amount > 30 ? 'Greater' : 'Lesser';
});
$scope.gridOptions.data = data;
});
and then instead of using the amount with a template, just bind on this property.
$scope.gridOptions = {
enableFiltering: true,
columnDefs: [{
field: 'name',
width: 70
}, {
field: 'greaterLesser',
name: 'Number',
width: 90,
}, {
field: 'amount',
name: 'Currency',
cellFilter: 'currencyFilter:this',
}]
};
Here is the updated plunker
Edit
If you want to use the template, you could implement the search function yourself. You can add the filter option to your field and implement the condition function. Something like:
filter: {
condition: function(searchTerm, cellValue) {
var value = cellValue > 30 ? 'greater' : 'lesser';
var result = value.search(searchTerm.toLowerCase());
return result > -1;
}
}
Here I used the search function but you could use match or some other function.
Here is a demo plunker
I used the below code to make search work
field: 'EmpId',
displayName: 'Employee Type',
cellTemplate: '<div style="cursor:pointer" class="ui-grid-cell-contents">{{grid.appScope.GetEmployeeType(row)}}</div>',
filter: {
condition: function (searchTerm, cellValue,row,column) {
var value = $scope.GetEmployeeType(row);//same function that you use to display value in cell Template
return (value.toLocaleLowerCase().indexOf(searchTerm.toLocaleLowerCase())>-1);
}
}
Not sure if I am doing this correctly. But I bind a kendo grid with array data of objects, where each object has a Boolean property (i.e. either true or false).
For the user I display it as a checkbox with the checked state showing the true state and vice versa. If you check something in the page 1; go to the page 3; check a few rows; and go back to page 1, the state of the checkbox defaults to how it was at the time it was loaded.
Here is the code snippet I've used.
$(function () {
var ds = new kendo.data.DataSource({
transport: {
read: {
url: '/echo/json/',
type: 'POST',
data: {
json: JSON.stringify(students)
}
}
},
pageSize: 5
});
var kgrid = $("#grid").kendoGrid({
columns: [
{
field: 'name',
title: 'Name'
}, {
field: 'age',
title: 'Age'
}, {
field: 'place',
title: 'Place'
}, {
field: 'hasCar',
title: 'Owns Car',
template: function (data) {
return '<input type="checkbox" ' + (data.hasCar ? 'checked' : '') + '/>';
}
}],
height: 240,
pageable: true
});
$('#btnload').click(function () {
var grid = kgrid.data('kendoGrid');
grid.setDataSource(ds);
$('#grid-display').show();
});
$('#btnrefresh').click(function(){
ds.read();
console.log('refresh');
});
$('#btnlist').click(function(){
var res = $('#result');
kgrid.find('tr td:last input:checked').each(function(i, e){
var name = $(e).closest('tr').children().first().text();
res.append('<p>' + name + '</p>');
console.log('execute');
});
});
});
How to persist the state on pagination?
Ps: fiddle to work with: http://jsfiddle.net/deostroll/2SGyV/3/
That is because the changes you are doing stays there in the grid only - not in kendoDataSource. Here MVVM is not done. So you have to change appropriate value in dataSource each time you click the checkbox.
When you select another page in kendoGrid, it fetches data from dataSource, and then display it the grid. Unless and untill that dataSource is changed, you can't see the changes done in the grid.
PS: Had there is any field for Id in dataSource I would have updated jsfiddle myself
UPDATE
Check this Updated jsfiddle
I have updated template for checkbox
template: function (data) {
return '<input type="checkbox" ' + (data.hasCar ? 'checked' : '') + ' data-name="'+ data.name + '"' +'/>';
}
Now checkbox will have name of the data. You can change it with id. On change event, update your dataSource accordingly on each checkbox click.
$('#grid-display input').live('change', function(){
alert($(this).attr('data-name'));
//update the data source here based on the data-name attribute u're getting
});
Use databound event in the kendo Grid.
Not sure below code work will work for you or not. But similar code worked for me
function OnDataBoundEventMethod(e) {
var view = this.dataSource.view();
for (var i = 0; i < view.length; i++) {
if ([view[i].hasCar]) {
this.tbody.find("tr[data-uid='" + view[i].uid + "']")
.addClass("k-state-selected")
.find(".row-check-box")
.attr("checked", "checked");
}
}
}
I think that what I'm trying to achieve is having a tree-like inside the ng-grid. I didn't found such an implementation but I'm wondering if I can use the grouping mechanism
I need to have the group header be editable in the same manner as the rows below it (see image above), with exactly the same editable cells, acting as a master row. When updating one cell from the header group should update all the cells beneath that group.
From ng-grid docs http://angular-ui.github.io/ng-grid/ :
default value for aggregateTemplate:
<div ng-click="row.toggleExpand()" ng-style="{'left': row.offsetleft}" class="ngAggregate">
<span class="ngAggregateText">{{row.label CUSTOM_FILTERS}} ({{row.totalChildren()}} {{AggItemsLabel}})</span>
<div class="{{row.aggClass()}}"></div>
</div>
Is it possible to use this option in order to render the aggregate row as I described?
The below answer/comment is related to tree like structure and not related to making aggregate row editable...
If you are looking for tree-like structure in ng-grid, then you could achieve that with the combination of ng-if, ng-click and API(s) that updates the ng-grid data option on click of a particular row. Here is a sample plnkr.
On click of a parent row, a toggle function is called to add/remove child rows in to the ng-grid data. (Refer to my plunker code for complete details)
$scope.toggleDisplay = function(iType) {
$scope.displayItemDetails[iType] = $scope.displayItemDetails[iType] ? 0 : 1;
$scope.selItems = $scope.updateTable();
};
$scope.updateTable = function() {
var selItems = [];
for (var i in $scope.allItems) {
var iType = $scope.allItems[i]["Type"];
if (angular.isUndefined($scope.displayItemDetails[iType])) {
$scope.displayItemDetails[iType] = 0;
}
if (1 == $scope.displayItemDetails[iType]) {
$scope.allItems[i]["Summary"] = '-';
} else {
$scope.allItems[i]["Summary"] = '+';
}
selItems.push($scope.allItems[i]);
if ($scope.displayItemDetails[iType]) {
for (var j in $scope.allItems[i]["Details"]) {
$scope.allItems[i]["Details"][j]["Summary"] = "";
selItems.push($scope.allItems[i]["Details"][j]);
}
}
}
return selItems;
};
$scope.gridOptions = {
data: 'selItems',
columnDefs: [{
field: 'Summary',
displayName: '',
cellTemplate: summaryCellTemplate,
width: 30
}, {
field: 'Name',
displayName: 'Name',
}, {
field: 'Type',
displayName: 'Type',
}, {
field: 'Cost',
displayName: 'Cost',
}, {
field: 'Quantity',
displayName: 'Quantity',
}],
enableCellSelection: false,
enableColumnResize: true
};
I'm building simple application using Ext Scheduler (http://www.bryntum.com/products/scheduler/).
I'm trying to add custom renderer for treecolumn that will have icon of person in tree.
I've created rendered for that column:
renderer: function (v, m, r) {
if (r.get('leaf')) {
return '<div style="float: left">' +
r.get('Name') +
'</div>'+
'<img data-qtip="<img src=iconUrl.png width=60px height=60px/>" '+
'src="iconUrl.png" '+
'width="20px" height="20px" style="float: right; margin-right: 5px"/>';
}
return v;
}
This looks OK, but for long names I get unwanted look:
Looking thrue code I found some unwanted markup:
What I need is almost identical renderer as in standard treecolumn, but I need to add extra icon on right of that column - employee image thumbnail.
I was searching over docs and I found out that there is private property called cellTpl (http://docs.sencha.com/extjs/4.2.1/source/Column2.html#Ext-tree-Column)
but I don't know how should I change this and should I change this at all because it is marked as private on top.
How should I change that renderer to get effect as on last image?
Here is some jsfiddle to play: http://jsfiddle.net/Misiu/gwFdr/
I ended extending Ext.tree.Column to instead of normal leaf icons it shows thumbnails.
Here's my code:
Ext.define('Grafik.component.tree.Column2', {
extend: 'Ext.tree.Column',
alias: 'widget.treecolumn2',
thumbnailsServiceUrl:'',
cellTpl: [
'<tpl for="lines">',
'<img src="{parent.blankUrl}" class="{parent.childCls} {parent.elbowCls}-img ',
'{parent.elbowCls}-<tpl if=".">line<tpl else>empty</tpl>"/>',
'</tpl>',
'<img src="{blankUrl}" class="{childCls} {elbowCls}-img {elbowCls}',
'<tpl if="isLast">-end</tpl><tpl if="expandable">-plus {expanderCls}</tpl>"/>',
'<tpl if="checked !== null">',
'<input type="button" role="checkbox" <tpl if="checked">aria-checked="true" </tpl>',
'class="{childCls} {checkboxCls}<tpl if="checked"> {checkboxCls}-checked</tpl>"/>',
'</tpl>',
'<img src="{loginUrl}" class="{childCls} {baseIconCls} ',
'{baseIconCls}-<tpl if="leaf">leaf<tpl else>parent</tpl> {iconCls}"',
'<tpl if="icon">style="background-image:url({icon})"</tpl>/>',
'<tpl if="href">',
'{value}',
'<tpl else>',
'<span class="{textCls} {childCls}">{value}</span>',
'</tpl>'
],
treeRenderer: function (value, metaData, record, rowIdx, colIdx, store, view) {
var me = this,
cls = record.get('cls'),
renderer = me.origRenderer,
data = record.data,
parent = record.parentNode,
rootVisible = view.rootVisible,
lines = [],
parentData;
if (cls) {
metaData.tdCls += ' ' + cls;
}
while (parent && (rootVisible || parent.data.depth > 0)) {
parentData = parent.data;
lines[rootVisible ? parentData.depth : parentData.depth - 1] =
parentData.isLast ? 0 : 1;
parent = parent.parentNode;
}
return me.getTpl('cellTpl').apply({
record: record,
baseIconCls: me.iconCls,
iconCls: data.iconCls,
icon: data.icon,
checkboxCls: me.checkboxCls,
checked: data.checked,
elbowCls: me.elbowCls,
expanderCls: me.expanderCls,
textCls: me.textCls,
leaf: data.leaf,
expandable: record.isExpandable(),
isLast: data.isLast,
blankUrl: Ext.BLANK_IMAGE_URL,
loginUrl: data.leaf ? me.thumbnailsServiceUrl + record.get('Login') : Ext.BLANK_IMAGE_URL,
href: data.href,
hrefTarget: data.hrefTarget,
lines: lines,
metaData: metaData,
// subclasses or overrides can implement a getChildCls() method, which can
// return an extra class to add to all of the cell's child elements (icon,
// expander, elbow, checkbox). This is used by the rtl override to add the
// "x-rtl" class to these elements.
childCls: me.getChildCls ? me.getChildCls() + ' ' : '',
value: renderer ? renderer.apply(me.origScope, arguments) : value
});
}
});
And usage looks like this:
{
text: 'Users',
dataIndex: 'Name',
hideable: false,
width: 260,
xtype: 'treecolumn2',
sortable: true,
resizable: false,
thumbnailsServiceUrl: window.appUrl + 'api/Thumbnails/'
}
Where api/Thumbnails is service that takes login and returns image.
Simple solution, hope it helps someone :)