Added boolean, array and custom formatted capabilities to xosTable
diff --git a/views/ngXosLib/xosHelpers/spec/ui/table.test.js b/views/ngXosLib/xosHelpers/spec/ui/table.test.js
index a6d85c8..31f93a9 100644
--- a/views/ngXosLib/xosHelpers/spec/ui/table.test.js
+++ b/views/ngXosLib/xosHelpers/spec/ui/table.test.js
@@ -7,15 +7,33 @@
(function () {
'use strict';
+ var scope, element, isolatedScope, rootScope, compile;
+ const compileElement = () => {
+
+ if(!scope){
+ scope = rootScope.$new();
+ }
+
+ element = angular.element('<xos-table config="config" data="data"></xos-table>');
+ compile(element)(scope);
+ scope.$digest();
+ isolatedScope = element.isolateScope().vm;
+ }
+
+
describe('The xos.helper module', function(){
describe('The xos-table component', () => {
beforeEach(module('xos.helpers'));
+ beforeEach(inject(function ($compile, $rootScope) {
+ compile = $compile;
+ rootScope = $rootScope;
+ }));
+
it('should throw an error if no config is specified', inject(($compile, $rootScope) => {
function errorFunctionWrapper(){
- $compile(angular.element('<xos-table></xos-table>'))($rootScope);
- $rootScope.$digest();
+ compileElement();
}
expect(errorFunctionWrapper).toThrow(new Error('[xosTable] Please provide a configuration via the "config" attribute'));
}));
@@ -23,18 +41,17 @@
it('should throw an error if no config columns are specified', inject(($compile, $rootScope) => {
function errorFunctionWrapper(){
// setup the parent scope
- let scope = $rootScope.$new();
+ scope = $rootScope.$new();
scope.config = 'green';
- $compile(angular.element('<xos-table config="config"></xos-table>'))(scope);
- $rootScope.$digest();
+ compileElement();
}
expect(errorFunctionWrapper).toThrow(new Error('[xosTable] Please provide a columns list in the configuration'));
}));
describe('when basicly configured', function() {
- var scope, element, isolatedScope;
beforeEach(inject(function ($compile, $rootScope) {
+
scope = $rootScope.$new();
scope.config = {
@@ -78,6 +95,13 @@
expect(tr.length).toEqual(3);
});
+ it('should render labels', () => {
+ let label1 = $(element).find('thead tr th')[0]
+ let label2 = $(element).find('thead tr th')[1]
+ expect($(label1).text().trim()).toEqual('Label 1');
+ expect($(label2).text().trim()).toEqual('Label 2');
+ });
+
describe('when no data are provided', () => {
beforeEach(() => {
isolatedScope.data = [];
@@ -91,10 +115,10 @@
});
});
- xdescribe('when a field type is provided', () => {
+ describe('when a field type is provided', () => {
describe('and is boolean', () => {
beforeEach(() => {
- isolatedScope.config = {
+ scope.config = {
columns: [
{
label: 'Label 1',
@@ -103,7 +127,7 @@
}
]
};
- isolatedScope.data = [
+ scope.data = [
{
'label-1': true
},
@@ -111,7 +135,7 @@
'label-1': false
}
];
- scope.$digest();
+ compileElement();
});
it('should render an incon', () => {
@@ -124,7 +148,7 @@
describe('and is date', () => {
beforeEach(() => {
- isolatedScope.config = {
+ scope.config = {
columns: [
{
label: 'Label 1',
@@ -133,17 +157,82 @@
}
]
};
- isolatedScope.data = [
+ scope.data = [
{
'label-1': '2015-02-17T22:06:38.059000Z'
}
];
- scope.$digest();
+ compileElement();
});
it('should render an formatted date', () => {
let td1 = $(element).find('tbody tr:first-child td')[0];
- expect($(td1).text()).toEqual('02-17-2015 14:06:38');
+ expect($(td1).text().trim()).toEqual('14:06 Feb 17, 2015');
+ });
+ });
+
+ describe('and is array', () => {
+ beforeEach(() => {
+ scope.data = [
+ {categories: ['Film', 'Music']}
+ ];
+ scope.config = {
+ columns: [
+ {
+ label: 'Categories',
+ prop: 'categories',
+ type: 'array'
+ }
+ ]
+ }
+ compileElement();
+ });
+ it('should render a comma separated list', () => {
+ let td1 = $(element).find('tbody tr:first-child')[0];
+ // console.log(td1);
+ expect($(td1).text().trim()).toEqual('Film, Music');
+ });
+ });
+
+ describe('and is custom', () => {
+ beforeEach(() => {
+ scope.data = [
+ {categories: ['Film', 'Music']}
+ ];
+ scope.config = {
+ columns: [
+ {
+ label: 'Categories',
+ prop: 'categories',
+ type: 'custom',
+ formatter: val => 'Formatted Content'
+ }
+ ]
+ }
+ compileElement();
+ });
+
+ it('should check for a formatter property', () => {
+ function errorFunctionWrapper(){
+ // setup the parent scope
+ scope = rootScope.$new();
+ scope.config = {
+ columns: [
+ {
+ label: 'Categories',
+ prop: 'categories',
+ type: 'custom'
+ }
+ ]
+ };
+ compileElement();
+ }
+ expect(errorFunctionWrapper).toThrow(new Error('[xosTable] You have provided a custom field type, a formatter function should provided too.'));
+ });
+
+ it('should format data using the formatter property', () => {
+ let td1 = $(element).find('tbody tr:first-child')[0];
+ expect($(td1).text().trim()).toEqual('Formatted Content');
});
});
});
diff --git a/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/table/table.component.js b/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/table/table.component.js
index 745c3ca..a38901a 100644
--- a/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/table/table.component.js
+++ b/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/table/table.component.js
@@ -227,7 +227,23 @@
</tbody>
<tbody>
<tr ng-repeat="item in vm.data | filter:vm.query | orderBy:vm.orderBy:vm.reverse | pagination:vm.currentPage * vm.config.pagination.pageSize | limitTo: (vm.config.pagination.pageSize || vm.data.length) track by $index">
- <td ng-repeat="col in vm.columns">{{item[col.prop]}}</td>
+ <td ng-repeat="col in vm.columns">
+ <span ng-if="!col.type">{{item[col.prop]}}</span>
+ <span ng-if="col.type === 'boolean'">
+ <i class="glyphicon"
+ ng-class="{'glyphicon-ok': item[col.prop], 'glyphicon-remove': !item[col.prop]}">
+ </i>
+ </span>
+ <span ng-if="col.type === 'date'">
+ {{item[col.prop] | date:'H:mm MMM d, yyyy'}}
+ </span>
+ <span ng-if="col.type === 'array'">
+ {{item[col.prop] | arrayToList}}
+ </span>
+ <span ng-if="col.type === 'custom'">
+ {{col.formatter(item[col.prop])}}
+ </span>
+ </td>
<td ng-if="vm.config.actions">
<a href=""
ng-repeat="action in vm.config.actions"
@@ -256,7 +272,7 @@
`,
bindToController: true,
controllerAs: 'vm',
- controller: function(){
+ controller: function(_){
if(!this.config){
throw new Error('[xosTable] Please provide a configuration via the "config" attribute');
@@ -266,6 +282,17 @@
throw new Error('[xosTable] Please provide a columns list in the configuration');
}
+ // if columns with type 'custom' are provide
+ // check that a custom formatted is provided too
+ let customCols = _.filter(this.config.columns, {type: 'custom'});
+ if(angular.isArray(customCols) && customCols.length > 0){
+ _.forEach(customCols, (col) => {
+ if(!col.formatter){
+ throw new Error('[xosTable] You have provided a custom field type, a formatter function should provided too.');
+ }
+ })
+ }
+
this.columns = this.config.columns;
this.classes = this.config.classes || 'table table-striped table-bordered';
@@ -282,4 +309,12 @@
}
}
})
+.filter('arrayToList', function(){
+ return (input) => {
+ if(!angular.isArray(input)){
+ throw new Error('[xosArrayToList] This filter require an array');
+ }
+ return input.join(', ');
+ }
+});
})();
diff --git a/xos/core/xoslib/static/js/vendor/ngXosHelpers.js b/xos/core/xoslib/static/js/vendor/ngXosHelpers.js
index 7985bc5..de44e1b 100644
--- a/xos/core/xoslib/static/js/vendor/ngXosHelpers.js
+++ b/xos/core/xoslib/static/js/vendor/ngXosHelpers.js
@@ -258,100 +258,6 @@
*
* Visit http://guide.xosproject.org/devguide/addview/ for more information
*
- * Created by teone on 4/15/16.
- */
-
-(function () {
- 'use strict';
-
- angular.module('xos.uiComponents')
-
- /**
- * @ngdoc directive
- * @name xos.uiComponents.directive:xosValidation
- * @restrict E
- * @description The xos-validation directive
- * @param {Object} errors The error object
- * @element ANY
- * @scope
- * @example
- <example module="sampleValidation">
- <file name="index.html">
- <div ng-controller="SampleCtrl as vm">
- <div class="row">
- <div class="col-xs-12">
- <label>Set an error type:</label>
- </div>
- <div class="col-xs-2">
- <a class="btn"
- ng-click="vm.errors.required = !vm.errors.required"
- ng-class="{'btn-default': !vm.errors.required, 'btn-success': vm.errors.required}">
- Required
- </a>
- </div>
- <div class="col-xs-2">
- <a class="btn"
- ng-click="vm.errors.email = !vm.errors.email"
- ng-class="{'btn-default': !vm.errors.email, 'btn-success': vm.errors.email}">
- Email
- </a>
- </div>
- <div class="col-xs-2">
- <a class="btn"
- ng-click="vm.errors.minlength = !vm.errors.minlength"
- ng-class="{'btn-default': !vm.errors.minlength, 'btn-success': vm.errors.minlength}">
- Min Length
- </a>
- </div>
- <div class="col-xs-2">
- <a class="btn"
- ng-click="vm.errors.maxlength = !vm.errors.maxlength"
- ng-class="{'btn-default': !vm.errors.maxlength, 'btn-success': vm.errors.maxlength}">
- Max Length
- </a>
- </div>
- </div>
- <xos-validation errors="vm.errors"></xos-validation>
- </div>
- </file>
- <file name="script.js">
- angular.module('sampleValidation', ['xos.uiComponents'])
- .controller('SampleCtrl', function(){
- this.errors = {
- email: false
- }
- });
- </file>
- </example>
- */
-
- .directive('xosValidation', function () {
- return {
- restrict: 'E',
- scope: {
- errors: '='
- },
- template: '\n <div ng-cloak>\n <!-- <pre>{{vm.errors.email | json}}</pre> -->\n <xos-alert config="vm.config" show="vm.errors.required !== undefined && vm.errors.required !== false">\n Field required\n </xos-alert>\n <xos-alert config="vm.config" show="vm.errors.email !== undefined && vm.errors.email !== false">\n This is not a valid email\n </xos-alert>\n <xos-alert config="vm.config" show="vm.errors.minlength !== undefined && vm.errors.minlength !== false">\n Too short\n </xos-alert>\n <xos-alert config="vm.config" show="vm.errors.maxlength !== undefined && vm.errors.maxlength !== false">\n Too long\n </xos-alert>\n <xos-alert config="vm.config" show="vm.errors.custom !== undefined && vm.errors.custom !== false">\n Field invalid\n </xos-alert>\n </div>\n ',
- transclude: true,
- bindToController: true,
- controllerAs: 'vm',
- controller: function controller() {
- this.config = {
- type: 'danger'
- };
- }
- };
- });
-})();
-//# sourceMappingURL=../../../maps/ui_components/dumbComponents/validation/validation.component.js.map
-
-'use strict';
-
-/**
- * © OpenCORD
- *
- * Visit http://guide.xosproject.org/devguide/addview/ for more information
- *
* Created by teone on 3/24/16.
*/
@@ -593,6 +499,100 @@
*
* Visit http://guide.xosproject.org/devguide/addview/ for more information
*
+ * Created by teone on 4/15/16.
+ */
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.uiComponents')
+
+ /**
+ * @ngdoc directive
+ * @name xos.uiComponents.directive:xosValidation
+ * @restrict E
+ * @description The xos-validation directive
+ * @param {Object} errors The error object
+ * @element ANY
+ * @scope
+ * @example
+ <example module="sampleValidation">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl as vm">
+ <div class="row">
+ <div class="col-xs-12">
+ <label>Set an error type:</label>
+ </div>
+ <div class="col-xs-2">
+ <a class="btn"
+ ng-click="vm.errors.required = !vm.errors.required"
+ ng-class="{'btn-default': !vm.errors.required, 'btn-success': vm.errors.required}">
+ Required
+ </a>
+ </div>
+ <div class="col-xs-2">
+ <a class="btn"
+ ng-click="vm.errors.email = !vm.errors.email"
+ ng-class="{'btn-default': !vm.errors.email, 'btn-success': vm.errors.email}">
+ Email
+ </a>
+ </div>
+ <div class="col-xs-2">
+ <a class="btn"
+ ng-click="vm.errors.minlength = !vm.errors.minlength"
+ ng-class="{'btn-default': !vm.errors.minlength, 'btn-success': vm.errors.minlength}">
+ Min Length
+ </a>
+ </div>
+ <div class="col-xs-2">
+ <a class="btn"
+ ng-click="vm.errors.maxlength = !vm.errors.maxlength"
+ ng-class="{'btn-default': !vm.errors.maxlength, 'btn-success': vm.errors.maxlength}">
+ Max Length
+ </a>
+ </div>
+ </div>
+ <xos-validation errors="vm.errors"></xos-validation>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleValidation', ['xos.uiComponents'])
+ .controller('SampleCtrl', function(){
+ this.errors = {
+ email: false
+ }
+ });
+ </file>
+ </example>
+ */
+
+ .directive('xosValidation', function () {
+ return {
+ restrict: 'E',
+ scope: {
+ errors: '='
+ },
+ template: '\n <div ng-cloak>\n <!-- <pre>{{vm.errors.email | json}}</pre> -->\n <xos-alert config="vm.config" show="vm.errors.required !== undefined && vm.errors.required !== false">\n Field required\n </xos-alert>\n <xos-alert config="vm.config" show="vm.errors.email !== undefined && vm.errors.email !== false">\n This is not a valid email\n </xos-alert>\n <xos-alert config="vm.config" show="vm.errors.minlength !== undefined && vm.errors.minlength !== false">\n Too short\n </xos-alert>\n <xos-alert config="vm.config" show="vm.errors.maxlength !== undefined && vm.errors.maxlength !== false">\n Too long\n </xos-alert>\n <xos-alert config="vm.config" show="vm.errors.custom !== undefined && vm.errors.custom !== false">\n Field invalid\n </xos-alert>\n </div>\n ',
+ transclude: true,
+ bindToController: true,
+ controllerAs: 'vm',
+ controller: function controller() {
+ this.config = {
+ type: 'danger'
+ };
+ }
+ };
+ });
+})();
+//# sourceMappingURL=../../../maps/ui_components/dumbComponents/validation/validation.component.js.map
+
+'use strict';
+
+/**
+ * © OpenCORD
+ *
+ * Visit http://guide.xosproject.org/devguide/addview/ for more information
+ *
* Created by teone on 3/24/16.
*/
@@ -769,10 +769,10 @@
data: '=',
config: '='
},
- template: '\n <div ng-show="vm.data.length > 0">\n <div class="row" ng-if="vm.config.filter == \'fulltext\'">\n <div class="col-xs-12">\n <input\n class="form-control"\n placeholder="Type to search.."\n type="text"\n ng-model="vm.query"/>\n </div>\n </div>\n <table ng-class="vm.classes" ng-hide="vm.data.length == 0">\n <thead>\n <tr>\n <th ng-repeat="col in vm.columns">\n {{col.label}}\n <span ng-if="vm.config.order">\n <a href="" ng-click="vm.orderBy = col.prop; vm.reverse = false">\n <i class="glyphicon glyphicon-chevron-up"></i>\n </a>\n <a href="" ng-click="vm.orderBy = col.prop; vm.reverse = true">\n <i class="glyphicon glyphicon-chevron-down"></i>\n </a>\n </span>\n </th>\n <th ng-if="vm.config.actions">Actions:</th>\n </tr>\n </thead>\n <tbody ng-if="vm.config.filter == \'field\'">\n <tr>\n <td ng-repeat="col in vm.columns">\n <input\n class="form-control"\n placeholder="Type to search by {{col.label}}"\n type="text"\n ng-model="vm.query[col.prop]"/>\n </td>\n <td ng-if="vm.config.actions"></td>\n </tr>\n </tbody>\n <tbody>\n <tr ng-repeat="item in vm.data | filter:vm.query | orderBy:vm.orderBy:vm.reverse | pagination:vm.currentPage * vm.config.pagination.pageSize | limitTo: (vm.config.pagination.pageSize || vm.data.length) track by $index">\n <td ng-repeat="col in vm.columns">{{item[col.prop]}}</td>\n <td ng-if="vm.config.actions">\n <a href=""\n ng-repeat="action in vm.config.actions"\n ng-click="action.cb(item)"\n title="{{action.label}}">\n <i\n class="glyphicon glyphicon-{{action.icon}}"\n style="color: {{action.color}};"></i>\n </a>\n </td>\n </tr>\n </tbody>\n </table>\n <xos-pagination\n ng-if="vm.config.pagination"\n page-size="vm.config.pagination.pageSize"\n total-elements="vm.data.length"\n change="vm.goToPage">\n </xos-pagination>\n </div>\n <div ng-show="vm.data.length == 0 || !vm.data">\n <xos-alert config="{type: \'info\'}">\n No data to show.\n </xos-alert>\n </div>\n ',
+ template: '\n <div ng-show="vm.data.length > 0">\n <div class="row" ng-if="vm.config.filter == \'fulltext\'">\n <div class="col-xs-12">\n <input\n class="form-control"\n placeholder="Type to search.."\n type="text"\n ng-model="vm.query"/>\n </div>\n </div>\n <table ng-class="vm.classes" ng-hide="vm.data.length == 0">\n <thead>\n <tr>\n <th ng-repeat="col in vm.columns">\n {{col.label}}\n <span ng-if="vm.config.order">\n <a href="" ng-click="vm.orderBy = col.prop; vm.reverse = false">\n <i class="glyphicon glyphicon-chevron-up"></i>\n </a>\n <a href="" ng-click="vm.orderBy = col.prop; vm.reverse = true">\n <i class="glyphicon glyphicon-chevron-down"></i>\n </a>\n </span>\n </th>\n <th ng-if="vm.config.actions">Actions:</th>\n </tr>\n </thead>\n <tbody ng-if="vm.config.filter == \'field\'">\n <tr>\n <td ng-repeat="col in vm.columns">\n <input\n class="form-control"\n placeholder="Type to search by {{col.label}}"\n type="text"\n ng-model="vm.query[col.prop]"/>\n </td>\n <td ng-if="vm.config.actions"></td>\n </tr>\n </tbody>\n <tbody>\n <tr ng-repeat="item in vm.data | filter:vm.query | orderBy:vm.orderBy:vm.reverse | pagination:vm.currentPage * vm.config.pagination.pageSize | limitTo: (vm.config.pagination.pageSize || vm.data.length) track by $index">\n <td ng-repeat="col in vm.columns">\n <span ng-if="!col.type">{{item[col.prop]}}</span>\n <span ng-if="col.type === \'boolean\'">\n <i class="glyphicon"\n ng-class="{\'glyphicon-ok\': item[col.prop], \'glyphicon-remove\': !item[col.prop]}">\n </i>\n </span>\n <span ng-if="col.type === \'date\'">\n {{item[col.prop] | date:\'H:mm MMM d, yyyy\'}}\n </span>\n <span ng-if="col.type === \'array\'">\n {{item[col.prop] | arrayToList}}\n </span>\n <span ng-if="col.type === \'custom\'">\n {{col.formatter(item[col.prop])}}\n </span>\n </td>\n <td ng-if="vm.config.actions">\n <a href=""\n ng-repeat="action in vm.config.actions"\n ng-click="action.cb(item)"\n title="{{action.label}}">\n <i\n class="glyphicon glyphicon-{{action.icon}}"\n style="color: {{action.color}};"></i>\n </a>\n </td>\n </tr>\n </tbody>\n </table>\n <xos-pagination\n ng-if="vm.config.pagination"\n page-size="vm.config.pagination.pageSize"\n total-elements="vm.data.length"\n change="vm.goToPage">\n </xos-pagination>\n </div>\n <div ng-show="vm.data.length == 0 || !vm.data">\n <xos-alert config="{type: \'info\'}">\n No data to show.\n </xos-alert>\n </div>\n ',
bindToController: true,
controllerAs: 'vm',
- controller: function controller() {
+ controller: ["_", function controller(_) {
var _this = this;
if (!this.config) {
@@ -783,6 +783,17 @@
throw new Error('[xosTable] Please provide a columns list in the configuration');
}
+ // if columns with type 'custom' are provide
+ // check that a custom formatted is provided too
+ var customCols = _.filter(this.config.columns, { type: 'custom' });
+ if (angular.isArray(customCols) && customCols.length > 0) {
+ _.forEach(customCols, function (col) {
+ if (!col.formatter) {
+ throw new Error('[xosTable] You have provided a custom field type, a formatter function should provided too.');
+ }
+ });
+ }
+
this.columns = this.config.columns;
this.classes = this.config.classes || 'table table-striped table-bordered';
@@ -795,7 +806,14 @@
_this.currentPage = n;
};
}
+ }]
+ };
+ }).filter('arrayToList', function () {
+ return function (input) {
+ if (!angular.isArray(input)) {
+ throw new Error('[xosArrayToList] This filter require an array');
}
+ return input.join(', ');
};
});
})();