Using a function in cell template - angularjs

I am using angularjs with ui-grid. One column of the grid contains a timestamp that I would like to render as a properly formatted date.
Up to now, I tried like this but the function is never called.
$scope.formatDate = function(date) {
return '42';
};
$scope.columns = [
{field: 'date', cellTemplate: '<div class="ui-grid-cell-contents">formatDate({{row.entity.date}})</div>'},
{field: 'title'},
{field: 'quantity'},
//[...]
];
Instead, the function call is considered as a string literal. As a result, the column always displays formatDate(*timestamp*).
I only found a non-satisfying way of achieving it by defining a function on each single row when receiving them :
$scope.columns = [
{field: 'getFormattedDate()'},
//[...]
];
$http.post('/api/data/').success(function (data) {
$scope.gridOptions.data = data.elements;
$scope.gridOptions.data.forEach(function(row) {
row.getFormattedDate = function() {
return '42';
}
})
});
Any better suggestion?

If you want to access controller scope level functions using ui-grid you can use grid.appScope, here is a quick example:
{
name: 'date',
cellTemplate: '<div class="ui-grid-cell-contents">{{grid.appScope.parseDate(row.entity.date)}}</div>'
}
Full Example:
angular.module('myApp', ['ui.grid'])
.controller('myCtrl', ['$scope', function ($scope) {
$scope.parseDate = function (p) {
// Just return the value you want to output
return p;
}
$scope.parseName = function (p) {
// Just return the value you want to output
return p;
}
$scope.gridOptions = {
data: [{
name: "Foo",
date: "2015-10-12"
}, {
name: "Bar",
date: "2014-10-12"
}],
columnDefs: [{
name: 'name',
cellTemplate: '<div class="ui-grid-cell-contents">{{grid.appScope.parseName(row.entity.name)}}</div>'
}, {
name: 'date',
cellTemplate: '<div class="ui-grid-cell-contents">{{grid.appScope.parseDate(row.entity.date)}}</div>'
}]
};
}]);
Fiddle Example

To use function output the whole function call, not the arguments , needs to be wrapped in expression braces
<div class="ui-grid-cell-contents">{{ formatDate(row.entity.date) }}</div>

