ui-grid.validate.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. /*!
  2. * ui-grid - v4.4.6 - 2018-04-06
  3. * Copyright (c) 2018 ; License: MIT
  4. */
  5. (function () {
  6. 'use strict';
  7. /**
  8. * @ngdoc overview
  9. * @name ui.grid.validate
  10. * @description
  11. *
  12. * # ui.grid.validate
  13. *
  14. * <div class="alert alert-warning" role="alert"><strong>Alpha</strong> This feature is in development. There will almost certainly be breaking api changes, or there are major outstanding bugs.</div>
  15. *
  16. * This module provides the ability to validate cells upon change.
  17. *
  18. * Design information:
  19. * -------------------
  20. *
  21. * Validation is not based on angularjs validation, since it would work only when editing the field.
  22. *
  23. * Instead it adds custom properties to any field considered as invalid.
  24. *
  25. * <br/>
  26. * <br/>
  27. *
  28. * <div doc-module-components="ui.grid.expandable"></div>
  29. */
  30. var module = angular.module('ui.grid.validate', ['ui.grid']);
  31. /**
  32. * @ngdoc service
  33. * @name ui.grid.validate.service:uiGridValidateService
  34. *
  35. * @description Services for validation features
  36. */
  37. module.service('uiGridValidateService', ['$sce', '$q', '$http', 'i18nService', 'uiGridConstants', function ($sce, $q, $http, i18nService, uiGridConstants) {
  38. var service = {
  39. /**
  40. * @ngdoc object
  41. * @name validatorFactories
  42. * @propertyOf ui.grid.validate.service:uiGridValidateService
  43. * @description object containing all the factories used to validate data.<br/>
  44. * These factories will be in the form <br/>
  45. * ```
  46. * {
  47. * validatorFactory: function(argument) {
  48. * return function(newValue, oldValue, rowEntity, colDef) {
  49. * return true || false || promise
  50. * }
  51. * },
  52. * messageFunction: function(argument) {
  53. * return string
  54. * }
  55. * }
  56. * ```
  57. *
  58. * Promises should return true or false as result according to the result of validation.
  59. */
  60. validatorFactories: {},
  61. /**
  62. * @ngdoc service
  63. * @name setExternalFactoryFunction
  64. * @methodOf ui.grid.validate.service:uiGridValidateService
  65. * @description Adds a way to retrieve validators from an external service
  66. * <p>Validators from this external service have a higher priority than default
  67. * ones
  68. * @param {function} externalFactoryFunction a function that accepts name and argument to pass to a
  69. * validator factory and that returns an object with the same properties as
  70. * you can see in {@link ui.grid.validate.service:uiGridValidateService#properties_validatorFactories validatorFactories}
  71. */
  72. setExternalFactoryFunction: function(externalFactoryFunction) {
  73. service.externalFactoryFunction = externalFactoryFunction;
  74. },
  75. /**
  76. * @ngdoc service
  77. * @name clearExternalFactory
  78. * @methodOf ui.grid.validate.service:uiGridValidateService
  79. * @description Removes any link to external factory from this service
  80. */
  81. clearExternalFactory: function() {
  82. delete service.externalFactoryFunction;
  83. },
  84. /**
  85. * @ngdoc service
  86. * @name getValidatorFromExternalFactory
  87. * @methodOf ui.grid.validate.service:uiGridValidateService
  88. * @description Retrieves a validator by executing a validatorFactory
  89. * stored in an external service.
  90. * @param {string} name the name of the validator to retrieve
  91. * @param {object} argument an argument to pass to the validator factory
  92. */
  93. getValidatorFromExternalFactory: function(name, argument) {
  94. return service.externalFactoryFunction(name, argument).validatorFactory(argument);
  95. },
  96. /**
  97. * @ngdoc service
  98. * @name getMessageFromExternalFactory
  99. * @methodOf ui.grid.validate.service:uiGridValidateService
  100. * @description Retrieves a message stored in an external service.
  101. * @param {string} name the name of the validator
  102. * @param {object} argument an argument to pass to the message function
  103. */
  104. getMessageFromExternalFactory: function(name, argument) {
  105. return service.externalFactoryFunction(name, argument).messageFunction(argument);
  106. },
  107. /**
  108. * @ngdoc service
  109. * @name setValidator
  110. * @methodOf ui.grid.validate.service:uiGridValidateService
  111. * @description Adds a new validator to the service
  112. * @param {string} name the name of the validator, must be unique
  113. * @param {function} validatorFactory a factory that return a validatorFunction
  114. * @param {function} messageFunction a function that return the error message
  115. */
  116. setValidator: function(name, validatorFactory, messageFunction) {
  117. service.validatorFactories[name] = {
  118. validatorFactory: validatorFactory,
  119. messageFunction: messageFunction
  120. };
  121. },
  122. /**
  123. * @ngdoc service
  124. * @name getValidator
  125. * @methodOf ui.grid.validate.service:uiGridValidateService
  126. * @description Returns a validator registered to the service
  127. * or retrieved from the external factory
  128. * @param {string} name the name of the validator to retrieve
  129. * @param {object} argument an argument to pass to the validator factory
  130. * @returns {object} the validator function
  131. */
  132. getValidator: function(name, argument) {
  133. if (service.externalFactoryFunction) {
  134. var validator = service.getValidatorFromExternalFactory(name, argument);
  135. if (validator) {
  136. return validator;
  137. }
  138. }
  139. if (!service.validatorFactories[name]) {
  140. throw ("Invalid validator name: " + name);
  141. }
  142. return service.validatorFactories[name].validatorFactory(argument);
  143. },
  144. /**
  145. * @ngdoc service
  146. * @name getMessage
  147. * @methodOf ui.grid.validate.service:uiGridValidateService
  148. * @description Returns the error message related to the validator
  149. * @param {string} name the name of the validator
  150. * @param {object} argument an argument to pass to the message function
  151. * @returns {string} the error message related to the validator
  152. */
  153. getMessage: function(name, argument) {
  154. if (service.externalFactoryFunction) {
  155. var message = service.getMessageFromExternalFactory(name, argument);
  156. if (message) {
  157. return message;
  158. }
  159. }
  160. return service.validatorFactories[name].messageFunction(argument);
  161. },
  162. /**
  163. * @ngdoc service
  164. * @name isInvalid
  165. * @methodOf ui.grid.validate.service:uiGridValidateService
  166. * @description Returns true if the cell (identified by rowEntity, colDef) is invalid
  167. * @param {object} rowEntity the row entity of the cell
  168. * @param {object} colDef the colDef of the cell
  169. * @returns {boolean} true if the cell is invalid
  170. */
  171. isInvalid: function (rowEntity, colDef) {
  172. return rowEntity['$$invalid'+colDef.name];
  173. },
  174. /**
  175. * @ngdoc service
  176. * @name setInvalid
  177. * @methodOf ui.grid.validate.service:uiGridValidateService
  178. * @description Makes the cell invalid by adding the proper field to the entity
  179. * @param {object} rowEntity the row entity of the cell
  180. * @param {object} colDef the colDef of the cell
  181. */
  182. setInvalid: function (rowEntity, colDef) {
  183. rowEntity['$$invalid'+colDef.name] = true;
  184. },
  185. /**
  186. * @ngdoc service
  187. * @name setValid
  188. * @methodOf ui.grid.validate.service:uiGridValidateService
  189. * @description Makes the cell valid by removing the proper error field from the entity
  190. * @param {object} rowEntity the row entity of the cell
  191. * @param {object} colDef the colDef of the cell
  192. */
  193. setValid: function (rowEntity, colDef) {
  194. delete rowEntity['$$invalid'+colDef.name];
  195. },
  196. /**
  197. * @ngdoc service
  198. * @name setError
  199. * @methodOf ui.grid.validate.service:uiGridValidateService
  200. * @description Adds the proper error to the entity errors field
  201. * @param {object} rowEntity the row entity of the cell
  202. * @param {object} colDef the colDef of the cell
  203. * @param {string} validatorName the name of the validator that is failing
  204. */
  205. setError: function(rowEntity, colDef, validatorName) {
  206. if (!rowEntity['$$errors'+colDef.name]) {
  207. rowEntity['$$errors'+colDef.name] = {};
  208. }
  209. rowEntity['$$errors'+colDef.name][validatorName] = true;
  210. },
  211. /**
  212. * @ngdoc service
  213. * @name clearError
  214. * @methodOf ui.grid.validate.service:uiGridValidateService
  215. * @description Removes the proper error from the entity errors field
  216. * @param {object} rowEntity the row entity of the cell
  217. * @param {object} colDef the colDef of the cell
  218. * @param {string} validatorName the name of the validator that is failing
  219. */
  220. clearError: function(rowEntity, colDef, validatorName) {
  221. if (!rowEntity['$$errors'+colDef.name]) {
  222. return;
  223. }
  224. if (validatorName in rowEntity['$$errors'+colDef.name]) {
  225. delete rowEntity['$$errors'+colDef.name][validatorName];
  226. }
  227. },
  228. /**
  229. * @ngdoc function
  230. * @name getErrorMessages
  231. * @methodOf ui.grid.validate.service:uiGridValidateService
  232. * @description returns an array of i18n-ed error messages.
  233. * @param {object} rowEntity gridOptions.data[] array instance whose errors we are looking for
  234. * @param {object} colDef the column whose errors we are looking for
  235. * @returns {array} An array of strings containing all the error messages for the cell
  236. */
  237. getErrorMessages: function(rowEntity, colDef) {
  238. var errors = [];
  239. if (!rowEntity['$$errors'+colDef.name] || Object.keys(rowEntity['$$errors'+colDef.name]).length === 0) {
  240. return errors;
  241. }
  242. Object.keys(rowEntity['$$errors'+colDef.name]).sort().forEach(function(validatorName) {
  243. errors.push(service.getMessage(validatorName, colDef.validators[validatorName]));
  244. });
  245. return errors;
  246. },
  247. /**
  248. * @ngdoc function
  249. * @name getFormattedErrors
  250. * @methodOf ui.grid.validate.service:uiGridValidateService
  251. * @description returns the error i18n-ed and formatted in html to be shown inside the page.
  252. * @param {object} rowEntity gridOptions.data[] array instance whose errors we are looking for
  253. * @param {object} colDef the column whose errors we are looking for
  254. * @returns {object} An object that can be used in a template (like a cellTemplate) to display the
  255. * message inside the page (i.e. inside a div)
  256. */
  257. getFormattedErrors: function(rowEntity, colDef) {
  258. var msgString = "";
  259. var errors = service.getErrorMessages(rowEntity, colDef);
  260. if (!errors.length) {
  261. return;
  262. }
  263. errors.forEach(function(errorMsg) {
  264. msgString += errorMsg + "<br/>";
  265. });
  266. return $sce.trustAsHtml('<p><b>' + i18nService.getSafeText('validate.error') + '</b></p>' + msgString );
  267. },
  268. /**
  269. * @ngdoc function
  270. * @name getTitleFormattedErrors
  271. * @methodOf ui.grid.validate.service:uiGridValidateService
  272. * @description returns the error i18n-ed and formatted in javaScript to be shown inside an html
  273. * title attribute.
  274. * @param {object} rowEntity gridOptions.data[] array instance whose errors we are looking for
  275. * @param {object} colDef the column whose errors we are looking for
  276. * @returns {object} An object that can be used in a template (like a cellTemplate) to display the
  277. * message inside an html title attribute
  278. */
  279. getTitleFormattedErrors: function(rowEntity, colDef) {
  280. var newLine = "\n";
  281. var msgString = "";
  282. var errors = service.getErrorMessages(rowEntity, colDef);
  283. if (!errors.length) {
  284. return;
  285. }
  286. errors.forEach(function(errorMsg) {
  287. msgString += errorMsg + newLine;
  288. });
  289. return $sce.trustAsHtml(i18nService.getSafeText('validate.error') + newLine + msgString);
  290. },
  291. /**
  292. * @ngdoc function
  293. * @name getTitleFormattedErrors
  294. * @methodOf ui.grid.validate.service:uiGridValidateService
  295. * @description Executes all validators on a cell (identified by row entity and column definition) and sets or clears errors
  296. * @param {object} rowEntity the row entity of the cell we want to run the validators on
  297. * @param {object} colDef the column definition of the cell we want to run the validators on
  298. * @param {object} newValue the value the user just entered
  299. * @param {object} oldValue the value the field had before
  300. */
  301. runValidators: function(rowEntity, colDef, newValue, oldValue, grid) {
  302. if (newValue === oldValue) {
  303. // If the value has not changed we perform no validation
  304. return;
  305. }
  306. if (typeof(colDef.name) === 'undefined' || !colDef.name) {
  307. throw new Error('colDef.name is required to perform validation');
  308. }
  309. service.setValid(rowEntity, colDef);
  310. var validateClosureFactory = function(rowEntity, colDef, validatorName) {
  311. return function(value) {
  312. if (!value) {
  313. service.setInvalid(rowEntity, colDef);
  314. service.setError(rowEntity, colDef, validatorName);
  315. if (grid) {
  316. grid.api.validate.raise.validationFailed(rowEntity, colDef, newValue, oldValue);
  317. }
  318. }
  319. };
  320. };
  321. var promises = [];
  322. for (var validatorName in colDef.validators) {
  323. service.clearError(rowEntity, colDef, validatorName);
  324. var msg;
  325. var validatorFunction = service.getValidator(validatorName, colDef.validators[validatorName]);
  326. // We pass the arguments as oldValue, newValue so they are in the same order
  327. // as ng-model validators (modelValue, viewValue)
  328. var promise = $q
  329. .when(validatorFunction(oldValue, newValue, rowEntity, colDef))
  330. .then(validateClosureFactory(rowEntity, colDef, validatorName));
  331. promises.push(promise);
  332. }
  333. return $q.all(promises);
  334. },
  335. /**
  336. * @ngdoc function
  337. * @name createDefaultValidators
  338. * @methodOf ui.grid.validate.service:uiGridValidateService
  339. * @description adds the basic validators to the list of service validators
  340. */
  341. createDefaultValidators: function() {
  342. service.setValidator('minLength',
  343. function (argument) {
  344. return function (oldValue, newValue, rowEntity, colDef) {
  345. if (newValue === undefined || newValue === null || newValue === '') {
  346. return true;
  347. }
  348. return newValue.length >= argument;
  349. };
  350. },
  351. function(argument) {
  352. return i18nService.getSafeText('validate.minLength').replace('THRESHOLD', argument);
  353. });
  354. service.setValidator('maxLength',
  355. function (argument) {
  356. return function (oldValue, newValue, rowEntity, colDef) {
  357. if (newValue === undefined || newValue === null || newValue === '') {
  358. return true;
  359. }
  360. return newValue.length <= argument;
  361. };
  362. },
  363. function(threshold) {
  364. return i18nService.getSafeText('validate.maxLength').replace('THRESHOLD', threshold);
  365. });
  366. service.setValidator('required',
  367. function (argument) {
  368. return function (oldValue, newValue, rowEntity, colDef) {
  369. if (argument) {
  370. return !(newValue === undefined || newValue === null || newValue === '');
  371. }
  372. return true;
  373. };
  374. },
  375. function(argument) {
  376. return i18nService.getSafeText('validate.required');
  377. });
  378. },
  379. initializeGrid: function (scope, grid) {
  380. grid.validate = {
  381. isInvalid: service.isInvalid,
  382. getFormattedErrors: service.getFormattedErrors,
  383. getTitleFormattedErrors: service.getTitleFormattedErrors,
  384. runValidators: service.runValidators
  385. };
  386. /**
  387. * @ngdoc object
  388. * @name ui.grid.validate.api:PublicApi
  389. *
  390. * @description Public Api for validation feature
  391. */
  392. var publicApi = {
  393. events: {
  394. validate: {
  395. /**
  396. * @ngdoc event
  397. * @name validationFailed
  398. * @eventOf ui.grid.validate.api:PublicApi
  399. * @description raised when one or more failure happened during validation
  400. * <pre>
  401. * gridApi.validate.on.validationFailed(scope, function(rowEntity, colDef, newValue, oldValue){...})
  402. * </pre>
  403. * @param {object} rowEntity the options.data element whose validation failed
  404. * @param {object} colDef the column whose validation failed
  405. * @param {object} newValue new value
  406. * @param {object} oldValue old value
  407. */
  408. validationFailed: function (rowEntity, colDef, newValue, oldValue) {
  409. }
  410. }
  411. },
  412. methods: {
  413. validate: {
  414. /**
  415. * @ngdoc function
  416. * @name isInvalid
  417. * @methodOf ui.grid.validate.api:PublicApi
  418. * @description checks if a cell (identified by rowEntity, colDef) is invalid
  419. * @param {object} rowEntity gridOptions.data[] array instance we want to check
  420. * @param {object} colDef the column whose errors we want to check
  421. * @returns {boolean} true if the cell value is not valid
  422. */
  423. isInvalid: function(rowEntity, colDef) {
  424. return grid.validate.isInvalid(rowEntity, colDef);
  425. },
  426. /**
  427. * @ngdoc function
  428. * @name getErrorMessages
  429. * @methodOf ui.grid.validate.api:PublicApi
  430. * @description returns an array of i18n-ed error messages.
  431. * @param {object} rowEntity gridOptions.data[] array instance whose errors we are looking for
  432. * @param {object} colDef the column whose errors we are looking for
  433. * @returns {array} An array of strings containing all the error messages for the cell
  434. */
  435. getErrorMessages: function (rowEntity, colDef) {
  436. return grid.validate.getErrorMessages(rowEntity, colDef);
  437. },
  438. /**
  439. * @ngdoc function
  440. * @name getFormattedErrors
  441. * @methodOf ui.grid.validate.api:PublicApi
  442. * @description returns the error i18n-ed and formatted in html to be shown inside the page.
  443. * @param {object} rowEntity gridOptions.data[] array instance whose errors we are looking for
  444. * @param {object} colDef the column whose errors we are looking for
  445. * @returns {object} An object that can be used in a template (like a cellTemplate) to display the
  446. * message inside the page (i.e. inside a div)
  447. */
  448. getFormattedErrors: function (rowEntity, colDef) {
  449. return grid.validate.getFormattedErrors(rowEntity, colDef);
  450. },
  451. /**
  452. * @ngdoc function
  453. * @name getTitleFormattedErrors
  454. * @methodOf ui.grid.validate.api:PublicApi
  455. * @description returns the error i18n-ed and formatted in javaScript to be shown inside an html
  456. * title attribute.
  457. * @param {object} rowEntity gridOptions.data[] array instance whose errors we are looking for
  458. * @param {object} colDef the column whose errors we are looking for
  459. * @returns {object} An object that can be used in a template (like a cellTemplate) to display the
  460. * message inside an html title attribute
  461. */
  462. getTitleFormattedErrors: function (rowEntity, colDef) {
  463. return grid.validate.getTitleFormattedErrors(rowEntity, colDef);
  464. }
  465. }
  466. }
  467. };
  468. grid.api.registerEventsFromObject(publicApi.events);
  469. grid.api.registerMethodsFromObject(publicApi.methods);
  470. if (grid.edit) {
  471. grid.api.edit.on.afterCellEdit(scope, function(rowEntity, colDef, newValue, oldValue) {
  472. grid.validate.runValidators(rowEntity, colDef, newValue, oldValue, grid);
  473. });
  474. }
  475. service.createDefaultValidators();
  476. }
  477. };
  478. return service;
  479. }]);
  480. /**
  481. * @ngdoc directive
  482. * @name ui.grid.validate.directive:uiGridValidate
  483. * @element div
  484. * @restrict A
  485. * @description Adds validating features to the ui-grid directive.
  486. * @example
  487. <example module="app">
  488. <file name="app.js">
  489. var app = angular.module('app', ['ui.grid', 'ui.grid.edit', 'ui.grid.validate']);
  490. app.controller('MainCtrl', ['$scope', function ($scope) {
  491. $scope.data = [
  492. { name: 'Bob', title: 'CEO' },
  493. { name: 'Frank', title: 'Lowly Developer' }
  494. ];
  495. $scope.columnDefs = [
  496. {name: 'name', enableCellEdit: true, validators: {minLength: 3, maxLength: 9}, cellTemplate: 'ui-grid/cellTitleValidator'},
  497. {name: 'title', enableCellEdit: true, validators: {required: true}, cellTemplate: 'ui-grid/cellTitleValidator'}
  498. ];
  499. }]);
  500. </file>
  501. <file name="index.html">
  502. <div ng-controller="MainCtrl">
  503. <div ui-grid="{ data: data, columnDefs: columnDefs }" ui-grid-edit ui-grid-validate></div>
  504. </div>
  505. </file>
  506. </example>
  507. */
  508. module.directive('uiGridValidate', ['gridUtil', 'uiGridValidateService', function (gridUtil, uiGridValidateService) {
  509. return {
  510. priority: 0,
  511. replace: true,
  512. require: '^uiGrid',
  513. scope: false,
  514. compile: function () {
  515. return {
  516. pre: function ($scope, $elm, $attrs, uiGridCtrl) {
  517. uiGridValidateService.initializeGrid($scope, uiGridCtrl.grid);
  518. },
  519. post: function ($scope, $elm, $attrs, uiGridCtrl) {
  520. }
  521. };
  522. }
  523. };
  524. }]);
  525. })();