├── .codeclimate.yml
├── .gitignore
├── .jshintrc
├── .travis.yml
├── API.md
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Gruntfile.js
├── Q&A.md
├── README.md
├── bower.json
├── bundle.js
├── config
├── karma.conf.angular.1.3.js
└── karma.conf.angular.1.4.js
├── demo
├── demo.js
├── iconmonstr-github-10-icon-128.png
└── main.css
├── dist
├── angular-validation-rule.js
├── angular-validation-rule.min.js
├── angular-validation.js
├── angular-validation.min.js
└── bundle.js
├── index.html
├── main.js
├── package.js
├── package.json
├── src
├── module.js
├── provider.js
├── reset.directive.js
├── rule.js
├── submit.directive.js
└── validator.directive.js
├── test
├── lib
│ ├── angular-mocks.1.3.18.js
│ ├── angular-mocks.1.4.7.js
│ ├── angular.1.3.18.js
│ └── angular.1.4.7.js
└── unit
│ ├── actualValueNoUseViewValue.js
│ ├── actualValueUseViewValue.js
│ ├── callbackSpec.js
│ ├── compileSpec.js
│ ├── directivesSpec.js
│ ├── providerSpec.js
│ ├── setgetValidMethodSpec.js
│ ├── showMessageSpec.js
│ ├── validationGroupSpec.js
│ ├── validationResetSpec.js
│ └── validationSubmitSpec.js
└── webpack.config.js
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | languages:
2 | JavaScript: true
3 | exclude_paths:
4 | - "config/*"
5 | - "demo/*"
6 | - "dist/*"
7 | - "test/*"
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # --------------------
2 | # OS Files
3 | # --------------------
4 | .DS_Store
5 | .AppleDouble
6 | .LSOverride
7 | Icon
8 | ._*
9 | .Spotlight-V100
10 | .Trashes
11 |
12 | # --------------------
13 | # Node Files
14 | # --------------------
15 | npm-debug.log
16 | node_modules/
17 | bower_components/
18 |
19 | # --------------------
20 | # Editor Files
21 | # --------------------
22 | .idea/
23 | *.swp
24 | *.vim
25 |
26 | # --------------------
27 | # coverage
28 | # --------------------
29 | coverage
30 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "quotmark": "single",
3 | "node": true,
4 | "globals": {
5 | "angular": true,
6 | "document": false,
7 | "jasmine": false,
8 | "module": false,
9 | "describe": false,
10 | "beforeEach": false,
11 | "afterEach": false,
12 | "it": false,
13 | "inject": false,
14 | "expect": false,
15 | "spyOn": false
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "0.10"
4 | - "0.11"
5 | - "0.12"
6 | - "1.0.0"
7 | - "2.0.0"
8 | - "3.0.0"
9 | - "4.0.0"
10 | - "5.0.0"
11 | - "7.0.0"
12 | - "8.0.0"
13 | before_script:
14 | - export DISPLAY=:99.0
15 | - sh -e /etc/init.d/xvfb start
16 | - npm install -g grunt-cli
17 |
18 | matrix:
19 | fast_finish: true
20 | # allow_failures:
21 | after_success:
22 | - grunt cov
23 |
--------------------------------------------------------------------------------
/API.md:
--------------------------------------------------------------------------------
1 | API
2 | ===
3 | ### **Add Valid Message (error, success) for validation `required`**
4 | `required-error-message` and `required-success-message`
5 |
6 | ```html
7 | Required
8 |
14 |
17 | ```
18 |
19 | ### **Add Valid Message (error, success) for validation `email`**
20 | `email-error-message` and `email-success-message`
21 |
22 | ```html
23 | Email
24 |
30 | ```
31 |
32 | ### **Use Default Valid Message**
33 | *You don't need to give valid message* - the valid/invalid message will be automatically placed next to your input element.
34 |
35 | ```html
36 | Number
37 |
38 | ```
39 |
40 | ### **Use Custom Default Valid/Invalid Message**
41 | You can customize the default valid/invalid message that will be automatically placed next to your input element.
42 |
43 | ```html
44 |
58 |
59 | Username
60 |
61 | ```
62 |
63 | ### **Use a custom Valid Message**
64 | You can also add a custom validation message by using `message-id` attribute. It allows you to place a valid/invalid message wherever you want, a target element must specify an `id` attribute that matches with a value of the `message-id`.
65 |
66 | ```html
67 | Number
68 |
69 |
70 | ```
71 |
72 | ### **Use a validation group**
73 | You can also add a `validation-group` directive to group many elements into a group. The group will be considered as valid if and only if one of them is valid. Otherwise, the group will be marked as invalid. A valid/invalid message will be placed inside an element that contains an id attribute with the same name as provided to the directive `validation-group`.
74 |
75 | ```html
76 | Validation group
77 |
78 |
79 |
80 |
81 |
82 | ```
83 |
84 | > Note that the `validation-group` directive can only be used to group elements placed in the same form. In other words, you can't group elements across different `
121 |
122 | Submit Only method
123 |
127 |
128 |
139 |
140 |
142 |
143 |
147 |
148 |
156 |
157 |
158 | ```
159 |
160 | ### **Select a global validation method** `watch blur submit submit-only`**
161 |
162 | `validationProvider.setValidMethod('submit')`
163 |
164 | ### **Setup a new Validation `setExpression()` `setDefaultMsg()` with `RegExp` or `Function` in config phase**
165 |
166 |
167 | ```html
168 |
169 | IP address (Custom setup the new validator - RegExp)
170 |
171 |
172 | Huei (Custom setup the new validator - Function)
173 |
174 |
175 | Kuaisheng (Custom setup the new validator - Function)
176 |
177 | ```
178 |
179 | ```javascript
180 | // your module
181 | angular.module('yourApp', ['validation'])
182 | .config(['$validationProvider', function ($validationProvider) {
183 | // Setup `ip` validation
184 | var expression = {
185 | ip: /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
186 | };
187 |
188 | var validMsg = {
189 | ip: {
190 | error: 'This isn\'t ip address',
191 | success: 'It\'s ip'
192 | }
193 | };
194 |
195 | $validationProvider.setExpression(expression) // set expression
196 | .setDefaultMsg(validMsg); // set valid message
197 |
198 | // Setup `huei` validation
199 | $validationProvider
200 | .setExpression({
201 | huei: function (value, scope, element, attrs) {
202 | return value === 'Huei Tan';
203 | // or you can do
204 | return $q.all([obj]).then(function () {
205 | // ...
206 | return true;
207 | })
208 | }
209 | })
210 | .setDefaultMsg({
211 | huei: {
212 | error: 'This should be Huei Tan',
213 | success: 'Thanks!'
214 | }
215 | });
216 |
217 | // Setup `kuaisheng` validation
218 | $validationProvider
219 | .setExpression({
220 | kuaisheng: function(value, scope, element, attrs, param) {
221 | var errorStr = [
222 | 'errorStr1',
223 | 'errorStr2'
224 | ];
225 | var len = errorStr.length;
226 | for (var i = len - 1; i >= 0; i--) {
227 | if (value.indexOf(errorStr[i]) > -1) {
228 | return {
229 | result: false,
230 | message: 'input should not include ' + errorStr[i]
231 | };
232 | }
233 | }
234 | return {
235 | result: true,
236 | message: ''
237 | };
238 | }
239 | })
240 | .setDefaultMsg({
241 | kuaisheng: {
242 | error: 'valid is error',
243 | success: 'Thanks!'
244 | }
245 | });
246 |
247 | }]);
248 | ```
249 |
250 | ### **Check form whether valid, return `true` if valid. `checkValid()`**
251 | ### **Reset the specific Form. `reset()`**
252 |
253 | ```html
254 |
265 | ```
266 |
267 | ```javascript
268 | // ... checkValid
269 | $scope.form.checkValid = validationProvider.checkValid;
270 | // ... reset
271 | $scope.form.reset = function (form) {
272 | validationProvider.reset(form);
273 | };
274 | // ... angular validation 1.2.0 reset
275 | $scope.form.reset = function () {
276 | // Don't need include validationProvider.reset();
277 | // Focus on your ng-click action
278 | };
279 | ```
280 |
281 | ### **Set the valid/invalid message style CSS**
282 |
283 | ```html
284 | Your valid message here
285 | Your invalid message here
286 | ```
287 |
288 | ```css
289 | .validation-valid {
290 |
291 | }
292 |
293 | .validation-invalid {
294 |
295 | }
296 | ```
297 |
298 | ### **Custom the valid/invalid message style HTML in `.config()`,**
299 | `setErrorHTML(func)` `setSuccessHTML(func)`, input should be a `function` and given `parameter` which is the valid/invalid message declared
300 | in `getDefaultMsg()`,and finally return the HTML code
301 |
302 | ```javascript
303 | // your angular
304 | .config(['$validationProvider', function ($validationProvider) {
305 | $validationProvider.setErrorHTML(function (msg, element, attrs) {
306 | // remember to return your HTML
307 | // eg: return '' + msg + '
';
308 | // or using filter
309 | // eg: return '{{"' + msg + '"| lowercase}}
';
310 | });
311 |
312 | $validationProvider.setSuccessHTML(function (msg, element, attrs) {
313 | // eg: return '' + msg + '
';
314 | // or using filter
315 | // eg: return '{{"' + msg + '"| lowercase}}
';
316 | });
317 | }]);
318 | ```
319 |
320 | ### **disable/enable show success/error message**
321 | `default: true`
322 | Easily disable success/error message
323 |
324 | ```javascript
325 | // your angular
326 | .config(['$validationProvider', function ($validationProvider) {
327 | $validationProvider.showSuccessMessage = false; // or true(default)
328 | $validationProvider.showErrorMessage = false; // or true(default)
329 | }]);
330 | ```
331 |
332 | ### **Multiple validators**
333 | Use commas to separate multiple validators.
334 |
335 | ```html
336 |
337 | ```
338 |
339 | ### **Validator parameters**
340 | The built in `maxlength` and `minlength` validators use parameters to configure the limits. For example:
341 |
342 | ```html
343 |
344 | ```
345 |
346 | You can use parameters in your custom validators in the same way.
347 | You can access this parameter in the validation expression like so:
348 |
349 | ```html
350 |
351 | ```
352 |
353 | ```javascript
354 | // your module
355 | angular.module('yourApp', ['validation'])
356 | .config(['$validationProvider', function ($validationProvider) {
357 | // Setup `isstring` validation
358 | $validationProvider
359 | .setExpression({
360 | isstring: function (value, scope, element, attrs, param) {
361 | return value === param;
362 | }
363 | })
364 | .setDefaultMsg({
365 | isstring: {
366 | error: 'This is not what we wanted!',
367 | success: 'Thanks!'
368 | }
369 | });
370 | }]);
371 | ```
372 |
373 | ### **Customizable Initial Validation**
374 | Intial Validation for the input false. You can make it to true!
375 |
376 | ```html
377 |
378 |
379 |
380 |
381 |
382 | ```
383 |
384 | ### Custom Error/Success Message Function
385 | #### html
386 | Declare your valid and invalid callback functions. Make sure to pass the `message` param.
387 | ``` html
388 |
389 | ```
390 | #### Javascript
391 | Now you can call your own function and have access to the message.
392 | ``` javascript
393 | scope.validationValidHandler = function(message){
394 | // you now have access to the error message
395 | displayMessage(message, 'success');
396 | };
397 |
398 | scope.validationInvalidHandler = function(message){
399 | // you now have access to the error message
400 | displayMessage(message, 'error');
401 | };
402 | ```
403 |
404 |
405 | ### **Setup a global valid/invalid/reset callback in config phase**
406 |
407 | ```javascript
408 | // your module
409 | angular.module('yourApp', ['validation'])
410 | .config(['$validationProvider', function ($validationProvider) {
411 | $validationProvider.validCallback = function(element) {
412 | element.parents('.validator-container:first').removeClass('has-error').addClass('has-success-tick');
413 | };
414 | $validationProvider.invalidCallback = function(element) {
415 | element.parents('.validator-container:first').removeClass('has-success-tick').addClass('has-error');
416 | };
417 | $validationProvider.resetCallback = function(element) {
418 | element.parents('.validator-container:first').removeClass('has-error');
419 | };
420 | }]);
421 | ```
422 |
423 |
424 | ### **Setup Message Element in config phase**
425 | `WHY`
426 | ````html
427 |
428 |
429 |
430 |
431 |
432 |
433 | ````
434 |
435 | `HOW`
436 |
437 | In this case, I can use `message-id` attribute as a "get a job done" solution.
438 | Because, If you choose this solution It will increase your effort of manually putting `message-id` and define your own Message Element.
439 |
440 | You can use `addMsgElement` as a better solution to centralize & automate your configurations.
441 |
442 | ```javascript
443 | // your module
444 | angular.module('yourApp', ['validation'])
445 | .config(['$validationProvider', function ($validationProvider) {
446 | /**
447 | * Add your Msg Element
448 | * @param {DOMElement} element - Your input element
449 | * @return void
450 | */
451 | $validationProvider.addMsgElement = function(element) {
452 | // Insert my own Msg Element
453 | $(element).parent().append(' ');
454 | };
455 |
456 | /**
457 | * Function to help validator get your Msg Element
458 | * @param {DOMElement} element - Your input element
459 | * @return {DOMElement}
460 | */
461 | $validationProvider.getMsgElement = function(element) {
462 | return $(element).parent().children().last();
463 | };
464 |
465 | }]);
466 | ```
467 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | 26 Oct 2020 v1.4.5
2 | ===
3 |
4 | _As of today, this project is no longer maintained._
5 |
6 | 31 May 2017 v1.4.4
7 | ===
8 |
9 | **Fix**
10 |
11 | [402752c](https://github.com/hueitan/angular-validation/commit/402752cebfe1250e8a438ffefe9ece17554a215d) Executes $apply() only if doesn't have another in execution (blur)
12 |
13 | **Improvement**
14 |
15 | [#198](https://github.com/hueitan/angular-validation/commit/fa1ec0dbe0628749990a09fb325d6517f10e16a4) Added support for dynamic default messages using functions.
16 |
17 | 24 Jan 2017 v1.4.3
18 | ===
19 |
20 | **Fix**
21 |
22 | [#257](https://github.com/hueitan/angular-validation/commit/0640de71ab045037b0fba86fd0a278e748d67786) Fix for initialValidity set up
23 |
24 | **Improvement**
25 |
26 | [#231](https://github.com/hueitan/angular-validation/pull/231) improve valid result, you can set success or error message when valid
27 | [#266](https://github.com/hueitan/angular-validation/pull/266) Observe validator changes
28 | [#271](https://github.com/hueitan/angular-validation/pull/271) Using model value
29 |
30 | 22 Jul 2016 v1.4.2
31 | ===
32 |
33 | **Fix**
34 |
35 | [#206](https://github.com/hueitan/angular-validation/pull/206) Fixed calls for validCallback/invalidCallback
36 | [#215](https://github.com/hueitan/angular-validation/pull/215) Fix validation-group not working properly
37 |
38 | **Improvement**
39 |
40 | [#223](https://github.com/hueitan/angular-validation/pull/223) Track originalViewValue
41 |
42 | **Documentation**
43 |
44 | [#204](https://github.com/hueitan/angular-validation/pull/204) Webpack integration + design improvements
45 |
46 | 27 Jan 2016 v1.4.1
47 | ===
48 |
49 | **Improvement**
50 |
51 | [#137](https://github.com/hueitan/angular-validation/commit/3b18df1cda799d4135947ba05169c149d128b28f) Support Angular 1.4.x version
52 | [#180](https://github.com/hueitan/angular-validation/pull/180) Add code coverage using coveralls and write more tests
53 | [#184](https://github.com/hueitan/angular-validation/pull/184) Support compiling validation output message
54 |
55 | **New Featuers**
56 | [#175](https://github.com/hueitan/angular-validation/pull/175) Add `validation-group` directive to support group validation, checkboxes, radios, text fields, and etc
57 | [#183](https://github.com/hueitan/angular-validation/pull/183) Support custom message element and update parameter for setErrorHTML() and setSuccessHTML()
58 |
59 | 17 Dec 2015 v1.4.0 (Support AngularJS 1.4)
60 | ===
61 |
62 | :celebration: :party:
63 |
64 | 17 Dec 2015 v1.3.4 (Moving to v1.4)
65 | ===
66 |
67 | **Improvement**
68 |
69 | [#113](https://github.com/hueitan/angular-validation/pull/162) removing directive scope dependency
70 | [#168](https://github.com/hueitan/angular-validation/pull/168) Added a global valid method
71 |
72 | **Fix**
73 |
74 | [0946ed4](https://github.com/hueitan/angular-validation/commit/0946ed4ab22611aaf68b625ac9028a9767f012f7) FIX provider.setSuccess/ErrorMessage & no-validation-message
75 |
76 | **Documentation**
77 |
78 | [#164](https://github.com/hueitan/angular-validation/issues/164) Improve the HTML documentation
79 |
80 | 31 Oct 2015 v1.3.3
81 | ===
82 |
83 | **Improvement**
84 |
85 | [00ce51c](https://github.com/hueitan/angular-validation/commit/00ce51cb85c21a7699c7faecc4a173e0834daadb) Updated `travis.yml`
86 | [6f9403d](https://github.com/hueitan/angular-validation/pull/143) Added the `package.js` in order to support Meteor package
87 | [a872d4c](https://github.com/hueitan/angular-validation/pull/147) Did refactoring the project
88 |
89 | **Fix**
90 |
91 | [331d2eb](https://github.com/hueitan/angular-validation/commit/331d2eb2fd68d11532fe612453861c263577fb58) Wrapped `validation-reset` within $timeout
92 | [e53469c](https://github.com/hueitan/angular-validation/commit/e53469c08a7e6e4ef7c8a60c0e67683bd7dd68ad) Fixed `no-validation-message` condition
93 | [4dcdb68](https://github.com/hueitan/angular-validation/pull/140) Set an actual value from the control's view (ctrl.$viewValue instead of watch value)
94 | [09aed2e](https://github.com/hueitan/angular-validation/pull/145) Fixed an error in the checkValid function
95 |
96 |
97 | **Documentation**
98 |
99 | [eeac2f0](https://github.com/hueitan/angular-validation/commit/eeac2f02552621f743899dc2d1bd61194f1977d4) Updated API.md to include an `ng-model-options` example
100 |
101 |
102 | 10 Aug 2015 v1.3.2
103 | ===
104 |
105 | **Fix**
106 |
107 | [fca3399](https://github.com/hueitan/angular-validation/pull/117) Trouble focusing on first invalid element on submit
108 |
109 | [ef425a9](https://github.com/hueitan/angular-validation/pull/122) Adding message to invalid and valid callback functions.
110 |
111 | **Known Issue**
112 |
113 | `$observe` https://github.com/hueitan/angular-validation/pull/132
114 |
115 | 11 May 2015 v1.3.1
116 | ===
117 |
118 | Welcome [angular-validation-schema](https://github.com/thetutlage/angular-validation-schema)
119 | Add GA for the demo page
120 |
121 | **Improvement**
122 |
123 | [6b22fe2](https://github.com/hueitan/angular-validation/commit/6b22fe2065e91cd051ecae973facadd115504c52) Documentation for bootstrap form group
124 | [dffdc32](https://github.com/hueitan/angular-validation/commit/dffdc3262e8631bc586bf7c9c030864d8b77fbf0) Allow passing of a parameter to validation rule and added min and max length rules
125 |
126 | **Fix**
127 |
128 | [09f9731](https://github.com/hueitan/angular-validation/commit/09f9731170a63704d0befdbbdd635e700af35433) second validator fires but does not wait
129 |
130 | **New Featuers**
131 |
132 | [1b9d527](https://github.com/hueitan/angular-validation/commit/1b9d52713c32d1b73c90174258ea9b0cc781489a) toggle has-error on parent form-group with $validationProvider valid/invalid callbacks
133 |
134 | 02 Feb 2015 v1.3.0
135 | ===
136 | **Important**
137 |
138 | This version support angularjs 1.3
139 |
140 | **Improvement**
141 |
142 | [b6a70b2](https://github.com/hueitan/angular-validation/commit/a6ac1400a8a637bc2296a1910c3456797b6a70b2) Use ctrl.$viewValue instead of element[0].value
143 | [0e1925e](https://github.com/hueitan/angular-validation/commit/a30e6edaffe15c64cac711a7290236b550e1925e) Default initial Validity is undefined
144 | [8c7bfb1](https://github.com/hueitan/angular-validation/commit/d505d45a93984e07f0a466d3e385e15ac8c7bfb1) Prevents span from being displayed for valid input without messages
145 | [509e2a0](https://github.com/hueitan/angular-validation/commit/8b1b46f8e37e5eb435de4a0842cb036e5509e2a0) Allows global disabling of error messages in case someone wants that
146 |
147 | **New Features**
148 |
149 | [717c98b](https://github.com/hueitan/angular-validation/commit/8a86b03ed8a792d91a6c1c3a50a9f1a64717c98b) Add a MessageId atrribute for customizing valid/invalid message position
150 |
151 | **Documentation**
152 |
153 | [b638dc6](https://github.com/hueitan/angular-validation/commit/744edde0326edc3ba4e2308062912e342b638dc6) Update readme with bower installation steps
154 | [10318f8](https://github.com/hueitan/angular-validation/commit/63d484979d157f3b8a0389f8220d6c91c10318f8) Update API.md for anchor link
155 |
156 | **Demo**
157 |
158 | [97c1e14](https://github.com/hueitan/angular-validation/commit/ff1aee1d4874fb247b2562a7913a9f14497c1e14) Add demo for select form
159 | [71f6e0b](https://github.com/hueitan/angular-validation/commit/64b8eee0e98600ab1d3945dbe78dd651e71f6e0b) Add ng-repeat input box example
160 |
161 | Thanks @lvarayut @eyupatis @U-US\katerbm @dmitry-dedukhin for the contributions
162 |
163 | 14 Nov 2014 v1.2.6
164 | ===
165 | **Important**
166 |
167 | This version support to angularjs version 1.2.x
168 | To support angularjs version 1.3.x, please use angular-validation 1.3.x
169 |
170 | **Fix**
171 |
172 | [bcaab7e](https://github.com/hueitan/angular-validation/commit/bcaab7e31f2e00e92e2d8e8397935c96c37d16b3) Custom message on html not displaying with multiple validator
173 |
174 | 24 Aug 2014 v1.2.5
175 | ===
176 | **New Features**
177 |
178 | [911b791](https://github.com/hueitan/angular-validation/commit/911b7917a4b1b302982a0f3b42c54da3bd28ee28) Support for multiple validators on a single input
179 |
180 | **Tools**
181 |
182 | [c941b0b](https://github.com/hueitan/angular-validation/commit/c941b0bd4c8c350cf1cfa910a1eabff184280031) Add jshint and jsbeautifier
183 |
184 | 1 Aug 2014 v1.2.2
185 | ===
186 | **Fix**
187 |
188 | [a96c499](https://github.com/hueitan/angular-validation/commit/a96c499967e0c0512a536545ea102ebc6283e1c6) Fix validationSubmit directive to avoid non interpretation of others angular treatments
189 | [0474c8a](https://github.com/hueitan/angular-validation/commit/0474c8aeb51dbb558fca88191cbba7e32423515b) Fixed customizable initial validation.
190 |
191 | **Demo**
192 |
193 | [5f06982](https://github.com/hueitan/angular-validation/commit/5f069820d2520a2bfc4e52906d446dac5edf2d97) Fix rule required
194 |
195 | **Documentation**
196 |
197 | [17e906a](https://github.com/hueitan/angular-validation/commit/17e906a6050bf8e62f6ad304eec65042dddfcced) Update readme file and add Q&A API page
198 |
199 | 10 July 2014 v1.2.1
200 | ===
201 | **New features**
202 |
203 | [6a682b0](https://github.com/hueitan/angular-validation/commit/6a682b0ac0928f8a426dc7ba0cf82de622c33cda) Add rule.js => angular-validation-rule.js
204 | [343f578](https://github.com/hueitan/angular-validation/commit/343f578f7aab2a56ecf280635a5d668f4ebc1966) Show error message when the form is undefined
205 | [78e15a3](https://github.com/hueitan/angular-validation/commit/78e15a34e0180a45bdd73dcb0677a4fbc796fd9f) Allow validate/reset multiple scope model
206 | [e16c592](https://github.com/hueitan/angular-validation/commit/e16c592cd7d31f7537f4c960505fe52d060a13d3) Allow validate/reset only one scope model
207 |
208 | **Documentation**
209 |
210 | [172fd8c](https://github.com/hueitan/angular-validation/commit/172fd8ccf9113fceac18e982af30fdf0da8592e2) Added bootstrap integration to readme. Moved contributing info to CON…
211 |
212 | 15 Jun 2014 v1.2.0
213 | ===
214 | **New features**
215 |
216 | [b1f875e](https://github.com/hueitan/angular-validation/commit/b1f875e5afebae6a5f28804e6f7996fc2d1f268a) Do more in setExpression(...)
217 | [b1d4a54](https://github.com/hueitan/angular-validation/commit/b1d4a54ef51f9a13a129787e8087f1610e18b28a) New Features validation-submit & validation-reset
218 |
219 | **Fix**
220 |
221 | [73ff4b0](https://github.com/hueitan/angular-validation/commit/73ff4b05d598f38d6f26ed433807360cdb518777) checkValid using timeout inside submit
222 | [af5ea19](https://github.com/hueitan/angular-validation/commit/af5ea19cce9d0dcd25d542e4103cb7b719f9e931) unique field event
223 |
224 | **Documentation**
225 |
226 | [9a6fd48](https://github.com/hueitan/angular-validation/commit/9a6fd48483ecc813303ddd21fc60d5aaf76823d6) When using checkValid()
227 |
228 | 14 May 2014 v1.1.2
229 | ===
230 | **Fix**
231 |
232 | [964c577](https://github.com/hueitan/angular-validation/commit/964c5779634f70aa1013c5c415beb9b6041678e6) Fix callback reference issue
233 | [ea4fc94](https://github.com/hueitan/angular-validation/commit/ea4fc9413546442dfda73df1d76b1a8e6a9ccd98) Add scope apply in blur callback
234 |
235 | **Tools**
236 |
237 | [41215a9](https://github.com/hueitan/angular-validation/commit/41215a9a1cbedd82e1db31cc116243910f5af4a4) Replace connect as BrowserSync
238 |
239 | **test**
240 |
241 | [6ff77c8](https://github.com/hueitan/angular-validation/commit/6ff77c8dc9d8de18652714dbb057803d8fcecbe1) Allow test for node 0.11
242 |
243 | 19 Apr 2014 v1.1.1
244 | ===
245 | **New features**
246 |
247 | [794291d](https://github.com/hueitan/angular-validation/commit/794291d782b8ba8c4888d9610364d72125402408) Focus first input element when submit error [#11](https://github.com/hueitan/angular-validation/issues/11)
248 |
249 | **Fix**
250 |
251 | [a08264c](https://github.com/hueitan/angular-validation/commit/a08264c91bfd2a6bde676d9cba0a4ce3c96a5099) Fix given value '' while reset phase
252 | [8e594cf](https://github.com/hueitan/angular-validation/commit/8e594cf33d573898346a468cfcbf8f9bf7e8d5ea) Fix scope.$on('submit') order
253 |
254 | **Documentation**
255 |
256 | [46937cc](https://github.com/hueitan/angular-validation/commit/46937cc625df9309c5bacb3e3195c151289cca78) Update Documentation, setup validation in config state
257 |
258 | **lib**
259 |
260 | upgrade angular.1.2.16
261 | upgrade angular.1.3.0-beta.5
262 |
263 | 13 Apr 2014 v1.1.0
264 | ===
265 | **New features**
266 |
267 | [09894d8](https://github.com/hueitan/angular-validation/commit/09894d8bc6a3379cc2741e456d7809a040d1ca49) Allow disable showing success message [#6](https://github.com/hueitan/angular-validation/issues/6)
268 | [ad0f55d](https://github.com/hueitan/angular-validation/commit/ad0f55d69629fbec4e550277c9f5e911626c623b) Differentiate submit submit-only method [#4](https://github.com/hueitan/angular-validation/issues/4)
269 |
270 | **Fix**
271 |
272 | [0a96acb](https://github.com/hueitan/angular-validation/commit/0a96acb59f023a101612565f3e6c9173929dcf96) Fix validate.reset method
273 |
274 | 9 Apr 2014 v1.0.6
275 | ===
276 | Include bower.json
277 |
278 | 2 Mar 2014 v1.0.5
279 | ===
280 | **Update Features**
281 |
282 | [51f0019](https://github.com/hueitan/angular-validation/commit/51f0019477a1ec459edfea4b2966bd6a16b2a348) provider `validate` `reset` doesn't need parameter `scope` anymore
283 |
284 | 27 Dec 2013 v1.0.0 FIRST RELEASE
285 | ===
286 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | CONTRIBUTING
2 | =====
3 |
4 | **Clone the repo to your computer**
5 | ```sh
6 | git clone https://github.com/hueitan/angular-validation.git
7 | ```
8 |
9 | **Install Grunt and Download dependencies**
10 | ```sh
11 | npm install -g grunt-cli
12 | npm install
13 | ```
14 |
15 | **Before coding** Boot the Environment
16 | ```sh
17 | grunt dev # developing environment
18 | grunt check # check the code quality
19 | grunt build # build files
20 | ```
21 |
22 | **Test**
23 | ```sh
24 | # Test it locally
25 | npm test
26 | ```
27 |
28 | Karma Test done by Travis-ci
29 |
30 | Adding a new validation
31 | =====
32 |
33 | **Start coding**
34 | ```
35 | open `rule.js`, looking for `var expression` and `var defaultMsg`
36 | Adding a new expression and defaultMsg to extend it
37 | ```
38 |
39 | **IP validation** As the example
40 | ```javascript
41 | // provider.js
42 | var expression = {
43 | required: /.+/gi,
44 | ... // add new expression below
45 | ip: /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
46 | };
47 |
48 | var defaultMsg = {
49 | required: {
50 | error: 'This should be Required!!',
51 | success: 'It\'s Required'
52 | },
53 | ... // add new valid message below
54 | ip: {
55 | error: 'This isn\'t ip',
56 | success: 'It\'s IP'
57 | }
58 | };
59 | ```
60 |
61 | When you are done, test it on `http://localhost:8080`
62 |
63 |
64 |
65 | **Give me a PR** Thanks for it
66 |
67 | **Who does the exactly same job ?**
68 |
69 | 1. https://github.com/kelp404/angular-validator
70 | 2. https://github.com/nelsonomuto/angular-ui-form-validation
71 |
72 | **Note**
73 |
74 | 1. More Status
75 | https://github.com/angular/angular.js/issues/583#issuecomment-31277556
76 | 2. ngForm module ideas
77 | https://docs.google.com/document/d/192dCUnoIBQ7-vurvPeS9BElGdxfk0Ddcof2KzDDi1Mc/edit
78 | 3. form-angular
79 | http://www.forms-angular.org/
80 | 4. Html5 form-validation
81 | http://www.sitepoint.com/client-side-form-validation-html5/
82 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 | require('time-grunt')(grunt);
3 |
4 | // Grunt Config
5 | grunt.initConfig({
6 | pkg: grunt.file.readJSON('package.json'),
7 | clean: {
8 | dist: {
9 | files: [{
10 | dot: true,
11 | src: [
12 | 'dist'
13 | ]
14 | }]
15 | }
16 | },
17 | copy: {
18 | main: {
19 | files: [{
20 | src: './bundle.js',
21 | dest: 'dist/'
22 | }
23 | // {expand: true, cwd: 'demo', src: ['script.js'], dest: 'dist'} // partials html file
24 | // {expand: true, cwd: 'app', src: ['*.*'], dest: 'dist/'}
25 | ]
26 | }
27 | },
28 | concat: {
29 | basic_and_extras: {
30 | files: {
31 | 'dist/angular-validation.js': ['src/module.js', 'src/provider.js', 'src/*.directive.js'],
32 | 'dist/angular-validation-rule.js': ['src/rule.js']
33 | }
34 | }
35 | },
36 | uglify: {
37 | my_target: {
38 | files: {
39 | 'dist/angular-validation.min.js': ['dist/angular-validation.js'],
40 | 'dist/angular-validation-rule.min.js': ['dist/angular-validation-rule.js']
41 | }
42 | }
43 | },
44 | jsbeautifier: {
45 | files: ['src/**/*.js', 'test/unit/*.js', 'demo/*.js'],
46 | options: {
47 | js: {
48 | indent_size: 2
49 | }
50 | }
51 | },
52 | jshint: {
53 | all: ['*.js', 'src/**/*.js', 'test/unit/*.js', 'demo/*.js'],
54 | options: {
55 | jshintrc: true
56 | }
57 | },
58 | browserSync: {
59 | dev: {
60 | bsFiles: {
61 | src: ['index.html', 'demo/**', 'dist/angular-validation.js']
62 | },
63 | options: {
64 | host: 'localhost',
65 | ports: {
66 | min: 8000,
67 | max: 8100
68 | },
69 | server: {
70 | baseDir: '.'
71 | },
72 | watchTask: true
73 | }
74 | }
75 | },
76 | watch: {
77 | files: ['src/*.js'],
78 | tasks: ['build'],
79 | options: {
80 | spawn: false,
81 | interrupt: true
82 | }
83 | },
84 | karma: {
85 | // angular 1.2.x support to version angular-validation 1.2.x
86 | // angular1_2: {
87 | // configFile: 'config/karma.conf.angular.1.2.js'
88 | // }
89 | angular1_3: {
90 | configFile: 'config/karma.conf.angular.1.3.js'
91 | },
92 | angular1_4: {
93 | configFile: 'config/karma.conf.angular.1.4.js'
94 | }
95 | },
96 | coveralls: {
97 | options: {
98 | debug: true,
99 | coverageDir: 'coverage',
100 | dryRun: false,
101 | force: true,
102 | recursive: true
103 | }
104 | }
105 | });
106 |
107 | require('load-grunt-tasks')(grunt);
108 |
109 | // Register Task
110 | grunt.registerTask('dev', ['browserSync', 'watch']);
111 | grunt.registerTask('check', ['jshint', 'jsbeautifier']); // use this before commit
112 | grunt.registerTask('build', ['check', 'clean', 'concat', 'uglify', 'copy']);
113 | grunt.registerTask('test', ['build', 'karma']);
114 | grunt.registerTask('cov', ['coveralls']);
115 | grunt.registerTask('default', ['build', 'test']);
116 | };
117 |
--------------------------------------------------------------------------------
/Q&A.md:
--------------------------------------------------------------------------------
1 | Q & A
2 | =====
3 | ### Can I validate multiple Checkbox?
4 |
5 | Yes, you can do it by using `validation-group`
6 |
7 | ```html
8 |
22 |
23 | ```
24 |
25 | ### Can I validate the form when init ? [#10](https://github.com/hueitan/angular-validation/issues/10)###
26 |
27 | ```html
28 |
32 | ```
33 | ```javascript
34 | $timeout(function () { // call $timeout to make sure the Form Constructor is generated
35 | $validationProvider.validate($scope.Form); // $scope.Form is html form name `Form Constructor`
36 | });
37 | ```
38 |
39 | ### What's the differentiate between validator-method `submit` and `submit-only`[#4](https://github.com/hueitan/angular-validation/issues/4)###
40 |
41 | `submit` : when user click submit, then start watching using `watch` to validate
42 | `submit-only` : when user click `submit`, doesn't validate through `watch` until `submit` button is clicked.
43 |
44 | ### Use checkValid() manually [#19](https://github.com/hueitan/angular-validation/issues/19)###
45 |
46 | Before using `checkValid()`, you have to execute `submit` first to get the latest result.
47 |
48 | ### How do we do tooltips for error messages upon focusing? [#68](https://github.com/hueitan/angular-validation/issues/68#issuecomment-86445467)
49 |
50 | Using `validCallback` and `invalidCallback` to implement
51 |
52 | ### Can this works correctly with AngularUI, ui-select, others ... ?###
53 |
54 | Yes, `angular-validation` works perfectly with other directive. (isolation scope). Find out more from the demo page.
55 |
56 | ### Working with `ng-submit` and submitting with enter, not click [#247](https://github.com/hueitan/angular-validation/issues/247)###
57 |
58 | As per plnkr - https://plnkr.co/edit/nwTEuxuTMmpEc4hrFwGp?p=preview
59 |
60 | Add checkValid and submit into both ng-click and ng-submit
61 |
62 | ```
63 |
67 | ```
68 |
69 | ### Can I validate $modelValue instead of a $viewValue? [#272](https://github.com/hueitan/angular-validation/pull/272)###
70 |
71 | Yes, adding `use-view-value="false"` attribute forces to use $modelValue instead of a $viewValue for evaluation when form is submitted. By default $viewValue is used. This need raises from a need of localized number inputs, which have to be stored in a $viewValue as a string (e.g. "2 000,0"), however in a $modelValue they are stored as a properly formatted number (2000). This can be done e.g. by using a custom directive with properly specified $formatters and $parsers.
72 |
73 | ```html
74 |
75 |
76 |
83 |
84 |
85 | ```
86 |
87 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | angular-validation 1.4.5
2 | =========================
3 | [](http://badge.fury.io/js/angular-validation)
4 | [](https://travis-ci.org/hueitan/angular-validation)
5 | [](https://codeclimate.com/github/hueitan/angular-validation)
6 | [](https://coveralls.io/github/hueitan/angular-validation?branch=master)
7 | [](https://david-dm.org/hueitan/angular-validation#info=devDependencies)
8 |
9 | Client-side Validation should be simple and clean.
10 | Don't let Client-side Validation dirty your controller.
11 |
12 | Setup your Validation on config phase by using some rules [(example)](https://github.com/hueitan/angular-validation/blob/master/dist/angular-validation-rule.js)
13 | If you prefer schema over html attributes , try [angular-validation-schema
14 | ](https://github.com/thetutlage/angular-validation-schema) [(Demo)](http://plnkr.co/edit/X56HEsDYgYoY8gbSj7cu?p=preview)
15 | And add Validation in your view only.
16 |
17 | angularjs 1.2.x support to version [angular-validation 1.2.x](https://github.com/hueitan/angular-validation/tree/v1.2.x)
18 | angularjs 1.3.x support after version [angular-validation 1.3.x](https://github.com/hueitan/angular-validation/tree/v1.3.x)
19 | angularjs 1.4.x support after version angular-validation 1.4.x
20 |
21 | Requirement
22 | -----
23 | [AngularJS](http://angularjs.org) 1.2.x (for [angular-validation 1.2.x](https://github.com/hueitan/angular-validation/tree/v1.2.x))
24 | [AngularJS](http://angularjs.org) 1.3.x (for [angular-validation 1.3.x](https://github.com/hueitan/angular-validation/tree/v1.3.x))
25 | [AngularJS](http://angularjs.org) 1.4.x (for [angular-validation 1.4.x](https://github.com/hueitan/angular-validation/tree/master))
26 |
27 | DEMO
28 | -----
29 | http://hueitan.github.io/angular-validation/
30 |
31 | Install
32 | -----
33 | Install with npm
34 |
35 | ```
36 | npm install angular-validation
37 | ```
38 |
39 | or with bower
40 |
41 | ```
42 | bower install angular-validation
43 | ```
44 |
45 | Using angular-validation
46 | ---
47 | ```html
48 |
49 |
50 | ```
51 | ```js
52 | angular.module('yourApp', ['validation']);
53 |
54 | // OR including your validation rule
55 | angular.module('yourApp', ['validation', 'validation.rule']);
56 | ```
57 |
58 | Writing your First Code
59 | ====
60 | ```html
61 |
75 | ```
76 |
77 | [Documentation API](https://github.com/hueitan/angular-validation/blob/master/API.md)
78 |
79 | Built-in validation in angular-validation-rule
80 | ===
81 |
82 | 1. Required
83 | 2. Url
84 | 3. Email
85 | 4. Number
86 | 5. Min length
87 | 6. Max length
88 |
89 | 5 and 6 require you to pass an inline parameter to set the length limit. Eg, `maxlength=6`.
90 |
91 | Anyone can give a `PR` for this angular-validation for more `built-in validation`
92 |
93 |
94 | Integrating with Twitter Bootstrap
95 | =====
96 |
97 | To integrate this package with Bootstrap you should do the following.
98 |
99 |
100 | Add the following LESS to your project
101 |
102 | ```css
103 | .ng-invalid.ng-dirty{
104 | .has-error .form-control;
105 | }
106 |
107 | label.has-error.control-label {
108 | .has-error .control-label;
109 | }
110 |
111 | ```
112 |
113 | Change the Error HTML to something like:
114 |
115 | ```javascript
116 | $validationProvider.setErrorHTML(function (msg) {
117 | return "" + msg + " ";
118 | });
119 | ```
120 |
121 | You can add the bootstrap class `.has-success` in a similar fashion.
122 |
123 | To toggle `.has-error` class on bootstrap `.form-group` wrapper for labels and controls, add:
124 |
125 | ```javascript
126 | angular.extend($validationProvider, {
127 | validCallback: function (element){
128 | $(element).parents('.form-group:first').removeClass('has-error');
129 | },
130 | invalidCallback: function (element) {
131 | $(element).parents('.form-group:first').addClass('has-error');
132 | }
133 | });
134 | ```
135 |
136 | License
137 | -----
138 | MIT
139 |
140 | CHANGELOG
141 | =====
142 | See [release](https://github.com/hueitan/angular-validation/releases)
143 |
144 | CONTRIBUTORS
145 | =====
146 | Thank you for your contribution [@lvarayut](https://github.com/lvarayut) and [@Nazanin1369](https://github.com/Nazanin1369) :heart:
147 | Thanks for all [contributors](https://github.com/hueitan/angular-validation/graphs/contributors)
148 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-validation",
3 | "version": "1.4.5",
4 | "homepage": "https://github.com/hueitan/angular-validation",
5 | "authors": [
6 | "hueitan"
7 | ],
8 | "description": "Client-side Validation for AngularJS",
9 | "main": "dist/angular-validation.js",
10 | "keywords": [
11 | "angular",
12 | "angularjs",
13 | "validation",
14 | "angular validation",
15 | "validator",
16 | "client-side"
17 | ],
18 | "license": "MIT",
19 | "ignore": [
20 | "**/.*",
21 | "node_modules",
22 | "bower_components",
23 | "test",
24 | "tests"
25 | ],
26 | "dependencies": {
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/bundle.js:
--------------------------------------------------------------------------------
1 | /******/ (function(modules) { // webpackBootstrap
2 | /******/ // The module cache
3 | /******/ var installedModules = {};
4 |
5 | /******/ // The require function
6 | /******/ function __webpack_require__(moduleId) {
7 |
8 | /******/ // Check if module is in cache
9 | /******/ if(installedModules[moduleId])
10 | /******/ return installedModules[moduleId].exports;
11 |
12 | /******/ // Create a new module (and put it into the cache)
13 | /******/ var module = installedModules[moduleId] = {
14 | /******/ exports: {},
15 | /******/ id: moduleId,
16 | /******/ loaded: false
17 | /******/ };
18 |
19 | /******/ // Execute the module function
20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
21 |
22 | /******/ // Flag the module as loaded
23 | /******/ module.loaded = true;
24 |
25 | /******/ // Return the exports of the module
26 | /******/ return module.exports;
27 | /******/ }
28 |
29 |
30 | /******/ // expose the modules object (__webpack_modules__)
31 | /******/ __webpack_require__.m = modules;
32 |
33 | /******/ // expose the module cache
34 | /******/ __webpack_require__.c = installedModules;
35 |
36 | /******/ // __webpack_public_path__
37 | /******/ __webpack_require__.p = '';
38 |
39 | /******/ // Load entry module and return exports
40 | /******/ return __webpack_require__(0);
41 | /******/ })
42 | /************************************************************************/
43 | /******/ ([
44 | /* 0 */
45 | /***/ function(module, exports, __webpack_require__) {
46 |
47 | module.exports = __webpack_require__(1);
48 |
49 |
50 | /***/ },
51 | /* 1 */
52 | /***/ function(module, exports) {
53 |
54 | 'use strict';
55 |
56 | angular.module('validation', ['validation.provider', 'validation.directive']);
57 | angular.module('validation.provider', []);
58 | angular.module('validation.directive', ['validation.provider']);
59 |
60 | /***/ }
61 | /******/ ]);
--------------------------------------------------------------------------------
/config/karma.conf.angular.1.3.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 | // base path, that will be used to resolve files and exclude
4 | basePath: '../',
5 |
6 | frameworks: ['jasmine'],
7 |
8 | // list of files / patterns to load in the browser
9 | files: [
10 | 'test/lib/angular.1.3.18.js',
11 | 'test/lib/angular-mocks.1.3.18.js',
12 | 'dist/angular-validation.js',
13 | 'dist/angular-validation-rule.js',
14 | 'test/unit/*.js'
15 | ],
16 |
17 | // list of files to exclude
18 | exclude: [],
19 |
20 | preprocessors: {
21 | 'dist/angular-validation.js': ['coverage']
22 | },
23 |
24 | // use dots reporter, as travis terminal does not support escaping sequences
25 | // possible values: 'dots', 'progress'
26 | // CLI --reporters progress
27 | reporters: ['progress', 'coverage'],
28 |
29 | // web server port
30 | // CLI --port 9876
31 | port: 9876,
32 |
33 | // enable / disable colors in the output (reporters and logs)
34 | // CLI --colors --no-colors
35 | colors: true,
36 |
37 | // level of logging
38 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
39 | // CLI --log-level debug
40 | logLevel: config.LOG_INFO,
41 |
42 | // enable / disable watching file and executing tests whenever any file changes
43 | // CLI --auto-watch --no-auto-watch
44 | autoWatch: false,
45 |
46 | // Start these browsers, currently available:
47 | // - Chrome
48 | // - ChromeCanary
49 | // - Firefox
50 | // - Opera
51 | // - Safari (only Mac)
52 | // - PhantomJS
53 | // - IE (only Windows)
54 | // CLI --browsers Chrome,Firefox,Safari
55 | browsers: ['PhantomJS'],
56 |
57 | // If browser does not capture in given timeout [ms], kill it
58 | // CLI --capture-timeout 5000
59 | captureTimeout: 20000,
60 |
61 | // Auto run tests on start (when browsers are captured) and exit
62 | // CLI --single-run --no-single-run
63 | singleRun: true,
64 |
65 | // report which specs are slower than 500ms
66 | // CLI --report-slower-than 500
67 | reportSlowerThan: 500,
68 |
69 | plugins: [
70 | 'karma-jasmine',
71 | 'karma-coverage',
72 | 'karma-chrome-launcher',
73 | 'karma-firefox-launcher',
74 | 'karma-phantomjs-launcher'
75 | ],
76 |
77 | // coverage settings
78 | coverageReporter: {
79 | type: "lcov",
80 | dir: "coverage/"
81 | }
82 | });
83 | };
84 |
--------------------------------------------------------------------------------
/config/karma.conf.angular.1.4.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 | // base path, that will be used to resolve files and exclude
4 | basePath: '../',
5 |
6 | frameworks: ['jasmine'],
7 |
8 | // list of files / patterns to load in the browser
9 | files: [
10 | 'test/lib/angular.1.4.7.js',
11 | 'test/lib/angular-mocks.1.4.7.js',
12 | 'dist/angular-validation.js',
13 | 'dist/angular-validation-rule.js',
14 | 'test/unit/*.js'
15 | ],
16 |
17 | // list of files to exclude
18 | exclude: [],
19 |
20 | preprocessors: {
21 | 'dist/angular-validation.js': ['coverage'],
22 | },
23 |
24 | // use dots reporter, as travis terminal does not support escaping sequences
25 | // possible values: 'dots', 'progress'
26 | // CLI --reporters progress
27 | reporters: ['progress', 'coverage'],
28 |
29 | // web server port
30 | // CLI --port 9876
31 | port: 9876,
32 |
33 | // enable / disable colors in the output (reporters and logs)
34 | // CLI --colors --no-colors
35 | colors: true,
36 |
37 | // level of logging
38 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
39 | // CLI --log-level debug
40 | logLevel: config.LOG_INFO,
41 |
42 | // enable / disable watching file and executing tests whenever any file changes
43 | // CLI --auto-watch --no-auto-watch
44 | autoWatch: false,
45 |
46 | // Start these browsers, currently available:
47 | // - Chrome
48 | // - ChromeCanary
49 | // - Firefox
50 | // - Opera
51 | // - Safari (only Mac)
52 | // - PhantomJS
53 | // - IE (only Windows)
54 | // CLI --browsers Chrome,Firefox,Safari
55 | browsers: ['PhantomJS'],
56 |
57 | // If browser does not capture in given timeout [ms], kill it
58 | // CLI --capture-timeout 5000
59 | captureTimeout: 20000,
60 |
61 | // Auto run tests on start (when browsers are captured) and exit
62 | // CLI --single-run --no-single-run
63 | singleRun: true,
64 |
65 | // report which specs are slower than 500ms
66 | // CLI --report-slower-than 500
67 | reportSlowerThan: 500,
68 |
69 | plugins: [
70 | 'karma-jasmine',
71 | 'karma-coverage',
72 | 'karma-chrome-launcher',
73 | 'karma-firefox-launcher',
74 | 'karma-phantomjs-launcher'
75 | ],
76 |
77 | // coverage settings
78 | coverageReporter: {
79 | type: "lcov",
80 | dir: "coverage/"
81 | }
82 | });
83 | };
84 |
--------------------------------------------------------------------------------
/demo/demo.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | angular.module('myApp', ['validation', 'validation.rule', 'ui.bootstrap', 'ui.bootstrap.tpls', 'ui.select', 'ngSanitize'])
3 |
4 | // -------------------
5 | // config phase
6 | // -------------------
7 | .config(['$validationProvider', function($validationProvider) {
8 | var defaultMsg;
9 | var expression;
10 |
11 | /**
12 | * Setup a default message for Url
13 | */
14 | defaultMsg = {
15 | url: {
16 | error: 'This is a error url given by user',
17 | success: 'It\'s Url'
18 | }
19 | };
20 |
21 | $validationProvider.setDefaultMsg(defaultMsg);
22 |
23 |
24 | /**
25 | * Setup a new Expression and default message
26 | * In this example, we setup a IP address Expression and default Message
27 | */
28 | expression = {
29 | ip: /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
30 | };
31 |
32 | defaultMsg = {
33 | ip: {
34 | error: 'This isn\'t ip address',
35 | success: 'It\'s ip'
36 | }
37 | };
38 |
39 | $validationProvider.setExpression(expression)
40 | .setDefaultMsg(defaultMsg);
41 |
42 | // or we can just setup directly
43 | $validationProvider.setDefaultMsg({
44 | ip: {
45 | error: 'This no ip',
46 | success: 'this ip'
47 | }
48 | })
49 | .setExpression({
50 | ip: /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
51 | });
52 |
53 | /**
54 | * Additions validation
55 | */
56 | $validationProvider
57 | .setExpression({
58 | huei: function(value, scope, element, attrs) {
59 | return value === 'Huei Tan';
60 | }
61 | })
62 | .setDefaultMsg({
63 | huei: {
64 | error: 'This should be Huei Tan',
65 | success: 'Thanks!'
66 | }
67 | });
68 |
69 | /**
70 | * Range Validation
71 | */
72 | $validationProvider
73 | .setExpression({
74 | range: function(value, scope, element, attrs) {
75 | if (value >= parseInt(attrs.min) && value <= parseInt(attrs.max)) {
76 | return value;
77 | }
78 | }
79 | })
80 | .setDefaultMsg({
81 | range: {
82 | error: 'Number should between 5 ~ 10',
83 | success: 'good'
84 | }
85 | });
86 | }])
87 |
88 | // -------------------
89 | // controller phase
90 | // -------------------
91 | .controller('index', ['$scope', '$injector', function($scope, $injector) {
92 | // Injector
93 |
94 | var $validationProvider = $injector.get('$validation');
95 |
96 | // Initial Value
97 | $scope.form = {
98 | requiredCallback: 'required',
99 | checkValid: $validationProvider.checkValid,
100 | submit: function() {
101 | // angular validation 1.2 can reduce this procedure, just focus on your action
102 | // $validationProvider.validate(form);
103 | },
104 | reset: function() {
105 | // angular validation 1.2 can reduce this procedure, just focus on your action
106 | // $validationProvider.reset(form);
107 | }
108 | };
109 |
110 | $scope.form2 = {
111 | checkValid: $validationProvider.checkValid,
112 | submit: function(form) {
113 | $validationProvider.validate(form);
114 | },
115 | reset: function(form) {
116 | $validationProvider.reset(form);
117 | }
118 | };
119 |
120 | $scope.form3 = {
121 | checkValid: $validationProvider.checkValid,
122 | submit: function(form) {
123 | $validationProvider.validate(form)
124 | .success($scope.success)
125 | .error($scope.error);
126 | },
127 | reset: function(form) {
128 | $validationProvider.reset(form);
129 | }
130 | };
131 |
132 | $scope.form4 = {
133 | changeErrorMsg: 'This is the First Error Msg',
134 | changeMsg: function() {
135 | $scope.form4.changeErrorMsg = 'This is the Second Error Msg';
136 | }
137 | };
138 |
139 | $scope.form5 = {
140 | checkValid: $validationProvider.checkValid,
141 | submit: function(form) {
142 | $validationProvider.validate(form);
143 | },
144 | reset: function(form) {
145 | $validationProvider.reset(form);
146 | }
147 | };
148 |
149 | $scope.form6 = {
150 | required: [{}, {}, {}], // ng-repeat
151 | checkValid: $validationProvider.checkValid
152 | };
153 |
154 | // Bootstrap Datepicker
155 | $scope.form7 = {
156 | dt: new Date(),
157 | open: function($event) {
158 | $scope.form7.status.opened = true;
159 | },
160 | dateOptions: {
161 | formatYear: 'yy',
162 | startingDay: 1
163 | },
164 | status: {
165 | opened: false
166 | }
167 | };
168 |
169 | $scope.form8 = {
170 | country: {},
171 | countries: [ // Taken from https://gist.github.com/unceus/6501985
172 | {
173 | name: 'Åland Islands',
174 | code: 'AX'
175 | }, {
176 | name: 'Albania',
177 | code: 'AL'
178 | }, {
179 | name: 'Algeria',
180 | code: 'DZ'
181 | }, {
182 | name: 'American Samoa',
183 | code: 'AS'
184 | }, {
185 | name: 'Antigua and Barbuda',
186 | code: 'AG'
187 | }, {
188 | name: 'Argentina',
189 | code: 'AR'
190 | }, {
191 | name: 'Bahamas',
192 | code: 'BS'
193 | }, {
194 | name: 'Bahrain',
195 | code: 'BH'
196 | }, {
197 | name: 'Bangladesh',
198 | code: 'BD'
199 | }, {
200 | name: 'Barbados',
201 | code: 'BB'
202 | }, {
203 | name: 'British Indian Ocean Territory',
204 | code: 'IO'
205 | }, {
206 | name: 'Brunei Darussalam',
207 | code: 'BN'
208 | }, {
209 | name: 'Bulgaria',
210 | code: 'BG'
211 | }, {
212 | name: 'Burkina Faso',
213 | code: 'BF'
214 | }, {
215 | name: 'Burundi',
216 | code: 'BI'
217 | }, {
218 | name: 'Cambodia',
219 | code: 'KH'
220 | }, {
221 | name: 'Cameroon',
222 | code: 'CM'
223 | }, {
224 | name: 'Canada',
225 | code: 'CA'
226 | }, {
227 | name: 'Czech Republic',
228 | code: 'CZ'
229 | }, {
230 | name: 'Denmark',
231 | code: 'DK'
232 | }, {
233 | name: 'Ethiopia',
234 | code: 'ET'
235 | }, {
236 | name: 'Falkland Islands (Malvinas)',
237 | code: 'FK'
238 | }, {
239 | name: 'Faroe Islands',
240 | code: 'FO'
241 | }, {
242 | name: 'Fiji',
243 | code: 'FJ'
244 | }, {
245 | name: 'Finland',
246 | code: 'FI'
247 | }, {
248 | name: 'France',
249 | code: 'FR'
250 | }, {
251 | name: 'Georgia',
252 | code: 'GE'
253 | }, {
254 | name: 'Germany',
255 | code: 'DE'
256 | }, {
257 | name: 'Hong Kong',
258 | code: 'HK'
259 | }, {
260 | name: 'Iceland',
261 | code: 'IS'
262 | }, {
263 | name: 'India',
264 | code: 'IN'
265 | }, {
266 | name: 'Indonesia',
267 | code: 'ID'
268 | }, {
269 | name: 'Iran, Islamic Republic Of',
270 | code: 'IR'
271 | }, {
272 | name: 'Iraq',
273 | code: 'IQ'
274 | }, {
275 | name: 'Ireland',
276 | code: 'IE'
277 | }, {
278 | name: 'Israel',
279 | code: 'IL'
280 | }, {
281 | name: 'Italy',
282 | code: 'IT'
283 | }, {
284 | name: 'Jamaica',
285 | code: 'JM'
286 | }, {
287 | name: 'Japan',
288 | code: 'JP'
289 | }, {
290 | name: 'Jersey',
291 | code: 'JE'
292 | }, {
293 | name: 'Jordan',
294 | code: 'JO'
295 | }, {
296 | name: 'Kiribati',
297 | code: 'KI'
298 | }, {
299 | name: 'Korea, Democratic People\'s Republic of',
300 | code: 'KP'
301 | }, {
302 | name: 'Korea, Republic of',
303 | code: 'KR'
304 | }, {
305 | name: 'Kuwait',
306 | code: 'KW'
307 | }, {
308 | name: 'Liberia',
309 | code: 'LR'
310 | }, {
311 | name: 'Libyan Arab Jamahiriya',
312 | code: 'LY'
313 | }, {
314 | name: 'Malaysia',
315 | code: 'MY'
316 | }, {
317 | name: 'Mexico',
318 | code: 'MX'
319 | }, {
320 | name: 'Micronesia, Federated States of',
321 | code: 'FM'
322 | }, {
323 | name: 'Moldova, Republic of',
324 | code: 'MD'
325 | }, {
326 | name: 'Norfolk Island',
327 | code: 'NF'
328 | }, {
329 | name: 'Northern Mariana Islands',
330 | code: 'MP'
331 | }, {
332 | name: 'Norway',
333 | code: 'NO'
334 | }, {
335 | name: 'Oman',
336 | code: 'OM'
337 | }, {
338 | name: 'Pakistan',
339 | code: 'PK'
340 | }, {
341 | name: 'Palau',
342 | code: 'PW'
343 | }, {
344 | name: 'Portugal',
345 | code: 'PT'
346 | }, {
347 | name: 'Puerto Rico',
348 | code: 'PR'
349 | }, {
350 | name: 'Qatar',
351 | code: 'QA'
352 | }, {
353 | name: 'Reunion',
354 | code: 'RE'
355 | }, {
356 | name: 'Romania',
357 | code: 'RO'
358 | }, {
359 | name: 'Russian Federation',
360 | code: 'RU'
361 | }, {
362 | name: 'South Africa',
363 | code: 'ZA'
364 | }, {
365 | name: 'Sweden',
366 | code: 'SE'
367 | }, {
368 | name: 'Tajikistan',
369 | code: 'TJ'
370 | }, {
371 | name: 'Thailand',
372 | code: 'TH'
373 | }, {
374 | name: 'Tunisia',
375 | code: 'TN'
376 | }, {
377 | name: 'Turkey',
378 | code: 'TR'
379 | }, {
380 | name: 'Tuvalu',
381 | code: 'TV'
382 | }, {
383 | name: 'Uganda',
384 | code: 'UG'
385 | }, {
386 | name: 'Ukraine',
387 | code: 'UA'
388 | }, {
389 | name: 'United Arab Emirates',
390 | code: 'AE'
391 | }, {
392 | name: 'United Kingdom',
393 | code: 'GB'
394 | }, {
395 | name: 'United States',
396 | code: 'US'
397 | }, {
398 | name: 'Vietnam',
399 | code: 'VN'
400 | }, {
401 | name: 'Yemen',
402 | code: 'YE'
403 | }, {
404 | name: 'Zambia',
405 | code: 'ZM'
406 | }, {
407 | name: 'Zimbabwe',
408 | code: 'ZW'
409 | }
410 | ],
411 | disable: function() {
412 | $scope.form8.disabled = true;
413 | },
414 | enable: function() {
415 | $scope.form8.disabled = false;
416 | },
417 | clear: function() {
418 | $scope.form8.country.selected = undefined;
419 | }
420 | };
421 |
422 | // Callback method
423 | $scope.success = function(message) {
424 | alert('Success ' + message);
425 | };
426 |
427 | $scope.error = function(message) {
428 | alert('Error ' + message);
429 | };
430 | }]);
431 | }).call(this);
432 |
--------------------------------------------------------------------------------
/demo/iconmonstr-github-10-icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hueitan/angular-validation/4636329802332285fa93234a336d7ea2f36684a7/demo/iconmonstr-github-10-icon-128.png
--------------------------------------------------------------------------------
/demo/main.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | max-width: 100%;
3 | overflow-x: hidden;
4 | }
5 |
6 | body {
7 | color: #626262;
8 | font-family: "Segoe UI",Arial,sans-serif;
9 | font-size: 14px;
10 | font-weight: normal;
11 | letter-spacing: 0.01em;
12 | }
13 | .validation-valid {
14 | color: #0074D9;
15 | }
16 |
17 | .validation-invalid {
18 | color: #FF4136;
19 | }
20 |
21 | #github-link {
22 | width: 50px;
23 | position: fixed;
24 | right: 1.5em;
25 | bottom: 3.5em;
26 | opacity: 0.6;
27 | -o-transition:.5s;
28 | -ms-transition:.5s;
29 | -moz-transition:.5s;
30 | -webkit-transition:.5s;
31 | transition:.5s;
32 | }
33 |
34 | #github-link:hover {
35 | opacity: 1;
36 | }
37 |
38 | .m-l-10 {
39 | margin-left: 10px;
40 | }
41 |
42 | pre, pre code {
43 | font-size: 13px;
44 | }
45 |
46 | pre code {
47 | border: 0;
48 | padding: 0;
49 | margin: 0;
50 | -moz-border-radius: 0;
51 | -webkit-border-radius: 0;
52 | border-radius: 0;
53 | font-family: Courier, 'New Courier', monospace;
54 | }
55 |
56 | pre {
57 | padding: 0;
58 | margin-bottom: 10px;
59 | font-size: 13px;
60 | line-height: 1.42857143;
61 | color: #333;
62 | word-break: break-all;
63 | word-wrap: break-word;
64 | border: 1px solid #ccc;
65 | border-radius: 4px;
66 | }
67 |
68 | .container > nav {
69 | margin: 0px 70px;
70 | }
71 |
72 | .navbar-default .navbar-brand {
73 | color: #0A7C71;
74 | }
75 |
76 | .bs-header {
77 | padding: 30px 15px 40px;
78 | font-size: 16px;
79 | text-align: center;
80 | text-shadow: 0 1px 0 rgba(0,0,0,.15);
81 | color: #0A7C71;
82 | background-color: #0DAD9E;
83 | text-align: left;
84 | }
85 |
86 | @media (min-width: 768px) {
87 | .bs-docs-header {
88 | padding-top: 60px;
89 | padding-bottom: 60px;
90 | font-size: 24px;
91 | text-align: left;
92 | }
93 | }
94 |
95 | .bs-docs-header {
96 | position: relative;
97 | text-align: left;
98 | padding: 60px 15px 40px;
99 | color: #fff;
100 | text-align: center;
101 | background-color: #36bc98;
102 | background-image: linear-gradient(to bottom,rgba(6, 99, 179, 0.81) 0,#36bc98 100%);
103 | background-repeat: repeat-x;
104 | }
105 |
106 | .bs-docs-header .container {
107 | text-align: left;
108 | }
109 |
110 | .bs-docs-header .container > p {
111 | color: #ccc;
112 | letter-spacing: 1.5px;
113 | }
114 |
115 | .bs-docs-decs {
116 | font-size: 16px;
117 | padding: 10px;
118 | }
119 |
120 | .m-b-20 {
121 | margin-bottom: 20px;
122 | }
123 |
124 | .checkbox {
125 | padding-left: 20px;
126 | }
127 |
128 | .radio {
129 | padding-left: 20px;
130 | }
131 |
132 | blockquote > label {
133 | font-size: 13px;
134 | }
135 |
136 | .form-control {
137 | width: 50%;
138 | }
139 |
140 | form > fieldset > legend {
141 | border: none;
142 | }
143 |
144 | /* gitter */
145 | .gitter-chat-embed {
146 | z-index: 99999;
147 | }
148 |
--------------------------------------------------------------------------------
/dist/angular-validation-rule.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | angular
3 | .module('validation.rule', ['validation'])
4 | .config(['$validationProvider', function($validationProvider) {
5 | var expression = {
6 | required: function(value) {
7 | return !!value;
8 | },
9 | url: /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)/,
10 | email: /^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/,
11 | number: /^\d+$/,
12 | minlength: function(value, scope, element, attrs, param) {
13 | return value && value.length >= param;
14 | },
15 | maxlength: function(value, scope, element, attrs, param) {
16 | return !value || value.length <= param;
17 | }
18 | };
19 |
20 | var defaultMsg = {
21 | required: {
22 | error: 'This should be Required!!',
23 | success: 'It\'s Required'
24 | },
25 | url: {
26 | error: 'This should be Url',
27 | success: 'It\'s Url'
28 | },
29 | email: {
30 | error: 'This should be Email',
31 | success: 'It\'s Email'
32 | },
33 | number: {
34 | error: 'This should be Number',
35 | success: 'It\'s Number'
36 | },
37 | minlength: {
38 | error: 'This should be longer',
39 | success: 'Long enough!'
40 | },
41 | maxlength: {
42 | error: 'This should be shorter',
43 | success: 'Short enough!'
44 | }
45 | };
46 | $validationProvider.setExpression(expression).setDefaultMsg(defaultMsg);
47 | }]);
48 | }).call(this);
49 |
--------------------------------------------------------------------------------
/dist/angular-validation-rule.min.js:
--------------------------------------------------------------------------------
1 | (function(){angular.module("validation.rule",["validation"]).config(["$validationProvider",function(a){var b={required:function(a){return!!a},url:/((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)/,email:/^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/,number:/^\d+$/,minlength:function(a,b,c,d,e){return a&&a.length>=e},maxlength:function(a,b,c,d,e){return!a||a.length<=e}},c={required:{error:"This should be Required!!",success:"It's Required"},url:{error:"This should be Url",success:"It's Url"},email:{error:"This should be Email",success:"It's Email"},number:{error:"This should be Number",success:"It's Number"},minlength:{error:"This should be longer",success:"Long enough!"},maxlength:{error:"This should be shorter",success:"Short enough!"}};a.setExpression(b).setDefaultMsg(c)}])}).call(this);
--------------------------------------------------------------------------------
/dist/angular-validation.min.js:
--------------------------------------------------------------------------------
1 | angular.module("validation",["validation.provider","validation.directive"]),angular.module("validation.provider",[]),angular.module("validation.directive",["validation.provider"]),function(){function a(){var a,b,c,d,e,f=this,g=function(f){a=f,b=a.get("$rootScope"),c=a.get("$http"),d=a.get("$q"),e=a.get("$timeout")},h={},i=null,j={};this.setExpression=function(a){return angular.extend(h,a),f},this.getExpression=function(a){return h[a]},this.setDefaultMsg=function(a){return angular.extend(j,a),f},this.getDefaultMsg=function(a){return j[a]},this.setValidMethod=function(a){i=a},this.getValidMethod=function(){return i},this.setErrorHTML=function(a){if(a.constructor===Function)return f.getErrorHTML=a,f},this.getErrorHTML=function(a){return''+a+"
"},this.setSuccessHTML=function(a){if(a.constructor===Function)return f.getSuccessHTML=a,f},this.getSuccessHTML=function(a){return''+a+"
"},this.showSuccessMessage=!0,this.showErrorMessage=!0,this.checkValid=function(a){return!(!a||!a.$valid)},this.validate=function(a){var c=d.defer(),g=0;if(void 0===a)return console.error("This is not a regular Form name scope"),c.reject("This is not a regular Form name scope"),c.promise;if(a.validationId)b.$broadcast(a.$name+"submit-"+a.validationId,g++);else if(a.constructor===Array)for(var h in a)b.$broadcast(a[h].$name+"submit-"+a[h].validationId,g++);else for(var i in a)"$"!==i[0]&&a[i].hasOwnProperty("$dirty")&&b.$broadcast(i+"submit-"+a[i].validationId,g++);return c.promise.success=function(a){return c.promise.then(function(b){a(b)}),c.promise},c.promise.error=function(a){return c.promise.then(null,function(b){a(b)}),c.promise},e(function(){f.checkValid(a)?c.resolve("success"):c.reject("error")}),c.promise},this.validCallback=null,this.invalidCallback=null,this.resetCallback=null,this.reset=function(a){if(void 0===a)return void console.error("This is not a regular Form name scope");if(a.validationId)b.$broadcast(a.$name+"reset-"+a.validationId);else if(a.constructor===Array)for(var c in a)b.$broadcast(a[c].$name+"reset-"+a[c].validationId);else for(var d in a)"$"!==d[0]&&a[d].hasOwnProperty("$dirty")&&b.$broadcast(d+"reset-"+a[d].validationId)},this.addMsgElement=function(a){return a.after(" ")},this.getMsgElement=function(a){return a.next()},this.$get=["$injector",function(a){return g(a),{setValidMethod:this.setValidMethod,getValidMethod:this.getValidMethod,setErrorHTML:this.setErrorHTML,getErrorHTML:this.getErrorHTML,setSuccessHTML:this.setSuccessHTML,getSuccessHTML:this.getSuccessHTML,setExpression:this.setExpression,getExpression:this.getExpression,setDefaultMsg:this.setDefaultMsg,getDefaultMsg:this.getDefaultMsg,showSuccessMessage:this.showSuccessMessage,showErrorMessage:this.showErrorMessage,checkValid:this.checkValid,validate:this.validate,validCallback:this.validCallback,invalidCallback:this.invalidCallback,resetCallback:this.resetCallback,reset:this.reset,addMsgElement:this.addMsgElement,getMsgElement:this.getMsgElement}}]}angular.module("validation.provider").provider("$validation",a)}.call(this),function(){function a(a){var b=a.get("$validation"),c=a.get("$timeout"),d=a.get("$parse");return{link:function(a,e,f){var g=d(f.validationReset)(a);c(function(){e.on("click",function(a){a.preventDefault(),b.reset(g)})})}}}angular.module("validation.directive").directive("validationReset",a),a.$inject=["$injector"]}.call(this),function(){function a(a){var b=a.get("$validation"),c=a.get("$timeout"),d=a.get("$parse");return{priority:1,require:"?ngClick",link:function(a,e,f){var g=d(f.validationSubmit)(a);c(function(){e.off("click"),e.on("click",function(c){c.preventDefault(),b.validate(g).success(function(){d(f.ngClick)(a)})})})}}}angular.module("validation.directive").directive("validationSubmit",a),a.$inject=["$injector"]}.call(this),function(){function a(a){function b(a,b,c){for(var d=document.querySelectorAll("*[validation-group="+b+"]"),e=0,f=d.length;e0?(b=a[0],angular.isObject(b)||(b={result:b,message:""})):b={result:!1,message:""},b}var d=a.get("$validation"),e=a.get("$q"),f=a.get("$timeout"),g=a.get("$compile"),h=a.get("$parse"),i={},j=function(a,b,c,e,f,i,j){var k,l=b||d.getDefaultMsg(c).success,m=h(i.validCallback),n=i.messageId,o=i.validationGroup;return k=n||o?angular.element(document.querySelector("#"+(n||o))):d.getMsgElement(a),a.attr("no-validation-message")?k.css("display","none"):d.showSuccessMessage&&l?(l=angular.isFunction(l)?l(a,i,j):l,k.html("").append(g(d.getSuccessHTML(l,a,i))(e)),k.css("display","")):k.css("display","none"),f.$setValidity(f.$name,!0),m(e,{message:l}),d.validCallback&&d.validCallback(a),!0},k=function(a,b,c,e,f,i,j){var k,l=b||d.getDefaultMsg(c).error,m=h(i.invalidCallback),n=i.messageId,o=i.validationGroup;return k=n||o?angular.element(document.querySelector("#"+(n||o))):d.getMsgElement(a),a.attr("no-validation-message")?k.css("display","none"):d.showErrorMessage&&l?(l=angular.isFunction(l)?l(a,i,j):l,k.html("").append(g(d.getErrorHTML(l,a,i))(e)),k.css("display","")):k.css("display","none"),f.$setValidity(f.$name,!1),m(e,{message:l}),d.invalidCallback&&d.invalidCallback(a),!1},l=function(a){var b=i[a];return Object.keys(b).some(function(a){return b[a]})},m={},n=function(a,f,g,h,m,o){var p=m.slice(0),q=p[0].trim(),r=q.indexOf("="),s=r===-1?q:q.substr(0,r),t=r===-1?null:q.substr(r+1),u=p.slice(1),v=s+"SuccessMessage",w=s+"ErrorMessage",x=d.getExpression(s),y=g.validationGroup,z={success:function(b){return j(f,b||g[v],s,a,h,g,t),!u.length||n(a,f,g,h,u,o)},error:function(b){return k(f,b||g[w],s,a,h,g,t)}};if(void 0===x)return console.error('You are using undefined validator "%s"',s),u.length?n(a,f,g,h,u,o):void 0;if(x.constructor===Function)return e.all([d.getExpression(s)(o,a,f,g,t)]).then(function(d){var e=c(d),f=e.message;return e.result?(y&&(i[y][h.$name]=!0,b(a,y,!0)),z.success(f)):y?(i[y][h.$name]=!1,l(y)?void b(a,y,!0):(b(a,y,!1),z.error(f))):z.error(f)},function(){return z.error()});if(x.constructor!==RegExp)return z.error();if(void 0!==o&&null!==o){if(d.getExpression(s).test(o))return y&&(i[y][h.$name]=!0,b(a,y,!0)),z.success();if(!y)return z.error();if(i[y][h.$name]=!1,!l(y))return b(a,y,!1),z.error();b(a,y,!0)}},o=function(){return(65536*(1+Math.random())|0).toString(16).substring(1)},p=function(){return o()+o()+o()+o()};return{restrict:"A",require:"ngModel",link:function(a,b,c,e){var g="false"!==c.useViewValue,h=c.validator,j=c.messageId,k=c.validationGroup,l=c.validMethod,o=c.ngModel,q=function(){},r=h.split(","),s=e.validationId=p(),t=null,u=void 0;return"boolean"==typeof c.initialValidity&&(u=c.initialValidity),c.$observe("validator",function(a){r=a.split(",")}),k&&(i[k]||(i[k]={}),i[k][e.$name]=!1),j||k||d.addMsgElement(b),e.$setValidity(e.$name,u),a.$on(e.$name+"reset-"+s,function(){q(),f(function(){e.$setViewValue(t),e.$setPristine(),e.$setValidity(e.$name,void 0),e.$render(),j||k?angular.element(document.querySelector("#"+(j||k))).html(""):d.getMsgElement(b).html(""),d.resetCallback&&d.resetCallback(b)})}),l=angular.isUndefined(l)?d.getValidMethod():l,a.$on(e.$name+"submit-"+s,function(d,h){var i=g?e.$viewValue:e.$modelValue,j=!1;j=n(a,b,c,e,r,i),"submit"===l&&(q(),q=a.$watch(function(){return a.$eval(o)},function(d,f){d!==f&&(void 0!==d&&null!==d||(d=""),j=n(a,b,c,e,r,d))}));var k=function(a){a?delete m[h]:(m[h]=b[0],f(function(){m[Math.min.apply(null,Object.keys(m))].focus()},0))};j instanceof Object?j.then(k):k(j)}),"blur"===l?void b.bind("blur",function(){var d=a.$eval(o);"$apply"!==a.$root.$$phase?a.$apply(function(){n(a,b,c,e,r,d)}):n(a,b,c,e,r,d)}):void("submit"!==l&&"submit-only"!==l&&(a.$watch(function(){return a.$eval(o)},function(f){if(e.$pristine&&e.$viewValue)t=e.$viewValue||"",e.$setViewValue(e.$viewValue);else if(e.$pristine)return void(j||k?angular.element(document.querySelector("#"+(j||k))).html(""):d.getMsgElement(b).html(""));n(a,b,c,e,r,f)}),f(function(){c.$observe("noValidationMessage",function(a){var c;c=j||k?angular.element(document.querySelector("#"+(j||k))):d.getMsgElement(b),"true"===a||a===!0?c.css("display","none"):"false"!==a&&a!==!1||c.css("display","block")})})))}}}angular.module("validation.directive").directive("validator",a),a.$inject=["$injector"]}.call(this);
--------------------------------------------------------------------------------
/dist/bundle.js:
--------------------------------------------------------------------------------
1 | /******/ (function(modules) { // webpackBootstrap
2 | /******/ // The module cache
3 | /******/ var installedModules = {};
4 |
5 | /******/ // The require function
6 | /******/ function __webpack_require__(moduleId) {
7 |
8 | /******/ // Check if module is in cache
9 | /******/ if(installedModules[moduleId])
10 | /******/ return installedModules[moduleId].exports;
11 |
12 | /******/ // Create a new module (and put it into the cache)
13 | /******/ var module = installedModules[moduleId] = {
14 | /******/ exports: {},
15 | /******/ id: moduleId,
16 | /******/ loaded: false
17 | /******/ };
18 |
19 | /******/ // Execute the module function
20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
21 |
22 | /******/ // Flag the module as loaded
23 | /******/ module.loaded = true;
24 |
25 | /******/ // Return the exports of the module
26 | /******/ return module.exports;
27 | /******/ }
28 |
29 |
30 | /******/ // expose the modules object (__webpack_modules__)
31 | /******/ __webpack_require__.m = modules;
32 |
33 | /******/ // expose the module cache
34 | /******/ __webpack_require__.c = installedModules;
35 |
36 | /******/ // __webpack_public_path__
37 | /******/ __webpack_require__.p = '';
38 |
39 | /******/ // Load entry module and return exports
40 | /******/ return __webpack_require__(0);
41 | /******/ })
42 | /************************************************************************/
43 | /******/ ([
44 | /* 0 */
45 | /***/ function(module, exports, __webpack_require__) {
46 |
47 | module.exports = __webpack_require__(1);
48 |
49 |
50 | /***/ },
51 | /* 1 */
52 | /***/ function(module, exports) {
53 |
54 | 'use strict';
55 |
56 | angular.module('validation', ['validation.provider', 'validation.directive']);
57 | angular.module('validation.provider', []);
58 | angular.module('validation.directive', ['validation.provider']);
59 |
60 | /***/ }
61 | /******/ ]);
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | Angular Validation
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
65 |
66 |
73 |
74 |
75 |
76 |
79 | Requirements
80 |
81 | AngularJS 1.2.x (for angular-validation 1.2.x)
82 | AngularJS 1.3.x (for angular-validation 1.3.x)
83 | Installation
84 |
85 |
86 |
Install with npm
87 |
npm install angular-validation
88 |
Or with Bower
89 |
bower install angular-validation
90 |
91 |
92 | Files to download
93 |
94 | Add the files to your html
95 | <script src="dist/angular-validation.js"></script>
96 | <script src="dist/angular-validation-rule.js"></script>
97 | If you are using Bower:
98 | <script src="lib/angular-validation/dist/angular-validation.js"></script>
99 | <script src="lib/angular-validation/dist/angular-validation-rule.js"></script>
100 | angular.module('yourApp', ['validation']);
101 | Including your validation rule
102 |
103 | angular.module('yourApp', ['validation', 'validation.rule']);
104 |
105 |
106 | Writing your first code
107 |
108 | <form name="Form">
109 | <div class="row">
110 | <div>
111 | <label>Required</label>
112 | <input type="text" name="required" ng-model="form.required" validator="required"></div>
113 | <div>
114 | <label>Url</label>
115 | <input type="text" name="url" ng-model="form.url" validator="required, url">
116 | </div>
117 | <button validation-submit="Form" ng-click="next()">Submit</button>
118 | <button validation-reset="Form">Reset</button>
119 | </div>
120 | </form>
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
131 | You can style different states of your form elements using the following selectors
132 | Invalid Selectors
133 |
134 | .ng-invalid.ng-dirty { /* Your style here */ }
135 |
136 | Valid Selectors
137 |
138 | .ng-valid.ng-dirty { /* Your style here */ }
139 |
140 |
141 |
142 | You can also include your vendor libraries classes in you are using CSS preprocessors such as
143 | LESS or
144 | SASS .
145 |
146 | Using Bootstrap classes
147 |
148 | .ng-valid.ng-dirty { .has-error .form-control; }
149 |
150 |
151 |
152 |
153 | For more on AngularJS validation classes check at their
154 | documentation .
155 |
156 |
157 |
158 |
159 |
160 |
163 |
164 |
205 |
206 |
228 |
229 |
230 |
260 |
261 |
262 |
300 |
301 |
302 |
356 |
357 |
358 |
381 |
382 |
383 |
403 |
404 |
405 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
468 |
469 |
470 |
473 |
482 |
488 |
489 |
490 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | var angular = require('./node_modules/angular/angular.js');
2 | var validationApp = require('./src/module.js');
3 |
--------------------------------------------------------------------------------
/package.js:
--------------------------------------------------------------------------------
1 | var options = {
2 | version: '1.4.5',
3 | where: 'client',
4 | packageName: 'hueitan:angular-validation'
5 | };
6 |
7 | // meta data
8 | Package.describe({
9 | name: options.packageName,
10 | version: options.version,
11 | summary: 'Client-side Validation for AngularJS',
12 | git: 'https://github.com/hueitan/angular-validation',
13 | documentation: 'README.md'
14 | });
15 |
16 | Package.onUse(function(api) {
17 | api.versionsFrom('METEOR@0.9.0', 'METEOR@1.0');
18 | api.use('angular:angular@1.3.15', options.where);
19 | api.addFiles([
20 | 'dist/angular-validation.js',
21 | 'dist/angular-validation-rule.js'
22 | ], options.where);
23 | });
24 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-validation",
3 | "version": "1.4.5",
4 | "description": "Client-side Validation for AngularJS",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/hueitan/angular-validation"
8 | },
9 | "homepage": "http://hueitan.github.io/angular-validation/",
10 | "scripts": {
11 | "test": "grunt karma",
12 | "build": "grunt build",
13 | "development": "grunt dev"
14 | },
15 | "author": {
16 | "name": "Huei Tan",
17 | "url": "https://github.com/hueitan"
18 | },
19 | "license": "MIT",
20 | "keywords": [
21 | "angular",
22 | "angularjs",
23 | "validation",
24 | "angular validation",
25 | "validator",
26 | "client-side"
27 | ],
28 | "main": "./dist/angular-validation.js",
29 | "bugs": "https://github.com/hueitan/angular-validation/issues",
30 | "devDependencies": {
31 | "babel-core": "^6.7.7",
32 | "babel-loader": "^6.2.4",
33 | "babel-preset-es2015": "^6.6.0",
34 | "grunt": "~1.0.1",
35 | "grunt-browser-sync": "~2.2.0",
36 | "grunt-contrib-clean": "~1.0.0",
37 | "grunt-contrib-concat": "~1.0.1",
38 | "grunt-contrib-copy": "~1.0.0",
39 | "grunt-contrib-jshint": "^1.0.0",
40 | "grunt-contrib-uglify": "~2.0.0",
41 | "grunt-contrib-watch": "~1.0.0",
42 | "grunt-jsbeautifier": "^0.2.13",
43 | "grunt-karma": "~2.0.0",
44 | "grunt-karma-coveralls": "^2.5.4",
45 | "jasmine-core": "^2.4.1",
46 | "karma": "~1.1.1",
47 | "karma-chrome-launcher": "~1.0.1",
48 | "karma-coffee-preprocessor": "~1.0.1",
49 | "karma-coverage": "^1.1.0",
50 | "karma-firefox-launcher": "~1.0.0",
51 | "karma-html2js-preprocessor": "~1.0.0",
52 | "karma-jasmine": "~1.0.2",
53 | "karma-phantomjs-launcher": "~1.0.1",
54 | "karma-requirejs": "~1.0.0",
55 | "karma-script-launcher": "~1.0.0",
56 | "load-grunt-tasks": "~3.5.0",
57 | "requirejs": "~2.2.0",
58 | "time-grunt": "~1.4.0"
59 | },
60 | "dependencies": {
61 | "webpack": "^1.13.0"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/module.js:
--------------------------------------------------------------------------------
1 | angular.module('validation', ['validation.provider', 'validation.directive']);
2 | angular.module('validation.provider', []);
3 | angular.module('validation.directive', ['validation.provider']);
4 |
--------------------------------------------------------------------------------
/src/provider.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | angular
3 | .module('validation.provider')
4 | .provider('$validation', Provider);
5 |
6 | function Provider() {
7 | var $injector;
8 | var $scope;
9 | var $http;
10 | var $q;
11 | var $timeout;
12 | var _this = this;
13 |
14 | /**
15 | * Setup the provider
16 | * @param injector
17 | */
18 | var setup = function(injector) {
19 | $injector = injector;
20 | $scope = $injector.get('$rootScope');
21 | $http = $injector.get('$http');
22 | $q = $injector.get('$q');
23 | $timeout = $injector.get('$timeout');
24 | };
25 |
26 | /**
27 | * Define validation type RegExp
28 | * @type {{}}
29 | */
30 | var expression = {};
31 |
32 | /**
33 | * default valid method
34 | * @type {{}}
35 | */
36 | var validMethod = null;
37 |
38 | /**
39 | * default error, success message
40 | * @type {{}}
41 | */
42 | var defaultMsg = {};
43 |
44 | /**
45 | * Allow user to set a custom Expression, do remember set the default message using setDefaultMsg
46 | * @param obj
47 | * @returns {*}
48 | */
49 | this.setExpression = function(obj) {
50 | angular.extend(expression, obj);
51 | return _this;
52 | };
53 |
54 | /**
55 | * Get the Expression
56 | * @param exprs
57 | * @returns {*}
58 | */
59 | this.getExpression = function(exprs) {
60 | return expression[exprs];
61 | };
62 |
63 | /**
64 | * Allow user to set default message
65 | * @param obj
66 | * @returns {*}
67 | */
68 | this.setDefaultMsg = function(obj) {
69 | angular.extend(defaultMsg, obj);
70 | return _this;
71 | };
72 |
73 | /**
74 | * Get the Default Message
75 | * @param msg
76 | * @returns {*}
77 | */
78 | this.getDefaultMsg = function(msg) {
79 | return defaultMsg[msg];
80 | };
81 |
82 | /**
83 | * allow user to set the global valid method
84 | * @param v
85 | * @returns {*}
86 | */
87 | this.setValidMethod = function(v) {
88 | validMethod = v;
89 | };
90 |
91 | /**
92 | * Get the valid method
93 | * @returns {*}
94 | */
95 | this.getValidMethod = function() {
96 | return validMethod;
97 | };
98 |
99 | /**
100 | * Override the errorHTML function
101 | * @param func
102 | * @returns {*}
103 | */
104 | this.setErrorHTML = function(func) {
105 | if (func.constructor !== Function) {
106 | return;
107 | }
108 | _this.getErrorHTML = func;
109 | return _this;
110 | };
111 |
112 | /**
113 | * Invalid message HTML, here's the default
114 | * @param message
115 | * @returns {string}
116 | */
117 | this.getErrorHTML = function(message) {
118 | return '' + message + '
';
119 | };
120 |
121 | /**
122 | * Override the successHTML function
123 | * @param func
124 | * @returns {*}
125 | */
126 | this.setSuccessHTML = function(func) {
127 | if (func.constructor !== Function) {
128 | return;
129 | }
130 | _this.getSuccessHTML = func;
131 | return _this;
132 | };
133 |
134 | /**
135 | * Valid message HTML, here's the default
136 | * @param message
137 | * @returns {string}
138 | */
139 | this.getSuccessHTML = function(message) {
140 | return '' + message + '
';
141 | };
142 |
143 | /**
144 | * Whether show the validation success message
145 | * You can easily change this to false in your config
146 | * example: $validationProvider.showSuccessMessage = false;
147 | * @type {boolean}
148 | */
149 | this.showSuccessMessage = true;
150 |
151 | /**
152 | * Whether show the validation error message
153 | * You can easily change this to false in your config
154 | * example: $validationProvider.showErrorMessage = false;
155 | * @type {boolean}
156 | */
157 | this.showErrorMessage = true;
158 |
159 | /**
160 | * Check form valid, return true
161 | * checkValid(Form): Check the specific form(Form) valid from angular `$valid`
162 | * @param form
163 | * @returns {boolean}
164 | */
165 | this.checkValid = function(form) {
166 | return !!(form && form.$valid);
167 | };
168 |
169 | /**
170 | * Validate the form when click submit, when `validMethod = submit`
171 | * @param form
172 | * @returns {promise|*}
173 | */
174 | this.validate = function(form) {
175 | var deferred = $q.defer();
176 | var idx = 0;
177 |
178 | if (form === undefined) {
179 | console.error('This is not a regular Form name scope');
180 | deferred.reject('This is not a regular Form name scope');
181 | return deferred.promise;
182 | }
183 |
184 | if (form.validationId) { // single
185 | $scope.$broadcast(form.$name + 'submit-' + form.validationId, idx++);
186 | } else if (form.constructor === Array) { // multiple
187 | for (var k in form) {
188 | $scope.$broadcast(form[k].$name + 'submit-' + form[k].validationId, idx++);
189 | }
190 | } else {
191 | for (var i in form) { // whole scope
192 | if (i[0] !== '$' && form[i].hasOwnProperty('$dirty')) {
193 | $scope.$broadcast(i + 'submit-' + form[i].validationId, idx++);
194 | }
195 | }
196 | }
197 |
198 | deferred.promise.success = function(fn) {
199 | deferred.promise.then(function(value) {
200 | fn(value);
201 | });
202 | return deferred.promise;
203 | };
204 |
205 | deferred.promise.error = function(fn) {
206 | deferred.promise.then(null, function(value) {
207 | fn(value);
208 | });
209 | return deferred.promise;
210 | };
211 |
212 | $timeout(function() {
213 | if (_this.checkValid(form)) {
214 | deferred.resolve('success');
215 | } else {
216 | deferred.reject('error');
217 | }
218 | });
219 |
220 | return deferred.promise;
221 | };
222 |
223 | /**
224 | * Do this function if validation valid
225 | * @param element
226 | */
227 | this.validCallback = null;
228 |
229 | /**
230 | * Do this function if validation invalid
231 | * @param element
232 | */
233 | this.invalidCallback = null;
234 |
235 | /**
236 | * Do this function when reset is performed
237 | * @param element
238 | */
239 | this.resetCallback = null;
240 |
241 | /**
242 | * reset the specific form
243 | * @param form
244 | */
245 | this.reset = function(form) {
246 | if (form === undefined) {
247 | console.error('This is not a regular Form name scope');
248 | return;
249 | }
250 |
251 | if (form.validationId) {
252 | $scope.$broadcast(form.$name + 'reset-' + form.validationId);
253 | } else if (form.constructor === Array) {
254 | for (var k in form) {
255 | $scope.$broadcast(form[k].$name + 'reset-' + form[k].validationId);
256 | }
257 | } else {
258 | for (var i in form) {
259 | if (i[0] !== '$' && form[i].hasOwnProperty('$dirty')) {
260 | $scope.$broadcast(i + 'reset-' + form[i].validationId);
261 | }
262 | }
263 | }
264 | };
265 |
266 | /**
267 | * Add Message Element in config phase
268 | * When you need custom your messageElement
269 | * NODE: this funtion & and `message-id` attribute, have similar purpose.
270 | * This function will help you add your `messageElement` automatically instead of pre-defined.
271 | * @param element
272 | */
273 | this.addMsgElement = function(element) {
274 | return element.after(' ');
275 | };
276 |
277 | /**
278 | * Add Message Element in config phase
279 | * When you need custom your messageElement
280 | * NODE: this funtion & and `message-id` attribute, have similar purpose.
281 | * This function will help you add your `messageElement` automatically instead of pre-defined.
282 | * @param element
283 | */
284 | this.getMsgElement = function(element) {
285 | return element.next();
286 | };
287 |
288 | /**
289 | * $get
290 | * @returns {{setErrorHTML: *, getErrorHTML: Function, setSuccessHTML: *, getSuccessHTML: Function, setExpression: *, getExpression: Function, setDefaultMsg: *, getDefaultMsg: Function, checkValid: Function, validate: Function, reset: Function}}
291 | */
292 | this.$get = ['$injector', function($injector) {
293 | setup($injector);
294 | return {
295 | setValidMethod: this.setValidMethod,
296 | getValidMethod: this.getValidMethod,
297 | setErrorHTML: this.setErrorHTML,
298 | getErrorHTML: this.getErrorHTML,
299 | setSuccessHTML: this.setSuccessHTML,
300 | getSuccessHTML: this.getSuccessHTML,
301 | setExpression: this.setExpression,
302 | getExpression: this.getExpression,
303 | setDefaultMsg: this.setDefaultMsg,
304 | getDefaultMsg: this.getDefaultMsg,
305 | showSuccessMessage: this.showSuccessMessage,
306 | showErrorMessage: this.showErrorMessage,
307 | checkValid: this.checkValid,
308 | validate: this.validate,
309 | validCallback: this.validCallback,
310 | invalidCallback: this.invalidCallback,
311 | resetCallback: this.resetCallback,
312 | reset: this.reset,
313 | addMsgElement: this.addMsgElement,
314 | getMsgElement: this.getMsgElement
315 | };
316 | }];
317 | }
318 | }).call(this);
319 |
--------------------------------------------------------------------------------
/src/reset.directive.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | angular
3 | .module('validation.directive')
4 | .directive('validationReset', Reset);
5 |
6 | function Reset($injector) {
7 | var $validationProvider = $injector.get('$validation');
8 | var $timeout = $injector.get('$timeout');
9 | var $parse = $injector.get('$parse');
10 | return {
11 | link: function postLink(scope, element, attrs) {
12 | var form = $parse(attrs.validationReset)(scope);
13 | $timeout(function() {
14 | element.on('click', function(e) {
15 | e.preventDefault();
16 | $validationProvider.reset(form);
17 | });
18 | });
19 | }
20 | };
21 | }
22 | Reset.$inject = ['$injector'];
23 | }).call(this);
24 |
--------------------------------------------------------------------------------
/src/rule.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | angular
3 | .module('validation.rule', ['validation'])
4 | .config(['$validationProvider', function($validationProvider) {
5 | var expression = {
6 | required: function(value) {
7 | return !!value;
8 | },
9 | url: /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)/,
10 | email: /^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/,
11 | number: /^\d+$/,
12 | minlength: function(value, scope, element, attrs, param) {
13 | return value && value.length >= param;
14 | },
15 | maxlength: function(value, scope, element, attrs, param) {
16 | return !value || value.length <= param;
17 | }
18 | };
19 |
20 | var defaultMsg = {
21 | required: {
22 | error: 'This should be Required!!',
23 | success: 'It\'s Required'
24 | },
25 | url: {
26 | error: 'This should be Url',
27 | success: 'It\'s Url'
28 | },
29 | email: {
30 | error: 'This should be Email',
31 | success: 'It\'s Email'
32 | },
33 | number: {
34 | error: 'This should be Number',
35 | success: 'It\'s Number'
36 | },
37 | minlength: {
38 | error: 'This should be longer',
39 | success: 'Long enough!'
40 | },
41 | maxlength: {
42 | error: 'This should be shorter',
43 | success: 'Short enough!'
44 | }
45 | };
46 | $validationProvider.setExpression(expression).setDefaultMsg(defaultMsg);
47 | }]);
48 | }).call(this);
49 |
--------------------------------------------------------------------------------
/src/submit.directive.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | angular
3 | .module('validation.directive')
4 | .directive('validationSubmit', Submit);
5 |
6 | function Submit($injector) {
7 | var $validationProvider = $injector.get('$validation');
8 | var $timeout = $injector.get('$timeout');
9 | var $parse = $injector.get('$parse');
10 | return {
11 | priority: 1, // execute before ng-click (0)
12 | require: '?ngClick',
13 | link: function postLink(scope, element, attrs) {
14 | var form = $parse(attrs.validationSubmit)(scope);
15 | $timeout(function() {
16 | // Disable ng-click event propagation
17 | element.off('click');
18 | element.on('click', function(e) {
19 | e.preventDefault();
20 | $validationProvider.validate(form)
21 | .success(function() {
22 | $parse(attrs.ngClick)(scope);
23 | });
24 | });
25 | });
26 | }
27 | };
28 | }
29 | Submit.$inject = ['$injector'];
30 | }).call(this);
31 |
--------------------------------------------------------------------------------
/src/validator.directive.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | angular
3 | .module('validation.directive')
4 | .directive('validator', Validator);
5 |
6 | function Validator($injector) {
7 | var $validationProvider = $injector.get('$validation');
8 | var $q = $injector.get('$q');
9 | var $timeout = $injector.get('$timeout');
10 | var $compile = $injector.get('$compile');
11 | var $parse = $injector.get('$parse');
12 | var groups = {};
13 |
14 | /**
15 | * Do this function if validation valid
16 | * @param element
17 | * @param validMessage
18 | * @param validation
19 | * @param callback
20 | * @param ctrl
21 | * @returns {}
22 | */
23 | var validFunc = function(element, validMessage, validation, scope, ctrl, attrs, param) {
24 | var messageToShow = validMessage || $validationProvider.getDefaultMsg(validation).success;
25 | var validCallback = $parse(attrs.validCallback);
26 | var messageId = attrs.messageId;
27 | var validationGroup = attrs.validationGroup;
28 | var messageElem;
29 |
30 | if (messageId || validationGroup) messageElem = angular.element(document.querySelector('#' + (messageId || validationGroup)));
31 | else messageElem = $validationProvider.getMsgElement(element);
32 |
33 | if (element.attr('no-validation-message')) {
34 | messageElem.css('display', 'none');
35 | } else if ($validationProvider.showSuccessMessage && messageToShow) {
36 | messageToShow = angular.isFunction(messageToShow) ? messageToShow(element, attrs, param) : messageToShow;
37 |
38 | messageElem.html('').append($compile($validationProvider.getSuccessHTML(messageToShow, element, attrs))(scope));
39 | messageElem.css('display', '');
40 | } else {
41 | messageElem.css('display', 'none');
42 | }
43 |
44 | ctrl.$setValidity(ctrl.$name, true);
45 | validCallback(scope, {
46 | message: messageToShow
47 | });
48 | if ($validationProvider.validCallback) $validationProvider.validCallback(element);
49 |
50 | return true;
51 | };
52 |
53 |
54 | /**
55 | * Do this function if validation invalid
56 | * @param element
57 | * @param validMessage
58 | * @param validation
59 | * @param callback
60 | * @param ctrl
61 | * @returns {}
62 | */
63 | var invalidFunc = function(element, validMessage, validation, scope, ctrl, attrs, param) {
64 | var messageToShow = validMessage || $validationProvider.getDefaultMsg(validation).error;
65 | var invalidCallback = $parse(attrs.invalidCallback);
66 | var messageId = attrs.messageId;
67 | var validationGroup = attrs.validationGroup;
68 | var messageElem;
69 |
70 | if (messageId || validationGroup) messageElem = angular.element(document.querySelector('#' + (messageId || validationGroup)));
71 | else messageElem = $validationProvider.getMsgElement(element);
72 |
73 | if (element.attr('no-validation-message')) {
74 | messageElem.css('display', 'none');
75 | } else if ($validationProvider.showErrorMessage && messageToShow) {
76 | messageToShow = angular.isFunction(messageToShow) ? messageToShow(element, attrs, param) : messageToShow;
77 |
78 | messageElem.html('').append($compile($validationProvider.getErrorHTML(messageToShow, element, attrs))(scope));
79 | messageElem.css('display', '');
80 | } else {
81 | messageElem.css('display', 'none');
82 | }
83 |
84 | ctrl.$setValidity(ctrl.$name, false);
85 | invalidCallback(scope, {
86 | message: messageToShow
87 | });
88 | if ($validationProvider.invalidCallback) $validationProvider.invalidCallback(element);
89 |
90 | return false;
91 | };
92 |
93 | /**
94 | * Verify whether there is one of the elements inside the group valid.
95 | * If so, it returns true, otherwise, it returns false
96 | *
97 | * @param validationGroup
98 | * @return {boolean}
99 | */
100 | var checkValidationGroup = function(validationGroup) {
101 | var group = groups[validationGroup];
102 |
103 | return Object.keys(group).some(function(key) {
104 | return group[key];
105 | });
106 | };
107 |
108 | /**
109 | * Set validity to all elements inside the given group
110 | *
111 | * @param scope
112 | * @param groupName
113 | * @param validity
114 | */
115 | function setValidationGroup(scope, validationGroup, validity) {
116 | var validationGroupElems = document.querySelectorAll('*[validation-group=' + validationGroup + ']');
117 |
118 | // Loop through all elements inside the group
119 | for (var i = 0, len = validationGroupElems.length; i < len; i++) {
120 | var elem = validationGroupElems[i];
121 | var formName = elem.form.name;
122 | var elemName = elem.name;
123 | scope[formName][elemName].$setValidity(elemName, validity);
124 | }
125 | }
126 |
127 | /**
128 | * collect elements for focus
129 | * @type {Object}
130 | ***private variable
131 | */
132 | var focusElements = {};
133 |
134 | /**
135 | * Get Validation Result Object
136 | * @param data
137 | * @returns {
138 | * result: Boolean, // is success or error
139 | * message: String // tips
140 | * }
141 | */
142 | function getResultObj(data) {
143 | var res = {};
144 | if (data && data.length > 0) {
145 | res = data[0];
146 | if (!angular.isObject(res)) {
147 | res = {
148 | result: res,
149 | message: ''
150 | };
151 | }
152 | } else {
153 | res = {
154 | result: false,
155 | message: ''
156 | };
157 | }
158 | return res;
159 | }
160 |
161 | /**
162 | * Check Validation with Function or RegExp
163 | * @param scope
164 | * @param element
165 | * @param attrs
166 | * @param ctrl
167 | * @param validation
168 | * @param value
169 | * @returns {}
170 | */
171 | var checkValidation = function(scope, element, attrs, ctrl, validation, value) {
172 | var validators = validation.slice(0);
173 | var validatorExpr = validators[0].trim();
174 | var paramIndex = validatorExpr.indexOf('=');
175 | var validator = paramIndex === -1 ? validatorExpr : validatorExpr.substr(0, paramIndex);
176 | var validatorParam = paramIndex === -1 ? null : validatorExpr.substr(paramIndex + 1);
177 | var leftValidation = validators.slice(1);
178 | var successMessage = validator + 'SuccessMessage';
179 | var errorMessage = validator + 'ErrorMessage';
180 | var expression = $validationProvider.getExpression(validator);
181 | var validationGroup = attrs.validationGroup;
182 | var valid = {
183 | success: function(message) {
184 | validFunc(element, message || attrs[successMessage], validator, scope, ctrl, attrs, validatorParam);
185 | if (leftValidation.length) {
186 | return checkValidation(scope, element, attrs, ctrl, leftValidation, value);
187 | } else {
188 | return true;
189 | }
190 | },
191 | error: function(message) {
192 | return invalidFunc(element, message || attrs[errorMessage], validator, scope, ctrl, attrs, validatorParam);
193 | }
194 | };
195 |
196 | if (expression === undefined) {
197 | console.error('You are using undefined validator "%s"', validator);
198 | if (leftValidation.length) return checkValidation(scope, element, attrs, ctrl, leftValidation, value);
199 | else return;
200 | }
201 | // Check with Function
202 | if (expression.constructor === Function) {
203 | return $q.all([$validationProvider.getExpression(validator)(value, scope, element, attrs, validatorParam)])
204 | .then(function(data) {
205 | var resultObj = getResultObj(data);
206 | var message = resultObj.message;
207 | if (resultObj.result) {
208 | if (validationGroup) {
209 | groups[validationGroup][ctrl.$name] = true;
210 | setValidationGroup(scope, validationGroup, true);
211 | }
212 | return valid.success(message);
213 | } else if (validationGroup) {
214 | groups[validationGroup][ctrl.$name] = false;
215 |
216 | // Whenever the element is invalid, we'll check whether one of the elements inside the its group valid or not.
217 | // If there is a valid element, its invalid message won't be shown, Otherwise, shows its invalid message.
218 | if (checkValidationGroup(validationGroup)) {
219 | setValidationGroup(scope, validationGroup, true);
220 | } else {
221 | setValidationGroup(scope, validationGroup, false);
222 | return valid.error(message);
223 | }
224 | } else return valid.error(message);
225 | }, function() {
226 | return valid.error();
227 | });
228 | }
229 |
230 | // Check with RegExp
231 | else if (expression.constructor === RegExp) {
232 | // Only apply the test if the value is neither undefined or null
233 | if (value !== undefined && value !== null) {
234 | if ($validationProvider.getExpression(validator).test(value)) {
235 | if (validationGroup) {
236 | groups[validationGroup][ctrl.$name] = true;
237 | setValidationGroup(scope, validationGroup, true);
238 | }
239 | return valid.success();
240 | } else if (validationGroup) {
241 | groups[validationGroup][ctrl.$name] = false;
242 |
243 | // Whenever the element is invalid, we'll check whether one of the elements inside the its group valid or not.
244 | // If there is a valid element, its invalid message won't be shown, Otherwise, shows its invalid message.
245 | if (checkValidationGroup(validationGroup)) {
246 | setValidationGroup(scope, validationGroup, true);
247 | } else {
248 | setValidationGroup(scope, validationGroup, false);
249 | return valid.error();
250 | }
251 | } else return valid.error();
252 | }
253 | } else return valid.error();
254 | };
255 |
256 | /**
257 | * generate unique guid
258 | */
259 | var s4 = function() {
260 | return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
261 | };
262 | var guid = function() {
263 | return (s4() + s4() + s4() + s4());
264 | };
265 |
266 |
267 | return {
268 | restrict: 'A',
269 | require: 'ngModel',
270 | link: function(scope, element, attrs, ctrl) {
271 | /**
272 | * All attributes
273 | */
274 | var useViewValue = attrs.useViewValue !== 'false';
275 | var validator = attrs.validator;
276 | var messageId = attrs.messageId;
277 | var validationGroup = attrs.validationGroup;
278 | var validMethod = attrs.validMethod;
279 | var ngModel = attrs.ngModel;
280 |
281 | /**
282 | * watch
283 | * @type {watch}
284 | *
285 | * Use to collect scope.$watch method
286 | *
287 | * use watch() to destroy the $watch method
288 | */
289 | var watch = function() {};
290 |
291 | /**
292 | * validator
293 | * @type {Array}
294 | *
295 | * Convert user input String to Array
296 | */
297 | var validation = validator.split(',');
298 |
299 | /**
300 | * guid use
301 | */
302 | var uid = ctrl.validationId = guid();
303 |
304 | /**
305 | * to have a value to rollback to
306 | */
307 | var originalViewValue = null;
308 |
309 | /**
310 | * Set initial validity to undefined if no boolean value is transmitted
311 | */
312 | var initialValidity = void 0;
313 | if (typeof attrs.initialValidity === 'boolean') {
314 | initialValidity = attrs.initialValidity;
315 | }
316 |
317 | /**
318 | * Observe validator changes in order to allow dynamically change it
319 | */
320 | attrs.$observe('validator', function(value) {
321 | validation = value.split(',');
322 | });
323 |
324 | /**
325 | * Set up groups object in order to keep track validation of elements
326 | */
327 | if (validationGroup) {
328 | if (!groups[validationGroup]) groups[validationGroup] = {};
329 | groups[validationGroup][ctrl.$name] = false;
330 | }
331 |
332 | /**
333 | * Default Valid/Invalid Message
334 | */
335 | if (!(messageId || validationGroup)) $validationProvider.addMsgElement(element);
336 |
337 | /**
338 | * Set custom initial validity
339 | * Usage:
340 | */
341 | ctrl.$setValidity(ctrl.$name, initialValidity);
342 |
343 | /**
344 | * Reset the validation for specific form
345 | */
346 | scope.$on(ctrl.$name + 'reset-' + uid, function() {
347 | /**
348 | * clear scope.$watch here
349 | * when reset status
350 | * clear the $watch method to prevent
351 | * $watch again while reset the form
352 | */
353 | watch();
354 |
355 | $timeout(function() {
356 | ctrl.$setViewValue(originalViewValue);
357 | ctrl.$setPristine();
358 | ctrl.$setValidity(ctrl.$name, undefined);
359 | ctrl.$render();
360 | if (messageId || validationGroup) angular.element(document.querySelector('#' + (messageId || validationGroup))).html('');
361 | else $validationProvider.getMsgElement(element).html('');
362 |
363 | if ($validationProvider.resetCallback) $validationProvider.resetCallback(element);
364 | });
365 | });
366 |
367 | /**
368 | * Check validator
369 | */
370 | validMethod = (angular.isUndefined(validMethod)) ? $validationProvider.getValidMethod() : validMethod;
371 |
372 | /**
373 | * Click submit form, check the validity when submit
374 | */
375 | scope.$on(ctrl.$name + 'submit-' + uid, function(event, index) {
376 | var value = useViewValue ? ctrl.$viewValue : ctrl.$modelValue;
377 | var isValid = false;
378 |
379 | isValid = checkValidation(scope, element, attrs, ctrl, validation, value);
380 |
381 | if (validMethod === 'submit') {
382 | // clear previous scope.$watch
383 | watch();
384 | watch = scope.$watch(function() {
385 | return scope.$eval(ngModel);
386 | }, function(value, oldValue) {
387 | // don't watch when init
388 | if (value === oldValue) {
389 | return;
390 | }
391 |
392 | // scope.$watch will translate '' to undefined
393 | // undefined/null will pass the required submit /^.+/
394 | // cause some error in this validation
395 | if (value === undefined || value === null) {
396 | value = '';
397 | }
398 |
399 | isValid = checkValidation(scope, element, attrs, ctrl, validation, value);
400 | });
401 | }
402 |
403 | var setFocus = function(isValid) {
404 | if (isValid) {
405 | delete focusElements[index];
406 | } else {
407 | focusElements[index] = element[0];
408 |
409 | $timeout(function() {
410 | focusElements[Math.min.apply(null, Object.keys(focusElements))].focus();
411 | }, 0);
412 | }
413 | };
414 |
415 | if (isValid instanceof Object) isValid.then(setFocus);
416 | else setFocus(isValid);
417 | });
418 |
419 | /**
420 | * Validate blur method
421 | */
422 | if (validMethod === 'blur') {
423 | element.bind('blur', function() {
424 | var value = scope.$eval(ngModel);
425 |
426 | if (scope.$root.$$phase !== '$apply') {
427 | scope.$apply(function() {
428 | checkValidation(scope, element, attrs, ctrl, validation, value);
429 | });
430 | } else {
431 | checkValidation(scope, element, attrs, ctrl, validation, value);
432 | }
433 | });
434 |
435 | return;
436 | }
437 |
438 | /**
439 | * Validate submit & submit-only method
440 | */
441 | if (validMethod === 'submit' || validMethod === 'submit-only') {
442 | return;
443 | }
444 |
445 | /**
446 | * Validate watch method
447 | * This is the default method
448 | */
449 | scope.$watch(function() {
450 | return scope.$eval(ngModel);
451 | }, function(value) {
452 | /**
453 | * dirty, pristine, viewValue control here
454 | */
455 | if (ctrl.$pristine && ctrl.$viewValue) {
456 | // has value when initial
457 | originalViewValue = ctrl.$viewValue || '';
458 | ctrl.$setViewValue(ctrl.$viewValue);
459 | } else if (ctrl.$pristine) {
460 | // Don't validate form when the input is clean(pristine)
461 | if (messageId || validationGroup) angular.element(document.querySelector('#' + (messageId || validationGroup))).html('');
462 | else $validationProvider.getMsgElement(element).html('');
463 | return;
464 | }
465 | checkValidation(scope, element, attrs, ctrl, validation, value);
466 | });
467 |
468 | $timeout(function() {
469 | /**
470 | * Don't showup the validation Message
471 | */
472 | attrs.$observe('noValidationMessage', function(value) {
473 | var el;
474 | if (messageId || validationGroup) el = angular.element(document.querySelector('#' + (messageId || validationGroup)));
475 | else el = $validationProvider.getMsgElement(element);
476 | if (value === 'true' || value === true) el.css('display', 'none');
477 | else if (value === 'false' || value === false) el.css('display', 'block');
478 | });
479 | });
480 | }
481 | };
482 | }
483 | Validator.$inject = ['$injector'];
484 | }).call(this);
485 |
--------------------------------------------------------------------------------
/test/unit/actualValueNoUseViewValue.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* jasmine specs for provider go here */
4 |
5 | describe('provider without use-view-value', function() {
6 | var $rootScope;
7 | var $compile;
8 | var $scope;
9 | var $timeout;
10 | var element;
11 | var validationProvider;
12 | var myApp;
13 |
14 | beforeEach(function() {
15 | myApp = angular.module('myApp', ['validation', 'validation.rule'])
16 | .config(function($validationProvider) {
17 | validationProvider = $validationProvider;
18 | });
19 |
20 | return myApp;
21 | });
22 |
23 | beforeEach(module('myApp'));
24 |
25 | beforeEach(inject(function($injector) {
26 | $rootScope = $injector.get('$rootScope');
27 | $compile = $injector.get('$compile');
28 | $scope = $rootScope.$new();
29 | $timeout = $injector.get('$timeout');
30 |
31 | element = $compile('')($scope);
32 | angular.element(document.body).append(element);
33 | $scope.$digest();
34 | }));
35 |
36 | it('set value to 123', inject(function() {
37 | var submitSpy = jasmine.createSpy('submitSpy');
38 | var successSpy = jasmine.createSpy('successSpy');
39 | var errorSpy = jasmine.createSpy('errorSpy');
40 |
41 | $scope.$apply(function() {
42 | $scope.number = 123;
43 | });
44 |
45 | $scope.$on('numberWatchsubmit-' + $scope.Form.numberWatch.validationId, function() {
46 | submitSpy();
47 | });
48 |
49 | validationProvider.validate($scope.Form)
50 | .success(function() {
51 | successSpy();
52 | })
53 | .error(function() {
54 | errorSpy();
55 | });
56 |
57 | $timeout.flush();
58 | expect(submitSpy).toHaveBeenCalled();
59 | expect(successSpy).toHaveBeenCalled();
60 | expect(errorSpy).not.toHaveBeenCalled();
61 | }));
62 |
63 | it('set value to 1234567', inject(function() {
64 | var submitSpy = jasmine.createSpy('submitSpy');
65 | var successSpy = jasmine.createSpy('successSpy');
66 | var errorSpy = jasmine.createSpy('errorSpy');
67 |
68 | $scope.$apply(function() {
69 | $scope.number = 1234567;
70 | });
71 |
72 | $scope.$on('numberWatchsubmit-' + $scope.Form.numberWatch.validationId, function() {
73 | submitSpy();
74 | });
75 |
76 | validationProvider.validate($scope.Form)
77 | .success(function() {
78 | successSpy();
79 | })
80 | .error(function() {
81 | errorSpy();
82 | });
83 |
84 | $timeout.flush();
85 | expect(submitSpy).toHaveBeenCalled();
86 | expect(successSpy).not.toHaveBeenCalled();
87 | expect(errorSpy).toHaveBeenCalled();
88 | }));
89 |
90 | it('set value to "ABC"', inject(function() {
91 | var submitSpy = jasmine.createSpy('submitSpy');
92 | var successSpy = jasmine.createSpy('successSpy');
93 | var errorSpy = jasmine.createSpy('errorSpy');
94 |
95 | // expect error because we are using not type="text"
96 | try {
97 | $scope.$apply(function() {
98 | $scope.number = 'ABC';
99 | });
100 | } catch (e) {
101 | errorSpy();
102 | }
103 |
104 | $scope.$on('numberWatchsubmit-' + $scope.Form.numberWatch.validationId, function() {
105 | submitSpy();
106 | });
107 |
108 | expect(submitSpy).not.toHaveBeenCalled();
109 | expect(successSpy).not.toHaveBeenCalled();
110 | expect(errorSpy).toHaveBeenCalled();
111 | }));
112 | });
113 |
--------------------------------------------------------------------------------
/test/unit/actualValueUseViewValue.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* jasmine specs for provider go here */
4 |
5 | describe('provider with use-view-value to false', function() {
6 | var $rootScope;
7 | var $compile;
8 | var $scope;
9 | var $timeout;
10 | var element;
11 | var validationProvider;
12 | var myApp;
13 |
14 | beforeEach(function() {
15 | myApp = angular.module('myApp', ['validation', 'validation.rule'])
16 | .config(function($validationProvider) {
17 | validationProvider = $validationProvider;
18 | });
19 |
20 | return myApp;
21 | });
22 |
23 | beforeEach(module('myApp'));
24 |
25 | beforeEach(inject(function($injector) {
26 | $rootScope = $injector.get('$rootScope');
27 | $compile = $injector.get('$compile');
28 | $scope = $rootScope.$new();
29 | $timeout = $injector.get('$timeout');
30 |
31 | element = $compile('')($scope);
32 | angular.element(document.body).append(element);
33 | $scope.$digest();
34 | }));
35 |
36 | it('set value to 123', inject(function() {
37 | var submitSpy = jasmine.createSpy('submitSpy');
38 | var successSpy = jasmine.createSpy('successSpy');
39 | var errorSpy = jasmine.createSpy('errorSpy');
40 |
41 | $scope.$apply(function() {
42 | $scope.number = 123;
43 | });
44 |
45 | $scope.$on('numberWatchsubmit-' + $scope.Form.numberWatch.validationId, function() {
46 | submitSpy();
47 | });
48 |
49 | validationProvider.validate($scope.Form)
50 | .success(function() {
51 | successSpy();
52 | })
53 | .error(function() {
54 | errorSpy();
55 | });
56 |
57 | $timeout.flush();
58 | expect(submitSpy).toHaveBeenCalled();
59 | expect(successSpy).toHaveBeenCalled();
60 | expect(errorSpy).not.toHaveBeenCalled();
61 | }));
62 |
63 | it('set value to "ABC"', inject(function() {
64 | var submitSpy = jasmine.createSpy('submitSpy');
65 | var successSpy = jasmine.createSpy('successSpy');
66 | var errorSpy = jasmine.createSpy('errorSpy');
67 |
68 | // expect error because we are using not type="text"
69 | try {
70 | $scope.$apply(function() {
71 | $scope.number = 'ABC';
72 | });
73 | } catch (e) {
74 | errorSpy();
75 | }
76 |
77 | $scope.$on('numberWatchsubmit-' + $scope.Form.numberWatch.validationId, function() {
78 | submitSpy();
79 | });
80 |
81 | expect(submitSpy).not.toHaveBeenCalled();
82 | expect(successSpy).not.toHaveBeenCalled();
83 | expect(errorSpy).toHaveBeenCalled();
84 | }));
85 | });
86 |
--------------------------------------------------------------------------------
/test/unit/callbackSpec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* jasmine specs for callback go here */
4 |
5 | describe('callback', function() {
6 | var $rootScope;
7 | var $compile;
8 | var $scope;
9 | var $timeout;
10 | var element;
11 | var validationProvider;
12 | var myApp;
13 |
14 | // spy
15 | var validSpy = null;
16 | var validAttrSpy = null;
17 | var invalidSpy = null;
18 | var invalidAttrSpy = null;
19 | var resetSpy = null;
20 |
21 | beforeEach(function() {
22 | myApp = angular.module('myApp', ['validation', 'validation.rule'])
23 | .config(function($validationProvider) {
24 | validationProvider = $validationProvider;
25 |
26 | validSpy = jasmine.createSpy('validSpy');
27 | validAttrSpy = jasmine.createSpy('validAttrSpy');
28 | invalidSpy = jasmine.createSpy('invalidSpy');
29 | invalidAttrSpy = jasmine.createSpy('invalidAttrSpy');
30 | resetSpy = jasmine.createSpy('resetSpy');
31 |
32 | validationProvider.validCallback = function() {
33 | validSpy();
34 | };
35 | validationProvider.invalidCallback = function() {
36 | invalidSpy();
37 | };
38 | validationProvider.resetCallback = function() {
39 | resetSpy();
40 | };
41 | });
42 |
43 | return myApp;
44 | });
45 |
46 | beforeEach(module('myApp'));
47 |
48 | beforeEach(inject(function($injector) {
49 | $rootScope = $injector.get('$rootScope');
50 | $compile = $injector.get('$compile');
51 | $scope = $rootScope.$new();
52 | $timeout = $injector.get('$timeout');
53 |
54 | $scope.validCallback = function() {
55 | validAttrSpy();
56 | };
57 | $scope.invalidCallback = function() {
58 | invalidAttrSpy();
59 | };
60 |
61 | element = $compile('')($scope);
62 | angular.element(document.body).append(element);
63 | $scope.$digest();
64 | }));
65 |
66 | it('successCallback', inject(function() {
67 |
68 | $scope.$apply(function() {
69 | $scope.required = 'required';
70 | });
71 |
72 | validationProvider.validate($scope.Form);
73 |
74 | $timeout.flush();
75 |
76 | expect(validSpy).toHaveBeenCalled();
77 | expect(validAttrSpy).toHaveBeenCalled();
78 | expect(invalidSpy).not.toHaveBeenCalled();
79 | expect(invalidAttrSpy).not.toHaveBeenCalled();
80 | expect(resetSpy).not.toHaveBeenCalled();
81 | }));
82 |
83 | it('errorCallback', inject(function() {
84 |
85 | $scope.$apply(function() {
86 | $scope.required = '';
87 | });
88 |
89 | validationProvider.validate($scope.Form);
90 |
91 | $timeout.flush();
92 |
93 | expect(validSpy).not.toHaveBeenCalled();
94 | expect(validAttrSpy).not.toHaveBeenCalled();
95 | expect(invalidSpy).toHaveBeenCalled();
96 | expect(invalidAttrSpy).toHaveBeenCalled();
97 | expect(resetSpy).not.toHaveBeenCalled();
98 | }));
99 |
100 | it('resetCallback', inject(function() {
101 |
102 | $scope.$apply(function() {
103 | $scope.required = 'required';
104 | });
105 |
106 | validationProvider.reset($scope.Form);
107 |
108 | $timeout.flush();
109 |
110 | expect(validSpy).not.toHaveBeenCalled();
111 | expect(invalidSpy).not.toHaveBeenCalled();
112 | expect(resetSpy).toHaveBeenCalled();
113 | }));
114 | });
115 |
--------------------------------------------------------------------------------
/test/unit/compileSpec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* jasmine specs for compilation go here */
4 |
5 | describe('Compilation - Example of Required', function() {
6 | var myApp;
7 | var $scope;
8 | var $rootScope;
9 | var $compile;
10 | var $timeout;
11 | var element;
12 |
13 | beforeEach(module('validation.directive'));
14 | beforeEach(module('validation.rule'));
15 |
16 | beforeEach(function() {
17 | myApp = angular.module('myApp', ['validation', 'validation.rule'])
18 | .config(function($validationProvider) {
19 | $validationProvider.setSuccessHTML(function(msg) {
20 | // given the example of using filter
21 | return '{{"' + msg + '"| lowercase}}
';
22 | });
23 | });
24 |
25 | return myApp;
26 | });
27 |
28 | beforeEach(module('myApp'));
29 |
30 | beforeEach(inject(function($injector) {
31 | $rootScope = $injector.get('$rootScope');
32 | $compile = $injector.get('$compile');
33 | $timeout = $injector.get('$timeout');
34 | $scope = $rootScope.$new();
35 |
36 | element = $compile('')($scope);
37 | $scope.$digest();
38 | }));
39 |
40 | it('It should be lowercase', function() {
41 | $scope.Form.required.$setViewValue('Required');
42 | var messageElem = angular.element(element[0].querySelector('p'));
43 | // console.log(messageElem[0].indexOf('it\'s required'));
44 | expect(messageElem.html()).toEqual('it\'s required');
45 | });
46 |
47 | });
48 |
--------------------------------------------------------------------------------
/test/unit/directivesSpec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* jasmine specs for directives go here */
4 |
5 | describe('directives', function() {
6 | var $scope;
7 | var $rootScope;
8 | var $compile;
9 | var $timeout;
10 | var element;
11 |
12 | beforeEach(module('validation.directive'));
13 | beforeEach(module('validation.rule'));
14 |
15 | describe('Example of Required', function() {
16 | beforeEach(inject(function($injector) {
17 | $rootScope = $injector.get('$rootScope');
18 | $compile = $injector.get('$compile');
19 | $timeout = $injector.get('$timeout');
20 | $scope = $rootScope.$new();
21 |
22 | element = $compile('')($scope);
23 | $scope.$digest();
24 | }));
25 |
26 | it('Initial should be pristine and invalid', function() {
27 | expect($scope.Form.$pristine).toBe(true);
28 | expect(element.hasClass('ng-pristine')).toBe(true);
29 | expect($scope.Form.$valid).toBeUndefined(true);
30 | expect($scope.Form.$invalid).toBeUndefined(true);
31 | });
32 |
33 | it('After Input should be dirty, valid, has class "validation-valid"', function() {
34 | $scope.Form.required.$setViewValue('Required');
35 |
36 | expect($scope.Form.$dirty).toBe(true);
37 | expect(element.hasClass('ng-dirty')).toBe(true);
38 | expect($scope.Form.$valid).toBe(true);
39 | expect(element.hasClass('ng-valid')).toBe(true);
40 | expect(element.find('p').hasClass('validation-valid')).toBe(true);
41 | });
42 |
43 | it('Input null should be dirty and invalid (after Input), has class "validation-invalid', function() {
44 | $scope.Form.required.$setViewValue('Required');
45 | $scope.Form.required.$setViewValue('');
46 |
47 | expect($scope.Form.$dirty).toBe(true);
48 | expect(element.hasClass('ng-dirty')).toBe(true);
49 | expect($scope.Form.$invalid).toBe(true);
50 | expect(element.hasClass('ng-invalid')).toBe(true);
51 | expect(element.find('p').hasClass('validation-invalid')).toBe(true);
52 | });
53 |
54 | it('no-validation-message', inject(function() {
55 | var display;
56 | // given no-validation-message="true"
57 | element = $compile('')($scope);
58 | $timeout.flush();
59 | display = element.find('span').css('display');
60 | expect(display).toBe('none');
61 |
62 | // given no-validation-message="false"
63 | element = $compile('')($scope);
64 | $timeout.flush();
65 | display = element.find('span').css('display');
66 | expect(display).toBe('block');
67 |
68 | // given no-validation-message="{{ noValidMessage }}" -> 'true'
69 | element = $compile('')($scope);
70 | $timeout.flush();
71 | $scope.$apply(function() {
72 | $scope.noValidMessage = 'true';
73 | });
74 | display = element.find('span').css('display');
75 | expect(display).toBe('none');
76 |
77 | // given no-validation-message="{{ noValidMessage }}" -> true
78 | $scope.$apply(function() {
79 | $scope.noValidMessage = true;
80 | });
81 | display = element.find('span').css('display');
82 | expect(display).toBe('none');
83 |
84 | // given no-validation-message="{{ noValidMessage }}" -> 'false'
85 | $scope.$apply(function() {
86 | $scope.noValidMessage = 'false';
87 | });
88 | display = element.find('span').css('display');
89 | expect(display).toBe('block');
90 |
91 | // given no-validation-message="{{ noValidMessage }}" -> false
92 | $scope.$apply(function() {
93 | $scope.noValidMessage = false;
94 | });
95 | display = element.find('span').css('display');
96 | expect(display).toBe('block');
97 | }));
98 |
99 | it('given invalid validator', inject(function() {
100 | console.error = function(msg) {
101 | expect(msg).toBe('You are using undefined validator "%s"');
102 | };
103 |
104 | element = $compile('')($scope);
105 | $timeout.flush();
106 |
107 | $scope.$apply(function() {
108 | $scope.required = 'true';
109 | });
110 |
111 | }));
112 | });
113 |
114 | describe('Message-id attribute', function() {
115 | var messageElem;
116 |
117 | beforeEach(inject(function($injector) {
118 | $rootScope = $injector.get('$rootScope');
119 | $compile = $injector.get('$compile');
120 | $timeout = $injector.get('$timeout');
121 | $scope = $rootScope.$new();
122 |
123 | element = $compile('')($scope);
124 | angular.element(document.body).append(element);
125 | $scope.$digest();
126 | }));
127 |
128 | afterEach(function() {
129 | element.remove();
130 | element = null;
131 | });
132 |
133 | it('should be pristine and invalid', function() {
134 | expect($scope.Form.$pristine).toBe(true);
135 | expect(element.hasClass('ng-pristine')).toBe(true);
136 | expect($scope.Form.$valid).toBeUndefined(true);
137 | expect($scope.Form.$invalid).toBeUndefined(true);
138 | });
139 |
140 | it('should be dirty and valid', function() {
141 | $scope.Form.required.$setViewValue('Required');
142 |
143 | expect($scope.Form.$dirty).toBe(true);
144 | expect(element.hasClass('ng-dirty')).toBe(true);
145 | expect($scope.Form.$valid).toBe(true);
146 | expect(element.hasClass('ng-valid')).toBe(true);
147 | });
148 |
149 | it('should be dirty and invalid', function() {
150 | $scope.Form.required.$setViewValue('Required');
151 | $scope.Form.required.$setViewValue('');
152 |
153 | expect($scope.Form.$dirty).toBe(true);
154 | expect(element.hasClass('ng-dirty')).toBe(true);
155 | expect($scope.Form.$invalid).toBe(true);
156 | expect(element.hasClass('ng-invalid')).toBe(true);
157 | });
158 |
159 | it('should have a success message inside the #message element', function() {
160 | $scope.Form.required.$setViewValue('Required');
161 |
162 | messageElem = angular.element(element[0].querySelector('#message > p'));
163 | expect(messageElem.hasClass('validation-valid')).toBe(true);
164 | });
165 |
166 | it('should have an error message inside the #message element', function() {
167 | $scope.Form.required.$setViewValue('Required');
168 | $scope.Form.required.$setViewValue('');
169 |
170 | messageElem = angular.element(element[0].querySelector('#message > p'));
171 | expect(messageElem.hasClass('validation-invalid')).toBe(true);
172 | });
173 | });
174 |
175 | describe('Observing validator changes', function() {
176 | beforeEach(inject(function($injector) {
177 | $rootScope = $injector.get('$rootScope');
178 | $compile = $injector.get('$compile');
179 | $scope = $rootScope.$new();
180 |
181 | $scope.validator = 'required';
182 |
183 | element = $compile('')($scope);
184 | $scope.$digest();
185 | }));
186 |
187 | it('Initial should be pristine and invalid', function() {
188 | expect($scope.Form.$pristine).toBe(true);
189 | expect(element.hasClass('ng-pristine')).toBe(true);
190 | expect($scope.Form.$valid).toBeUndefined(true);
191 | expect($scope.Form.$invalid).toBeUndefined(true);
192 | });
193 |
194 | it('After input should be dirty and valid', function() {
195 | $scope.Form.inputField.$setViewValue('Some text');
196 |
197 | expect($scope.Form.$dirty).toBe(true);
198 | expect(element.hasClass('ng-dirty')).toBe(true);
199 | expect($scope.Form.$valid).toBe(true);
200 | expect(element.hasClass('ng-valid')).toBe(true);
201 | });
202 |
203 | it('When validator changes should be invalid', function() {
204 | $scope.$apply(function() {
205 | $scope.validator = 'required, number';
206 | });
207 |
208 | $scope.Form.inputField.$setViewValue('Some text');
209 |
210 | expect($scope.Form.$valid).toBe(false);
211 | expect(element.hasClass('ng-valid')).toBe(false);
212 | expect($scope.Form.$invalid).toBe(true);
213 | expect(element.hasClass('ng-invalid')).toBe(true);
214 | });
215 | });
216 | });
217 |
--------------------------------------------------------------------------------
/test/unit/providerSpec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* jasmine specs for provider go here */
4 |
5 | describe('provider', function() {
6 | var $rootScope;
7 | var $compile;
8 | var $scope;
9 | var $timeout;
10 | var element;
11 | var validationProvider;
12 | var myApp;
13 |
14 | beforeEach(function() {
15 | myApp = angular.module('myApp', ['validation', 'validation.rule'])
16 | .config(function($validationProvider) {
17 | validationProvider = $validationProvider;
18 | });
19 | return myApp;
20 | });
21 |
22 | beforeEach(module('myApp'));
23 |
24 | beforeEach(inject(function($injector) {
25 | $rootScope = $injector.get('$rootScope');
26 | $compile = $injector.get('$compile');
27 | $scope = $rootScope.$new();
28 | $timeout = $injector.get('$timeout');
29 |
30 | element = $compile('')($scope);
31 | }));
32 |
33 | it('set/get Expression (RegExp or Function)', inject(function() {
34 | var model = {
35 | huei: /^huei$/
36 | };
37 |
38 | validationProvider.setExpression(model);
39 | expect(validationProvider.getExpression('huei')).toEqual(model.huei);
40 |
41 | model = {
42 | huei: function() {
43 | return true;
44 | }
45 | };
46 |
47 | validationProvider.setExpression(model);
48 | expect(validationProvider.getExpression('huei')).toBe(model.huei);
49 | }));
50 |
51 | it('set/get DefaultMsg (String)', inject(function() {
52 | var obj = {
53 | huei: {
54 | error: 'It\'s should be huei',
55 | success: 'It\'s huei'
56 | }
57 | };
58 |
59 | validationProvider.setDefaultMsg(obj);
60 |
61 | expect(validationProvider.getDefaultMsg('huei')).toEqual(obj.huei);
62 | for (var key in validationProvider.getDefaultMsg('huei')) {
63 | expect(key).toMatch(/^error$|^success$/);
64 | expect(validationProvider.getDefaultMsg('huei')[key]).toEqual(obj.huei[key]);
65 | }
66 | }));
67 |
68 | it('set/get DefaultMsg (function)', inject(function() {
69 | var defaultMessages = {
70 | field: {
71 | error: function(element, attrs, param) {
72 | return 'error';
73 | },
74 | success: function(element, attrs, param) {
75 | return 'success';
76 | }
77 | }
78 | };
79 |
80 | validationProvider.setDefaultMsg(defaultMessages);
81 |
82 | expect(validationProvider.getDefaultMsg('field')).toEqual(defaultMessages.field);
83 | for (var key in validationProvider.getDefaultMsg('field')) {
84 | expect(key).toMatch(/^error$|^success$/);
85 | expect(validationProvider.getDefaultMsg('field')[key]).toEqual(defaultMessages.field[key]);
86 | }
87 | }));
88 |
89 | it('defaultMessage (function) error displays expected message', inject(function() {
90 | var messageSpy = jasmine.createSpy('messageSpy');
91 |
92 | validationProvider.setDefaultMsg({
93 | minlength: {
94 | error: function(element, attrs, param) {
95 | messageSpy();
96 | return element.attr('name') + ' ' + attrs.class + ' ' + param;
97 | }
98 | }
99 | });
100 |
101 | var messageElement = $compile('')($scope);
102 | $scope.Form.messagefield.$setViewValue('xxx');
103 | validationProvider.validate($scope.Form);
104 |
105 | var messageSpan = messageElement.find('span');
106 | expect(messageSpan.css('display')).toBe('');
107 | expect(messageSpan.find('p').text()).toBe('messagefield messagefieldclass 5');
108 | expect(messageSpy).toHaveBeenCalled();
109 | }));
110 |
111 | it('defaultMessage (function) success displays expected message', inject(function() {
112 | var messageSpy = jasmine.createSpy('messageSpy');
113 |
114 | validationProvider.setDefaultMsg({
115 | minlength: {
116 | success: function(element, attrs, param) {
117 | messageSpy();
118 | return element.attr('name') + ' ' + attrs.class + ' ' + param;
119 | }
120 | }
121 | });
122 |
123 | var messageElement = $compile('')($scope);
124 | $scope.Form.messagefield.$setViewValue('xxxxxx');
125 | validationProvider.validate($scope.Form);
126 |
127 | var messageSpan = messageElement.find('span');
128 | expect(messageSpan.css('display')).toBe('');
129 | expect(messageSpan.find('p').text()).toBe('messagefield messagefieldclass 5');
130 | expect(messageSpy).toHaveBeenCalled();
131 | }));
132 |
133 | it('set/get successHTML', inject(function() {
134 | validationProvider.setSuccessHTML('sethtml');
135 | expect(validationProvider.getSuccessHTML('true')).not.toEqual('sethtml');
136 | expect(validationProvider.getSuccessHTML('true')).toEqual('true
');
137 |
138 | validationProvider.setSuccessHTML(function(msg) {
139 | return '' + msg + '
';
140 | });
141 |
142 | expect(validationProvider.getSuccessHTML('true')).toEqual('true
');
143 | }));
144 |
145 | it('set/get errorHTML', inject(function() {
146 | validationProvider.setErrorHTML('sethtml');
147 | expect(validationProvider.getErrorHTML('false')).not.toEqual('sethtml');
148 | expect(validationProvider.getErrorHTML('false')).toEqual('false
');
149 |
150 | validationProvider.setErrorHTML(function(msg) {
151 | return '' + msg + '
';
152 | });
153 |
154 | expect(validationProvider.getErrorHTML('error')).toEqual('error
');
155 | }));
156 |
157 | it('checkValid', inject(function() {
158 | expect(validationProvider.checkValid($scope.Form)).toBe(false);
159 | $scope.Form.required.$setViewValue('required');
160 | expect(validationProvider.checkValid($scope.Form)).toBe(true);
161 | $scope.Form.required.$setViewValue('');
162 | expect(validationProvider.checkValid($scope.Form)).toBe(false);
163 | delete $scope.Form;
164 | expect(validationProvider.checkValid($scope.Form)).toBe(false);
165 | expect(validationProvider.checkValid()).toBe(false);
166 | }));
167 |
168 | it('reset', inject(function() {
169 | var resetSpy = jasmine.createSpy('resetSpy');
170 | $scope.$on('requiredreset-' + $scope.Form.required.validationId, function() {
171 | resetSpy();
172 | });
173 | validationProvider.reset($scope.Form);
174 | expect(element.find('p')[0]).toBeUndefined();
175 | expect(resetSpy).toHaveBeenCalled();
176 | }));
177 |
178 | it('validate - submit', inject(function() {
179 | var submitSpy = jasmine.createSpy('submitSpy');
180 | var successSpy = jasmine.createSpy('successSpy');
181 | var errorSpy = jasmine.createSpy('errorSpy');
182 | var submitSpy2 = jasmine.createSpy('submitSpy2');
183 | var successSpy2 = jasmine.createSpy('successSpy2');
184 | var errorSpy2 = jasmine.createSpy('errorSpy2');
185 |
186 | // test .success()
187 | $scope.$on('requiredsubmit-' + $scope.Form.required.validationId, function() {
188 | submitSpy();
189 | });
190 | $scope.$apply(function() {
191 | $scope.required = 'Required';
192 | });
193 | validationProvider.validate($scope.Form)
194 | .success(function() {
195 | successSpy();
196 | })
197 | .error(function() {
198 | errorSpy();
199 | });
200 |
201 | $timeout.flush();
202 | expect(submitSpy).toHaveBeenCalled();
203 | expect(successSpy).toHaveBeenCalled();
204 | expect(errorSpy).not.toHaveBeenCalled();
205 |
206 | // test .error()
207 | $scope.$apply(function() {
208 | $scope.required = '';
209 | });
210 |
211 | $scope.$on('requiredsubmit-' + $scope.Form.required.validationId, function() {
212 | submitSpy2();
213 | });
214 |
215 | validationProvider.validate($scope.Form)
216 | .success(function() {
217 | successSpy2();
218 | })
219 | .error(function() {
220 | errorSpy2();
221 | });
222 |
223 | $timeout.flush();
224 | expect(submitSpy2).toHaveBeenCalled();
225 | expect(successSpy2).not.toHaveBeenCalled();
226 | expect(errorSpy2).toHaveBeenCalled();
227 | }));
228 |
229 | it('validate - single input', inject(function() {
230 | element = $compile('')($scope);
231 |
232 | var submitSpy = jasmine.createSpy('submitSpy');
233 | var submitSpy2 = jasmine.createSpy('submitSpy2');
234 | var successSpy = jasmine.createSpy('successSpy');
235 | var errorSpy = jasmine.createSpy('errorSpy');
236 |
237 | $scope.$on('requiredsubmit-' + $scope.Form.required.validationId, function() {
238 | submitSpy();
239 | });
240 | $scope.$on('required2submit-' + $scope.Form.required2.validationId, function() {
241 | submitSpy2();
242 | });
243 | $scope.$apply(function() {
244 | $scope.required = 'Required';
245 | $scope.required2 = 'Required';
246 | });
247 | validationProvider.validate($scope.Form.required)
248 | .success(function() {
249 | successSpy();
250 | })
251 | .error(function() {
252 | errorSpy();
253 | });
254 |
255 | $timeout.flush();
256 | expect(submitSpy).toHaveBeenCalled();
257 | expect(submitSpy2).not.toHaveBeenCalled();
258 | expect(successSpy).toHaveBeenCalled();
259 | expect(errorSpy).not.toHaveBeenCalled();
260 |
261 | }));
262 |
263 | // TODO - Missing multiple input []
264 |
265 | it('validate invalid form', inject(function() {
266 | console.error = function(msg) {
267 | expect(msg).toBe('This is not a regular Form name scope');
268 | };
269 |
270 | element = $compile('')($scope);
271 | $timeout.flush();
272 |
273 | validationProvider.validate($scope.Form2);
274 |
275 | }));
276 |
277 | it('reset invalid form', inject(function() {
278 | console.error = function(msg) {
279 | expect(msg).toBe('This is not a regular Form name scope');
280 | };
281 |
282 | element = $compile('')($scope);
283 | $timeout.flush();
284 |
285 | validationProvider.reset($scope.Form2);
286 |
287 | }));
288 |
289 | it('set/get validMethod', inject(function() {
290 | expect(validationProvider.getValidMethod()).toEqual(null);
291 |
292 | validationProvider.setValidMethod('submit');
293 |
294 | expect(validationProvider.getValidMethod()).toEqual('submit');
295 | }));
296 |
297 |
298 |
299 | describe('validationProvider.addMsgElement', function() {
300 | /**
301 | * TEST CASE:Check if Default $validationProvider.addMsgElement is defined as a Function
302 | * TEST TYPE: UNIT
303 | * [STEP 1] SET defaultAddMsg = $validationProvider.addMsgElement
304 | * Expected: defaultAddMsg is defined AND (defaultAddMsg instanceof Function) === true
305 | */
306 | it('default value is defined as a Function', inject(function($validation) {
307 | expect($validation.addMsgElement instanceof Function).toBe(true);
308 | })); //END it
309 |
310 |
311 | /**
312 | * TEST CASE:Check if Default $validationProvider.addMsgElement is right after input element
313 | * Test TYPE: E2E
314 | * [STEP 1] SET Up
315 | * Expected: Msg-Element is right after Input element & Total Msg-Element = 1
316 | */
317 | it('default element is right after input element', inject(function($validation) {
318 | var msgElement = element.find('span');
319 | expect(msgElement.length).toEqual(1);
320 |
321 | var elementAfterInput = element.find('input').next();
322 | expect(msgElement[0].isSameNode(elementAfterInput[0])).toBe(true);
323 | })); //END it
324 |
325 |
326 | /**
327 | * TEST CASE:Check if custom $validationProvider.addMsgElement is consistent (do not be modified/injected)
328 | * Test TYPE: E2E
329 | * [STEP 1] SET Up custom addMsgElement method
330 | * [STEP 2] Place validator directive in the UI
331 | * Expected: custom method is not be modified
332 | */
333 | it('custom element is consistent', inject(function($validation) {
334 | var customMsgElement = function(elm) {};
335 | $validation.addMsgElement = customMsgElement;
336 | $compile('')($scope);
337 | expect($validation.addMsgElement).toEqual(customMsgElement);
338 | })); //END it
339 |
340 | /**
341 | * TEST CASE:Check if custom $validationProvider.addMsgElement is placed correctly
342 | * Test TYPE: E2E
343 | * [STEP 1] SET Up custom addMsgElement method to push MsgElement as the first child of "Form" element
344 | * [STEP 2] Place validator directive in the UI
345 | * Expected: MsgElement is placed as the first child element
346 | */
347 | it('custom element is placed correctly', inject(function($validation) {
348 | $validation.addMsgElement = function(elm) {
349 | elm.parent().prepend('
');
350 | };
351 | var formElementForCustomMsgElm = $compile('')($scope);
352 |
353 | var elementFirstChild = formElementForCustomMsgElm.children()[0];
354 | var elementFromSetting = formElementForCustomMsgElm.find('div')[0];
355 |
356 | expect(elementFromSetting.isSameNode(elementFirstChild)).toBe(true);
357 | })); //END it
358 |
359 |
360 | /**
361 | * TEST CASE:Check if custom $validationProvider.addMsgElement received correct parameters
362 | * Test TYPE: E2E
363 | * [STEP 1] SET Up custom addMsgElement
364 | * [STEP 2] Place validator directive in the UI
365 | * Expected: addMsgElement has 01 parameter, parameter data type = DOMElement
366 | */
367 | it('received correct parameters', inject(function($validation) {
368 | $validation.addMsgElement = function() {
369 | expect(arguments.length).toEqual(1);
370 | expect(angular.isElement(arguments[0])).toBe(true);
371 | };
372 | $compile('')($scope);
373 |
374 | })); //END it
375 |
376 |
377 | }); //END describe
378 |
379 | describe('validationProvider.getMsgElement', function() {
380 | /**
381 | * TEST CASE:Check if $validationProvider.getMsgElement is defined as a Function
382 | * TEST TYPE: UNIT
383 | * [STEP 1] SET defaultgetMsg = $validationProvider.getMsgElement
384 | * Expected: (getMsgElement instanceof Function) === true
385 | */
386 | it('default value is defined as a Function', inject(function($validation) {
387 | expect($validation.getMsgElement instanceof Function).toBe(true);
388 | })); //END it
389 |
390 | /**
391 | * TEST CASE:Check if default $validationProvider.getMsgElement is right after input element
392 | * TEST TYPE: E2E
393 | * [STEP 1] SET Up Input A:
394 | * [STEP 2] Get Element B right after Input A
395 | * Expected: (Element B) is (element returned from $validationProvider.getMsgElement() );
396 | */
397 | it('default element is right after input element', inject(function($validation) {
398 | var inputElement = element.find('input');
399 | var elementAfterInput = inputElement.next();
400 | var elementFromSetting = $validation.getMsgElement(inputElement);
401 |
402 | expect(elementFromSetting[0].isSameNode(elementAfterInput[0])).toBe(true);
403 | })); //END it
404 |
405 |
406 | /**
407 | * TEST CASE:Check if custom $validationProvider.getMsgElement is consistent (do not be modified/injected)
408 | * Test TYPE: E2E
409 | * [STEP 1] SET Up custom getMsgElement method
410 | * [STEP 2] Place validator directive in the UI
411 | * Expected: custom method is not be modified
412 | */
413 | it('custom element is consistent', inject(function($validation) {
414 | var customMsgElement = function(elm) {};
415 | $validation.getMsgElement = customMsgElement;
416 | $compile('')($scope);
417 | expect($validation.getMsgElement).toEqual(customMsgElement);
418 | })); //END it
419 |
420 | /**
421 | * TEST CASE:Check if custom $validationProvider.getMsgElement is gotten correctly
422 | * Test TYPE: E2E
423 | * [STEP 1] SET Up custom getMsgElement method to get first child of "Form" element.
424 | * [STEP 2] Place validator directive in the UI
425 | * Expected: MsgElement is placed as the first child element
426 | */
427 | it('custom element is placed correctly', inject(function($validation) {
428 | $validation.getMsgElement = function(elm) {
429 | return formElementForCustomMsgElm.children()[0];
430 | };
431 | var formElementForCustomMsgElm = $compile('')($scope);
432 | var inputElement = formElementForCustomMsgElm.find('input');
433 |
434 | var elementFirstChild = formElementForCustomMsgElm.children()[0];
435 |
436 | var elementFromSetting = $validation.getMsgElement(inputElement);
437 |
438 | expect(elementFromSetting.isSameNode(elementFirstChild)).toBe(true);
439 | })); //END it
440 |
441 | /**
442 | * TEST CASE:Check if custom $validationProvider.getMsgElement received correct parameters
443 | * Test TYPE: E2E
444 | * [STEP 1] SET Up custom getMsgElement
445 | * [STEP 2] Place validator directive in the UI
446 | * Expected: getMsgElement has 01 parameter, parameter data type = DOMElement
447 | */
448 | it('received correct parameters', inject(function($validation) {
449 | $validation.getMsgElement = function() {
450 | expect(arguments.length).toEqual(1);
451 | expect(angular.isElement(arguments[0])).toBe(true);
452 | };
453 | $compile('')($scope);
454 |
455 | })); //END it
456 |
457 | }); //END describe
458 |
459 |
460 | });
461 |
--------------------------------------------------------------------------------
/test/unit/setgetValidMethodSpec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* jasmine specs for provider go here */
4 |
5 | describe('provider', function() {
6 | var $rootScope;
7 | var $compile;
8 | var $scope;
9 | var $timeout;
10 | var element;
11 | var validationProvider;
12 | var myApp;
13 |
14 | beforeEach(function() {
15 | myApp = angular.module('myApp', ['validation', 'validation.rule'])
16 | .config(function($validationProvider) {
17 | validationProvider = $validationProvider;
18 | });
19 | return myApp;
20 | });
21 |
22 | beforeEach(module('myApp'));
23 |
24 | beforeEach(inject(function($injector) {
25 | $rootScope = $injector.get('$rootScope');
26 | $compile = $injector.get('$compile');
27 | $scope = $rootScope.$new();
28 | $timeout = $injector.get('$timeout');
29 |
30 | // set validMethod submit
31 | validationProvider.setValidMethod('submit');
32 |
33 | element = $compile('')($scope);
34 |
35 |
36 | }));
37 |
38 |
39 | it('set validMethod submit', inject(function() {
40 |
41 | $scope.$apply(function() {
42 | $scope.required = 'required';
43 | });
44 |
45 | expect(element.find('p')[0]).toBeUndefined();
46 |
47 | validationProvider.validate($scope.Form);
48 |
49 | // this is important step
50 | $timeout.flush();
51 |
52 | expect(element.find('p')[0]).toBeDefined();
53 | }));
54 | });
55 |
--------------------------------------------------------------------------------
/test/unit/showMessageSpec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* jasmine specs for provider go here */
4 |
5 | describe('provider.showSuccessMessage=false', function() {
6 | var $rootScope;
7 | var $compile;
8 | var $scope;
9 | var $timeout;
10 | var element;
11 | var validationProvider;
12 | var myApp;
13 |
14 | beforeEach(function() {
15 | myApp = angular.module('myApp', ['validation', 'validation.rule'])
16 | .config(function($validationProvider) {
17 | validationProvider = $validationProvider;
18 | validationProvider.showSuccessMessage = false;
19 | });
20 | return myApp;
21 | });
22 |
23 | beforeEach(module('myApp'));
24 |
25 | beforeEach(inject(function($injector) {
26 | $rootScope = $injector.get('$rootScope');
27 | $compile = $injector.get('$compile');
28 | $scope = $rootScope.$new();
29 | $timeout = $injector.get('$timeout');
30 |
31 | element = $compile('')($scope);
32 | }));
33 |
34 | it('showSuccessMessage', inject(function() {
35 | $scope.Form.required.$setViewValue('Required');
36 | var display = element.find('span').css('display');
37 | expect(display).toBe('none');
38 |
39 | $scope.Form.required.$setViewValue('');
40 | display = element.find('span').css('display');
41 | expect(display).toBe('');
42 | }));
43 |
44 | });
45 |
46 | describe('provider.showErrorMessage=false', function() {
47 | var $rootScope;
48 | var $compile;
49 | var $scope;
50 | var $timeout;
51 | var element;
52 | var validationProvider;
53 | var myApp;
54 |
55 | beforeEach(function() {
56 | myApp = angular.module('myApp', ['validation', 'validation.rule'])
57 | .config(function($validationProvider) {
58 | validationProvider = $validationProvider;
59 | validationProvider.showErrorMessage = false;
60 | });
61 | return myApp;
62 | });
63 |
64 | beforeEach(module('myApp'));
65 |
66 | beforeEach(inject(function($injector) {
67 | $rootScope = $injector.get('$rootScope');
68 | $compile = $injector.get('$compile');
69 | $scope = $rootScope.$new();
70 | $timeout = $injector.get('$timeout');
71 |
72 | element = $compile('')($scope);
73 | }));
74 |
75 | it('showErrorMessage', inject(function() {
76 | $scope.Form.required.$setViewValue('Required');
77 | var display = element.find('span').css('display');
78 | expect(display).toBe('');
79 |
80 | $scope.Form.required.$setViewValue('');
81 | display = element.find('span').css('display');
82 | expect(display).toBe('none');
83 | }));
84 |
85 | });
86 |
87 | // no-validation-message should has higher priority than showErrorMessage/showSuccessMessage
88 | describe('provider.showErrorMessage=false with no-validation-message', function() {
89 | var $rootScope;
90 | var $compile;
91 | var $scope;
92 | var $timeout;
93 | var element;
94 | var validationProvider;
95 | var myApp;
96 |
97 | beforeEach(function() {
98 | myApp = angular.module('myApp', ['validation', 'validation.rule'])
99 | .config(function($validationProvider) {
100 | validationProvider = $validationProvider;
101 | validationProvider.showErrorMessage = true;
102 | validationProvider.showSuccessMessage = true;
103 | });
104 | return myApp;
105 | });
106 |
107 | beforeEach(module('myApp'));
108 |
109 | beforeEach(inject(function($injector) {
110 | $rootScope = $injector.get('$rootScope');
111 | $compile = $injector.get('$compile');
112 | $scope = $rootScope.$new();
113 | $timeout = $injector.get('$timeout');
114 |
115 | element = $compile('')($scope);
116 | }));
117 |
118 | it('showErrorMessage', inject(function() {
119 | $scope.Form.required.$setViewValue('Required');
120 | var display = element.find('span').css('display');
121 | expect(display).toBe('none');
122 |
123 | $scope.Form.required.$setViewValue('');
124 | display = element.find('span').css('display');
125 | expect(display).toBe('none');
126 | }));
127 |
128 | });
129 |
--------------------------------------------------------------------------------
/test/unit/validationGroupSpec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('validation-group directive', function() {
4 | var $scope;
5 | var $rootScope;
6 | var $compile;
7 | var $timeout;
8 | var validationProvider;
9 | var element;
10 |
11 | beforeEach(module('validation.directive'));
12 | beforeEach(module('validation.rule'));
13 |
14 | describe('validation-group attribute for checkbox elements', function() {
15 | var messageElem;
16 |
17 | beforeEach(inject(function($injector) {
18 | $rootScope = $injector.get('$rootScope');
19 | $compile = $injector.get('$compile');
20 | $scope = $rootScope.$new();
21 |
22 | element = $compile('')($scope);
23 | angular.element(document.body).append(element);
24 | $scope.$digest();
25 | }));
26 |
27 | afterEach(function() {
28 | element.remove();
29 | element = null;
30 | });
31 |
32 | it('should be pristine', function() {
33 | expect($scope.Form.$pristine).toBe(true);
34 | expect(element.hasClass('ng-pristine')).toBe(true);
35 | expect($scope.Form.$valid).toBeUndefined(true);
36 | expect($scope.Form.$invalid).toBeUndefined(true);
37 | });
38 |
39 | it('should be dirty and valid', function() {
40 | $scope.Form.checkbox1.$setViewValue(true);
41 | $scope.Form.checkbox2.$setViewValue(true);
42 |
43 | expect($scope.Form.$dirty).toBe(true);
44 | expect(element.hasClass('ng-dirty')).toBe(true);
45 | expect($scope.Form.$valid).toBe(true);
46 | expect(element.hasClass('ng-valid')).toBe(true);
47 | });
48 |
49 | it('should be dirty and invalid', function() {
50 | $scope.Form.checkbox1.$setViewValue(true);
51 | $scope.Form.checkbox2.$setViewValue(true);
52 | $scope.Form.checkbox1.$setViewValue(false);
53 | $scope.Form.checkbox2.$setViewValue(false);
54 |
55 | expect($scope.Form.$dirty).toBe(true);
56 | expect(element.hasClass('ng-dirty')).toBe(true);
57 | expect($scope.Form.$invalid).toBe(true);
58 | expect(element.hasClass('ng-invalid')).toBe(true);
59 | });
60 |
61 | it('should be valid when at least one of elements is valid', function() {
62 | $scope.Form.checkbox1.$setViewValue(true);
63 |
64 | expect($scope.Form.$valid).toBe(true);
65 | expect(element.hasClass('ng-valid')).toBe(true);
66 |
67 | $scope.Form.checkbox1.$setViewValue(false);
68 | expect($scope.Form.$valid).toBe(false);
69 | expect(element.hasClass('ng-invalid')).toBe(true);
70 |
71 | $scope.Form.checkbox2.$setViewValue(true);
72 | expect($scope.Form.$valid).toBe(true);
73 | expect(element.hasClass('ng-valid')).toBe(true);
74 | });
75 |
76 | it('should have a success message inside the #checkbox element when an element is valid', function() {
77 | $scope.Form.checkbox1.$setViewValue(true);
78 |
79 | messageElem = angular.element(element[0].querySelector('#checkbox > p'));
80 | expect(messageElem.hasClass('validation-valid')).toBe(true);
81 | });
82 |
83 | it('should have an error message inside the #checkbox element when no element is valid', function() {
84 | $scope.Form.checkbox1.$setViewValue(true);
85 | $scope.Form.checkbox1.$setViewValue(false);
86 |
87 | messageElem = angular.element(element[0].querySelector('#checkbox > p'));
88 | expect(messageElem.hasClass('validation-invalid')).toBe(true);
89 | });
90 |
91 | it('should have a success message inside the #checkbox element when both elements are valid', function() {
92 | $scope.Form.checkbox1.$setViewValue(true);
93 | $scope.Form.checkbox2.$setViewValue(true);
94 |
95 | messageElem = angular.element(element[0].querySelector('#checkbox > p'));
96 | expect(messageElem.hasClass('validation-valid')).toBe(true);
97 | });
98 |
99 | it('should have a success message inside the #checkbox element when one of element is valid', function() {
100 | $scope.Form.checkbox1.$setViewValue(true);
101 | $scope.Form.checkbox2.$setViewValue(true);
102 | $scope.Form.checkbox1.$setViewValue(false);
103 |
104 | messageElem = angular.element(element[0].querySelector('#checkbox > p'));
105 | expect(messageElem.hasClass('validation-valid')).toBe(true);
106 | });
107 |
108 | it('should have an error message inside the #checkbox element when both of elements are invalid', function() {
109 | $scope.Form.checkbox1.$setViewValue(true);
110 | $scope.Form.checkbox2.$setViewValue(true);
111 | $scope.Form.checkbox1.$setViewValue(false);
112 | $scope.Form.checkbox2.$setViewValue(false);
113 |
114 | messageElem = angular.element(element[0].querySelector('#checkbox > p'));
115 | expect(messageElem.hasClass('validation-invalid')).toBe(true);
116 | });
117 | });
118 |
119 | describe('validation-group attribute for any elements', function() {
120 | var messageElem;
121 |
122 | beforeEach(inject(function($injector) {
123 | $rootScope = $injector.get('$rootScope');
124 | $compile = $injector.get('$compile');
125 | $scope = $rootScope.$new();
126 |
127 | element = $compile('')($scope);
128 | angular.element(document.body).append(element);
129 | $scope.$digest();
130 | }));
131 |
132 | afterEach(function() {
133 | element.remove();
134 | element = null;
135 | });
136 |
137 | it('should be pristine', function() {
138 | expect($scope.Form.$pristine).toBe(true);
139 | expect(element.hasClass('ng-pristine')).toBe(true);
140 | expect($scope.Form.$valid).toBeUndefined(true);
141 | expect($scope.Form.$invalid).toBeUndefined(true);
142 | });
143 |
144 | it('should be dirty and valid', function() {
145 | $scope.Form.email.$setViewValue('foo@bar.com');
146 | $scope.Form.telephone.$setViewValue('065839481');
147 |
148 | expect($scope.Form.$dirty).toBe(true);
149 | expect(element.hasClass('ng-dirty')).toBe(true);
150 | expect($scope.Form.$valid).toBe(true);
151 | expect(element.hasClass('ng-valid')).toBe(true);
152 | });
153 |
154 | it('should be dirty and invalid', function() {
155 | $scope.Form.email.$setViewValue('foo@bar.com');
156 | $scope.Form.telephone.$setViewValue('065839481');
157 | $scope.Form.email.$setViewValue('');
158 | $scope.Form.telephone.$setViewValue('');
159 |
160 | expect($scope.Form.$dirty).toBe(true);
161 | expect(element.hasClass('ng-dirty')).toBe(true);
162 | expect($scope.Form.$invalid).toBe(true);
163 | expect(element.hasClass('ng-invalid')).toBe(true);
164 | });
165 |
166 | it('should be valid when at least one of elements is valid', function() {
167 | $scope.Form.email.$setViewValue('foo@bar.com');
168 | expect($scope.Form.$valid).toBe(true);
169 | expect(element.hasClass('ng-valid')).toBe(true);
170 |
171 | $scope.Form.telephone.$setViewValue('065839481');
172 | $scope.Form.email.$setViewValue('');
173 | expect($scope.Form.$valid).toBe(true);
174 | expect(element.hasClass('ng-valid')).toBe(true);
175 |
176 | $scope.Form.telephone.$setViewValue('');
177 | expect($scope.Form.$valid).toBe(false);
178 | expect(element.hasClass('ng-invalid')).toBe(true);
179 | });
180 |
181 |
182 | it('should have a success message inside the #contact element when an element is valid', function() {
183 | $scope.Form.email.$setViewValue('foo@bar.com');
184 |
185 | messageElem = angular.element(element[0].querySelector('#contact > p'));
186 | expect(messageElem.hasClass('validation-valid')).toBe(true);
187 | });
188 |
189 | it('should have an error message inside the #contact element when no element is valid', function() {
190 | $scope.Form.email.$setViewValue('foo@bar.com');
191 | $scope.Form.email.$setViewValue('');
192 |
193 | messageElem = angular.element(element[0].querySelector('#contact > p'));
194 | expect(messageElem.hasClass('validation-invalid')).toBe(true);
195 | });
196 |
197 | it('should have a success message inside the #contact element when both elements are valid', function() {
198 | $scope.Form.email.$setViewValue('foo@bar.com');
199 | $scope.Form.telephone.$setViewValue('065839481');
200 |
201 | messageElem = angular.element(element[0].querySelector('#contact > p'));
202 | expect(messageElem.hasClass('validation-valid')).toBe(true);
203 | });
204 |
205 | it('should have a success message inside the #contact element when one of element is valid', function() {
206 | $scope.Form.email.$setViewValue('foo@bar.com');
207 | $scope.Form.telephone.$setViewValue('065839481');
208 | $scope.Form.email.$setViewValue('');
209 |
210 | messageElem = angular.element(element[0].querySelector('#contact > p'));
211 | expect(messageElem.hasClass('validation-valid')).toBe(true);
212 | });
213 |
214 | it('should have an error message inside the #contact element when both of elements are invalid', function() {
215 | $scope.Form.email.$setViewValue('foo@bar.com');
216 | $scope.Form.telephone.$setViewValue('065839481');
217 | $scope.Form.email.$setViewValue('');
218 | $scope.Form.telephone.$setViewValue('');
219 |
220 | messageElem = angular.element(element[0].querySelector('#contact > p'));
221 | expect(messageElem.hasClass('validation-invalid')).toBe(true);
222 | });
223 | });
224 |
225 | describe('validation-group attribute validated by using the provider', function() {
226 | var successSpy;
227 | var errorSpy;
228 | beforeEach(inject(function($injector) {
229 | $rootScope = $injector.get('$rootScope');
230 | $compile = $injector.get('$compile');
231 | validationProvider = $injector.get('$validation');
232 | $timeout = $injector.get('$timeout');
233 | $scope = $rootScope.$new();
234 |
235 | element = $compile('')($scope);
236 | angular.element(document.body).append(element);
237 | $scope.$digest();
238 | }));
239 |
240 | afterEach(function() {
241 | element.remove();
242 | element = null;
243 | });
244 |
245 | it('should validate a form and call a success callback', function() {
246 | successSpy = jasmine.createSpy('successSpy');
247 | errorSpy = jasmine.createSpy('errorSpy');
248 |
249 | $scope.Form.checkbox1.$setViewValue(true);
250 | $scope.Form.checkbox2.$setViewValue(true);
251 |
252 | validationProvider.validate($scope.Form)
253 | .success(function() {
254 | successSpy();
255 | })
256 | .error(function() {
257 | errorSpy();
258 | });
259 | $timeout.flush();
260 | expect(successSpy).toHaveBeenCalled();
261 | expect(errorSpy).not.toHaveBeenCalled();
262 | });
263 |
264 | it('should validate a form and call a success callback when at least one of elements in the form is valid', function() {
265 | successSpy = jasmine.createSpy('successSpy');
266 | errorSpy = jasmine.createSpy('errorSpy');
267 |
268 | $scope.Form.checkbox1.$setViewValue(true);
269 |
270 | validationProvider.validate($scope.Form)
271 | .success(function() {
272 | successSpy();
273 | })
274 | .error(function() {
275 | errorSpy();
276 | });
277 | $timeout.flush();
278 | expect(successSpy).toHaveBeenCalled();
279 | expect(errorSpy).not.toHaveBeenCalled();
280 | });
281 |
282 | it('should validate a form and call an error callback when all elements are invalid', function() {
283 | successSpy = jasmine.createSpy('successSpy');
284 | errorSpy = jasmine.createSpy('errorSpy');
285 |
286 | $scope.Form.checkbox1.$setViewValue(true);
287 | $scope.Form.checkbox2.$setViewValue(true);
288 | $scope.Form.checkbox1.$setViewValue(false);
289 | $scope.Form.checkbox2.$setViewValue(false);
290 |
291 | validationProvider.validate($scope.Form)
292 | .success(function() {
293 | successSpy();
294 | })
295 | .error(function() {
296 | errorSpy();
297 | });
298 | $timeout.flush();
299 | expect(successSpy).not.toHaveBeenCalled();
300 | expect(errorSpy).toHaveBeenCalled();
301 | });
302 |
303 | it('should validate a form element and call a success callback', function() {
304 | successSpy = jasmine.createSpy('successSpy');
305 | errorSpy = jasmine.createSpy('errorSpy');
306 |
307 | $scope.Form.checkbox1.$setViewValue(true);
308 |
309 | validationProvider.validate($scope.Form.checkbox1)
310 | .success(function() {
311 | successSpy();
312 | })
313 | .error(function() {
314 | errorSpy();
315 | });
316 | $timeout.flush();
317 | expect(successSpy).toHaveBeenCalled();
318 | expect(errorSpy).not.toHaveBeenCalled();
319 | });
320 |
321 | it('should validate a form element and call an error callback', function() {
322 | successSpy = jasmine.createSpy('successSpy');
323 | errorSpy = jasmine.createSpy('errorSpy');
324 |
325 | $scope.Form.checkbox1.$setViewValue(true);
326 | $scope.Form.checkbox1.$setViewValue(false);
327 |
328 | validationProvider.validate($scope.Form)
329 | .success(function() {
330 | successSpy();
331 | })
332 | .error(function() {
333 | errorSpy();
334 | });
335 | $timeout.flush();
336 | expect(successSpy).not.toHaveBeenCalled();
337 | expect(errorSpy).toHaveBeenCalled();
338 | });
339 | });
340 |
341 | describe('validation-group attribute validated by using the provider with expression dynamic return messages using functions. ', function() {
342 | var successSpy;
343 | var errorSpy;
344 | beforeEach(inject(function($injector) {
345 | $rootScope = $injector.get('$rootScope');
346 | $compile = $injector.get('$compile');
347 | validationProvider = $injector.get('$validation');
348 | var expression = {
349 | kuaisheng: function(value, scope, element, attrs, param) {
350 | var errorStr = [
351 | 'errorStr1',
352 | 'errorStr2'
353 | ];
354 | var len = errorStr.length;
355 | for (var i = len - 1; i >= 0; i--) {
356 | if (value.indexOf(errorStr[i]) > -1) {
357 | return {
358 | result: false,
359 | message: 'input should not include ' + errorStr[i]
360 | };
361 | }
362 | }
363 |
364 | return {
365 | result: true,
366 | message: ''
367 | };
368 | }
369 | };
370 | var defaultMsg = {
371 | kuaisheng: {
372 | error: 'valid is error'
373 | }
374 | };
375 | validationProvider.setExpression(expression).setDefaultMsg(defaultMsg);
376 |
377 | $timeout = $injector.get('$timeout');
378 | $scope = $rootScope.$new();
379 |
380 | element = $compile('')($scope);
381 | angular.element(document.body).append(element);
382 | $scope.$digest();
383 | }));
384 |
385 | afterEach(function() {
386 | element.remove();
387 | element = null;
388 | });
389 |
390 | it('should validate a form and error message be dynamic', function() {
391 | successSpy = jasmine.createSpy('successSpy');
392 | errorSpy = jasmine.createSpy('errorSpy');
393 |
394 | $scope.Form.kuaisheng.$setViewValue('asberrorStr1dd');
395 |
396 |
397 | validationProvider.validate($scope.Form)
398 | .success(function() {
399 | successSpy();
400 | })
401 | .error(function() {
402 | errorSpy();
403 | });
404 | $timeout.flush();
405 | var messageElem = angular.element(element[0].querySelector('p'));
406 | expect(messageElem.html()).toEqual('input should not include errorStr1');
407 |
408 | });
409 | });
410 | });
411 |
--------------------------------------------------------------------------------
/test/unit/validationResetSpec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* jasmine specs for validationReset go here */
4 |
5 | describe('validation-reset SPEC', function() {
6 | var myApp;
7 | var $scope;
8 | var $rootScope;
9 | var $compile;
10 | var $timeout;
11 | var element;
12 |
13 | beforeEach(module('validation.directive'));
14 | beforeEach(module('validation.rule'));
15 |
16 | beforeEach(function() {
17 | myApp = angular.module('myApp', ['validation', 'validation.rule'])
18 | .config(function($validationProvider) {
19 | $validationProvider.setSuccessHTML(function(msg) {
20 | // given the example of using filter
21 | return '{{"' + msg + '"| lowercase}}
';
22 | });
23 | });
24 |
25 | return myApp;
26 | });
27 |
28 | beforeEach(module('myApp'));
29 |
30 | beforeEach(inject(function($injector) {
31 | $rootScope = $injector.get('$rootScope');
32 | $compile = $injector.get('$compile');
33 | $timeout = $injector.get('$timeout');
34 | $scope = $rootScope.$new();
35 |
36 | element = $compile('')($scope);
37 | $scope.$digest();
38 | }));
39 |
40 | it('Form should be reset', function() {
41 | $scope.Form.required.$setViewValue('Required');
42 | var messageElem = angular.element(element[0].querySelector('button'));
43 |
44 | $timeout(function() {
45 | angular.element(messageElem).triggerHandler('click');
46 | }, 0);
47 | // I'm not sure why we need flush again
48 | $timeout.flush();
49 |
50 | expect(angular.element(element[0].querySelector('span')).html()).toEqual('');
51 | });
52 |
53 | });
54 |
--------------------------------------------------------------------------------
/test/unit/validationSubmitSpec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* jasmine specs for validationReset go here */
4 |
5 | describe('validation-reset SPEC', function() {
6 | var myApp;
7 | var $scope;
8 | var $rootScope;
9 | var $compile;
10 | var $timeout;
11 | var element;
12 | var callbackSpy = jasmine.createSpy('callbackSpy');
13 |
14 | beforeEach(module('validation.directive'));
15 | beforeEach(module('validation.rule'));
16 |
17 | beforeEach(function() {
18 | myApp = angular.module('myApp', ['validation', 'validation.rule'])
19 | .config(function($validationProvider) {
20 | $validationProvider.setSuccessHTML(function(msg) {
21 | // given the example of using filter
22 | return '{{"' + msg + '"| lowercase}}
';
23 | });
24 | });
25 |
26 | return myApp;
27 | });
28 |
29 | beforeEach(module('myApp'));
30 |
31 | beforeEach(inject(function($injector) {
32 | $rootScope = $injector.get('$rootScope');
33 | $compile = $injector.get('$compile');
34 | $timeout = $injector.get('$timeout');
35 | $scope = $rootScope.$new();
36 | $scope.next = function() {
37 | callbackSpy();
38 | };
39 | element = $compile('')($scope);
40 | $scope.$digest();
41 | }));
42 |
43 | it('Submit with trigger ng-click', function() {
44 | $scope.Form.required.$setViewValue('Required');
45 | var messageElem = angular.element(element[0].querySelector('button'));
46 |
47 | $timeout(function() {
48 | angular.element(messageElem).triggerHandler('click');
49 | }, 0);
50 | // I'm not sure why we need flush again
51 | $timeout.flush();
52 |
53 | expect(callbackSpy).toHaveBeenCalled();
54 | });
55 |
56 | });
57 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | entry: ['./src/module.js'],
3 | output: {
4 | filename: 'dist/bundle.js'
5 | },
6 | module: {
7 | loaders: [{
8 | test: /\.jsx?$/,
9 | exclude: /(node_modules|bower_components)/,
10 | loader: 'babel',
11 | query: {
12 | presets: ['es2015']
13 | }
14 | }]
15 | }
16 | };
17 |
--------------------------------------------------------------------------------