It's worth noting that the cellTemplate will not do anything if you don't include HTML in the format:
will not hit the formatting method "formatRating()":
columnDefs: [
{
name: 'starRating', headerCellClass: 'blue', headerTooltip: 'Star Rating',
cellTemplate: '{{grid.appScope.formatRating(row.entity.starRating)}}'
},
Works with the addition of a <span> in the cellTemplate:
columnDefs: [
{
name: 'starRating', headerCellClass: 'blue', headerTooltip: 'Star Rating',
cellTemplate: '<span>{{grid.appScope.formatRating(row.entity.starRating)}}</span>'
},
my format method:
$scope.formatRating = function (starRating) {
switch (starRating) {
case "ONE": return "1/5"; break;
case "TWO": return "2/5"; break;
case "THREE": return "3/5"; break;
case "FOUR": return "4/5"; break;
case "FIVE": return "5/5"; break;
}
}

I use the controllerAs: 'vm' option on my controller, so I had to do the following to call functions in a cellTemplate.
Define the function on the vm scope like this:
vm.$scope.getBitFlag = (flags, value) => (flags & value) ? "✅" : "⬜️";
Then use it like this:
cellTemplate: "<div>{{ grid.appScope.getBitFlag(row.entity.prefValue, 1) }}</div>"

Related

setting a editableCellCondition in a ui-grid inside an angular 1.5 component

I am trying to set rows editable/not editable based on a flag in the data.
I can get this working outside an angular 1.5 component, but can't seem to access row.entity inside a controller in a component.
function memberDisplayCtrl ($scope, memberFactory,uiGridConstants) {
var ctrl = this;
ctrl.people = memberFactory.getMembers();
ctrl.checkStatus = function(ctrl){
// How do I do something like this:
// if (ctrl.row.entity.Status === 'Y') { return 'true'; } else {return 'false';}
};
ctrl.gridOptions = {
enableSorting: true,
enableCellEdit:false,
cellEditableCondition: ctrl.checkStatus(ctrl),
enableHorizontalScrollbar : 0,
enableVerticalScrollbar : 0,
enableColumnMenus: false,
minRowsToShow: ctrl.people.length,
columnDefs: [
{ displayName:'First Name', name: 'fname', enableCellEdit:true },
{ displayName:'Last Name', name: 'lname', enableCellEdit:true },
{ displayName:'Date of Birth', name: 'DOB', type:'date', enableCellEdit:true, cellFilter: 'date:"yyyy-MM-dd"'},
{ displayName:'Address', name: 'address', enableCellEdit:true},
{ displayName:'Status',name: 'Status', enableCellEdit: true}
],
data : ctrl.people
};
}
I'm pretty sure I have a scope problem but can't seem to figure it out. How do I access row.entity? I have an isolated scope inside my controller (since it is part of a component)
plunker here: https://plnkr.co/edit/Wz7gKs
Thanks
just pass a function to cellEditableCondition instead of executing it:
cellEditableCondition: ctrl.checkStatus
and note, that parameter received by that function is not current controller's this (aliased in your case as ctrl), but ui-grid's scope, so I renamed ctrl to scope:
function memberDisplayCtrl ($scope, memberFactory,uiGridConstants) {
var ctrl = this;
ctrl.people = memberFactory.getMembers();
ctrl.checkStatus = function(scope) {
return scope.row.entity.Status === 'Y'
};
ctrl.gridOptions = {
enableSorting: true,
enableCellEdit:false,
cellEditableCondition: ctrl.checkStatus,
enableHorizontalScrollbar : 0,
enableVerticalScrollbar : 0,
enableColumnMenus: false,
minRowsToShow: ctrl.people.length,
columnDefs: [
{ displayName:'First Name', name: 'fname', enableCellEdit:true },
{ displayName:'Last Name', name: 'lname', enableCellEdit:true },
{ displayName:'Date of Birth', name: 'DOB', type:'date', enableCellEdit:true, cellFilter: 'date:"yyyy-MM-dd"'},
{ displayName:'Address', name: 'address', enableCellEdit:true},
{ displayName:'Status',name: 'Status', enableCellEdit: true}
],
data : ctrl.people
};
}
also, I see your commented code:
if (ctrl.row.entity.Status === 'Y') {
return 'true';
}
else {
return 'false';
}
here you intend to return string variable in both cases which will always evaluated as boolean true, you should return boolean:
if (ctrl.row.entity.Status === 'Y') {
return true;
}
else {
return false;
}
which is equal to much shorter version:
return ctrl.row.entity.Status === 'Y';
plunker: https://plnkr.co/edit/KXbJ40?p=preview

ui.grid data displayed is the same on each row

I am using a cell template for each row in my data grid. If I qualify the json object with an index then the value appears correctly but of course it is the same for each row. If I remove the index, then all rows are displayed with the same value but the value is an array.
---the js file
(function() {
angular.module('xxxSurvey').controller('EditxxxSurveyController', EditxxxSurveyController);
EditxxxSurveyController.$inject = ['$scope', 'UserFacilityListService', 'xxxSurveyService'];
function EditxxxSurveyController($scope, UserFacilityListService, xxxSurveyService) {
$scope.dataLoaded = false;
$scope.currentPage = 1;
$scope.pageSize = 10;
// test ui-grid setup
$scope.dataLoaded = true;
$scope.editWorksheetOptions = {
enableSorting: true,
columnDefs: [
{
name: 'all', field: 'MasterPatientId', width: 40,
enableSorting: false, enableColumnMenu: false, pinnedLeft: true,
//cellTemplate: '<input type="checkbox" id="i{{COL_FIELD}}">'
cellTemplate: '<div class="ui-grid-cell-contents">{{grid.appScope.worksheetInfo.MasterProviderId}}</div>'
},
{name: 'residentName', field: 'residentName', minWidth: 90, pinnedLeft: true,
cellTemplate: '<div class="ui-grid-cell-contents">{{grid.appScope.editWorksheetOptions.data[0].ResidentNameLast}}</div>'
},
{name: 'residentRoom', field: 'residentRoom', width: 90, pinnedLeft: true},
{name: 'status', field: 'status', width: 90},
],
data: []
};
$scope.$on('FacilitySelected', function() {
if (UserFacilityListService.getSelectedFacility()) {
$scope.selectedFacility = UserFacilityListService.getSelectedFacility();
}
var promise = xxxSurveyService.getCurrentWorksheet($scope.selectedFacility.MasterProviderId);
promise.then(
function(payload) {
if (payload !== null) {
$scope.worksheetInfo = payload.worksheetInfo;
$scope.editWorksheetOptions.data = payload.residentData;
}
}
);
});
}
})();
--the json data
[{"AssessmentId":1,"WorksheetId":4,"MasterPatientId":1,"ResidentNameFirst":"xx","ResidentNameMiddle":"^","ResidentNameLast":"zzz","ResidentNameSuffix":"^"},
{"AssessmentId":2,"WorksheetId":2,"MasterPatientId":2,"ResidentNameFirst":null,"ResidentNameMiddle":null,"ResidentNameLast":null,"ResidentNameSuffix":null}]
--the html div id="editWorksheetGrid" ui-grid="editWorksheetOptions" class="grid" ui-grid-pinning>
i had same issue. For me it was rowIdentity problem.
Define following in your controller.
$scope.gridOptions.rowIdentity = function (row) {
return row.ID; //make sure ID is unique.
};
This fixed my problem.
Thanks

I want to read a array value inside factory in angularjs

I am trying to read value in array in factory but I am unable to do so. I am using ng-grid and when I click on one row I get selecteditems list which I pass in another controller where I call a factory service in which I pass that as a parameter but that parameter in the factory stays as array and when I read it using index it shows blank.
My code is as below -
myNgApp.controller('MyGrid', ['$scope', function ($scope) {
$scope.mySelections = [];
$scope.mySelItems = [];
$scope.myData = [{ Reference: 12, Customer: "fff", Title: "sd", Task: "Enter Details", Received: "Today", Due: "01/09/2014" },
{ Reference: 7899, Customer: "eee", Title: "dsd", Task: "Enter Details", Received: "Yesterday", Due: "05/09/2014" }];
$scope.gridOptions = {
data: 'myData',
checkboxHeaderTemplate: '<input class="ngSelectionHeader" type="checkbox" ng-model="allSelected" ng-change="toggleSelectAll(allSelected)"/>',
selectWithCheckboxOnly: true,
showSelectionCheckbox: true,
selectedItems: $scope.mySelections,
multiSelect: true,
columnDefs: [{ field: 'Reference', displayName: 'Reference', width: '*' }, { field: 'Customer', displayName: 'Customer', width: '**' }, { field: 'Title', displayName: 'Title', width: '***' }, { field: 'Task', displayName: 'Task', width: '***' }, { field: 'Received', displayName: 'Received', width: '**' }, { field: 'Due', displayName: 'Due', width: '**' }],
showGroupPanel: true,
enableCellSelection: false,
enableRowSelection: true,
enableCellEditOnFocus: false,
enablePinning: true,
showColumnMenu: true,
showFilter: true,
enableColumnResize: true,
enableColumnReordering: true,
maintainColumnRatios: true,
afterSelectionChange: function () {
angular.forEach($scope.mySelections, function (item) {
if ($scope.mySelItems.length == 0) {
$scope.mySelItems.push(item.Title)
}
else {
$scope.mySelItems[0] = item.Title
}
});
}
};
}]);
myNgApp.factory('myPreviewDataService', function () {
return function (x) {
var arr = [x, "Apple", "Banana", "Orange"];
return arr
};
});
myNgApp.factory('myPreviewTplService', function () {
return function () {
return '<div><div class="ngPreviewItems" ng-repeat="item in items">{{item}}</div></div>';
};
});
myNgApp.directive('showPreview', function ($compile) {
return {
scope: true,
link: function (scope, element, attrs) {
var el;
attrs.$observe('template', function (tpl) {
if (angular.isDefined(tpl)) {
// compile the provided template against the current scope
el = $compile(tpl)(scope);
// stupid way of emptying the element
element.html("");
// add the template content
element.append(el);
}
});
}
};
});
myNgApp.controller('myPreviewController', function ($scope, myPreviewDataService, myPreviewTplService) {
//$scope.showContent = function () {
$scope.items = myPreviewDataService($scope.mySelItems);
$scope.template = myPreviewTplService();
//};
});
here $scope.mySelItems is from ng grid controller that gets updated when we select a checkbox.
What I get is an array but I am unable to read its content, when I display the array as it is it gets displayed like ["test"] but when I try to read it x[0] in myPreviewDataService factory or by $scope.mySelItems[0] in myPreviewController then I get blank. I am not able to figure out why this is happening
I was able to solve it. In myPreviewDataService factory I changed the array elements from string to array
var arr = [x, "Apple", "Banana", "Orange"];
changed to
var arr = [x, ["Apple"], ["Banana"], ["Orange"]];
and in myPreviewTplService factory I changed {{item}} to {{item[0]}}
it worked.
P.S I think we can also use ng switch based on condition in myPreviewTplService factory based on the type of item, I tried to do it but I was not able to do so and worked with my earlier solution.

Angularjs directive not redrawing

I am trying to get a recursive directive working in angular. After quite a bit of time on stackoverflow and digging through the angular documentation I have got most of it working. I'm having a hard time getting the actions working though. The object is getting updated as I would expect, but the directive doesn't seem to be redrawn accordingly.
Here is the directive:
.directive('formgenerator', ['$compile', function ($compile) {
return {
restrict: 'E',
terminal: true,
scope: { val: '=', index: '=' },
replace: true,
link: function (scope, element, attrs) {
var template = '<div data-ng-if="val">';
template += !scope.val.type ? ''
: scope.val.type === 'text' ? '<label>{{val.label}}</label><input type="text" data-ng-model="val.value"></input><button ng-click="deleteMe(index)">delete</button>'
: scope.val.type === 'select' ? '<label>{{val.label}}</label><select data-ng-model="val.value" data-ng-options="v.name for v in val.values track by v.id"></select><button ng-click="deleteMe(index)">delete</button>'
: scope.val.type === 'multiselect' ? '<label>{{val.label}}</label><select data-ng-model="val.value" multiple="multiple" data-ng-options="v.name for v in val.values track by v.id"></select><button ng-click="deleteMe(index)">delete</button>'
: '';
template += '</div>';
if (angular.isArray(scope.val.inputs)) {
template += '<ul class="indent"><li ng-repeat="input in val.inputs track by $index"><formgenerator val="input" index="$index"></formgenerator></li></ul>';
}
scope.deleteMe = function (index) {
scope.$parent.val.inputs.splice(index, 1);
//var inpts = scope.$parent.val.inputs;
//inpts.splice(index, 1);
//scope.$parent.val.inputs = inpts;
//scope.$parent.$parent.val.inputs.splice(index, 1);
//scope.$parent.$parent.$parent.val.inputs[scope.$parent.this.index].inputs.splice(scope.$parent.this.index, 1);
};
var newElement = angular.element(template);
$compile(newElement)(scope);
element.replaceWith(newElement);
}
};
}]);
Here is the object the controller is passing into the directive:
form = {
inputs:
[
{
type: 'text',
value: 'textValue',
label: 'Text value',
defaultValue: 'defaultTextValue'
},
{
inputs:
[
{
type: 'text',
value: 'textValue1',
label: 'Text value1',
defaultValue: 'defaultTextValue1'
},
{
type: 'select',
value: 'textValue2',
values: [{ name: '1st', id: 1 }, { name: '2nd', id: 2 }],
label: 'Text value2',
defaultValue: 'defaultTextValue2'
},
{
type: 'multiselect',
value: 'textValue3',
values: [{ name: '1st', id: 1 }, { name: '2nd', id: 2 }],
label: 'Text value3',
defaultValue: 'defaultTextValue3'
}
]
}
]
};
Here is the jsFiddle: http://jsfiddle.net/5YCe7/1/
Basically, if I hit the delete button next to Text value2, I would expect the single select to 'disappear' and the multiselect to 'move up'. What seems to be happening though is that the values of the multiselect move in place of the values for the select.
Any help on this would be greatly appreciated.
There are a few errors here:
When you press deleteMe, you need to again compile and replace the element with the new element. link won't automatically be called again.
I don't think your index is being assigned correctly
I would recommend looking at a few links before making recursive directives:
Is it possible to make a Tree View with Angular?
Recursion in Angular directives

How to evaluate a filter string programmatically in an Angular expression

I'm trying to evaluate filters programmatically within an Angular expression in a template.
HTML:
<div ng-app="myApp">
<div ng-controller = "MyCtrl">
<div ng-grid="gridOptions" class="gridStyle"></div>
</div>
</div>
JS:
var app = angular.module('myApp', ['ngGrid']);
app.controller('MyCtrl', function ($scope) {
$scope.myData = [{name: "Moroni", age: 50},
{name: "Tiancum", age: 43},
{name: "Jacob", age: 43},
{name: "Nephi", age: 29},
{name: "Enos", age: 34}];
$scope.gridOptions = {
data: 'myData',
columnDefs: [{ field: "name", },
{ field: "age", cellTemplate: '<div><div>{{row.getProperty(col.field) | col.colDef.filter}}</div></div>', cellFilter: 'test' }],
};
});
app.filter('test', function () {
return function (input) {
return input + '!';
};
});
CSS:
.gridStyle {
border: 1px solid rgb(212, 212, 212);
width: 100%;
height: 500px;
}
As you can see, in the cellTemplate for the 'age' column, I'm trying to pass through the cell data through a filter that is in a string in my column definitions (col.colDef.filter).
Is this possible?
I want to do this because I want to use just one template but define a variety of filters on each of the columns.
http://jsfiddle.net/GWha8/2/
How about simply adding test as the filter instead of getting the col's:
{{row.getProperty(col.field) | test}}
Updated jsfiddle.
The gridOption id you should be using is cellFilter (not filter). If you are not doing any other formatting that would require a cellTemplate all you need to do it set $scope.gridOptions.columnDefs[1].cellFilter = 'test' like this:
$scope.gridOptions = {
data: 'myData',
columnDefs: [
{
field: "name"
},
{
field: "age",
cellFilter: 'test'
}
]
};
However, if you are do need to do other changes that will require a cellTemplate then you can just reference your filter by name right in the template (no need to define $scope.gridOptions.columnDefs[1].cellFilter) like this:
$scope.gridOptions = {
data: 'myData',
columnDefs: [
{
field: "name"
},
{
field: "age",
cellTemplate: '<div class="ngCellText" ng-class="col.colIndex()">{{COL_FIELD | test}}</div>''
}
]
};
1) you can fix this in ng grid sources inside ngColumn, and use column cellFilter
if (colDef.cellTemplate && !TEMPLATE_REGEXP.test(colDef.cellTemplate)) {
self.cellTemplate = $.ajax({
type: "GET",
url: colDef.cellTemplate,
async: false
}).responseText;
}
self.cellTemplate = self.cellTemplate.replace(CUSTOM_FILTERS, self.cellFilter ? "|" + self.cellFilter : "");
2) you can create custom function which accept filter and return string
function getTemplateFilter(test){
return '<div><div>{{row.getProperty(col.field) | '+ test +'}}</div></div>'
}

Resources