PageSourceSearch

https://vatechamerica.com/training/js/angular-messages.js

js vatechamerica.com collected 2026-10-04 13:20:28 UTC 25,331 bytes, 678 lines download raw bytes

1/**
2 * @license AngularJS v1.4.0
3 * (c) 2010-2015 Google, Inc. http://angularjs.org
4 * License: MIT
5 */
6(function(window, angular, undefined) {'use strict';
7
8/* jshint ignore:start */
9// this code is in the core, but not in angular-messages.js
10var isArray = angular.isArray;
11var forEach = angular.forEach;
12var isString = angular.isString;
13var jqLite = angular.element;
14/* jshint ignore:end */
15
16/**
17 * @ngdoc module
18 * @name ngMessages
19 * @description
20 *
21 * The `ngMessages` module provides enhanced support for displaying messages within templates
22 * (typically within forms or when rendering message objects that return key/value data).
23 * Instead of relying on JavaScript code and/or complex ng-if statements within your form template to
24 * show and hide error messages specific to the state of an input field, the `ngMessages` and
25 * `ngMessage` directives are designed to handle the complexity, inheritance and priority
26 * sequencing based on the order of how the messages are defined in the template.
27 *
28 * Currently, the ngMessages module only contains the code for the `ngMessages`, `ngMessagesInclude`
29 * `ngMessage` and `ngMessageExp` directives.
30 *
31 * # Usage
32 * The `ngMessages` directive listens on a key/value collection which is set on the ngMessages attribute.
33 * Since the {@link ngModel ngModel} directive exposes an `$error` object, this error object can be
34 * used with `ngMessages` to display control error messages in an easier way than with just regular angular
35 * template directives.
36 *
37 * ```html
38 * <form name="myForm">
39 *   <label>
40 *     Enter text:
41 *     <input type="text" ng-model="field" name="myField" required minlength="5" />
42 *   </label>
43 *   <div ng-messages="myForm.myField.$error" role="alert">
44 *     <div ng-message="required">You did not enter a field</div>
45 *     <div ng-message="minlength, maxlength">
46 *       Your email must be between 5 and 100 characters long
47 *     </div>
48 *   </div>
49 * </form>
50 * ```
51 *
52 * Now whatever key/value entries are present within the provided object (in this case `$error`) then
53 * the ngMessages directive will render the inner first ngMessage directive (depending if the key values
54 * match the attribute value present on each ngMessage directive). In other words, if your errors
55 * object contains the following data:
56 *
57 * ```javascript
58 * <!-- keep in mind that ngModel automatically sets these error flags -->
59 * myField.$error = { minlength : true, required : false };
60 * ```
61 *
62 * Then the `required` message will be displayed first. When required is false then the `minlength` message
63 * will be displayed right after (since these messages are ordered this way in the template HTML code).
64 * The prioritization of each message is determined by what order they're present in the DOM.
65 * Therefore, instead of having custom JavaScript code determine the priority of what errors are
66 * present before others, the presentation of the errors are handled within the template.
67 *
68 * By default, ngMessages will only display one error at a time. However, if you wish to display all
69 * messages then the `ng-messages-multiple` attribute flag can be used on the element containing the
70 * ngMessages directive to make this happen.
71 *
72 * ```html
73 * <!-- attribute-style usage -->
74 * <div ng-messages="myForm.myField.$error" ng-messages-multiple>...</div>
75 *
76 * <!-- element-style usage -->
77 * <ng-messages for="myForm.myField.$error" multiple>...</ng-messages>
78 * ```
79 *
80 * ## Reusing and Overriding Messages
81 * In addition to prioritization, ngMessages also allows for including messages from a remote or an inline
82 * template. This allows for generic collection of messages to be reused across multiple parts of an
83 * application.
84 *
85 * ```html
86 * <script type="text/ng-template" id="error-messages">
87 *   <div ng-message="required">This field is required</div>
88 *   <div ng-message="minlength">This field is too short</div>
89 * </script>
90 *
91 * <div ng-messages="myForm.myField.$error" role="alert">
92 *   <div ng-messages-include="error-messages"></div>
93 * </div>
94 * ```
95 *
96 * However, including generic messages may not be useful enough to match all input fields, therefore,
97 * `ngMessages` provides the ability to override messages defined in the remote template by redefining
98 * them within the directive container.
99 *
100 * ```html
101 * <!-- a generic template of error messages known as "my-custom-messages" -->
102 * <script type="text/ng-template" id="my-custom-messages">
103 *   <div ng-message="required">This field is required</div>
104 *   <div ng-message="minlength">This field is too short</div>
105 * </script>
106 *
107 * <form name="myForm">
108 *   <label>
109 *     Email address
110 *     <input type="email"
111 *            id="email"
112 *            name="myEmail"
113 *            ng-model="email"
114 *            minlength="5"
115 *            required />
116 *   </label>
117 *   <!-- any ng-message elements that appear BEFORE the ng-messages-include will
118 *        override the messages present in the ng-messages-include template -->
119 *   <div ng-messages="myForm.myEmail.$error" role="alert">
120 *     <!-- this required message has overridden the template message -->
121 *     <div ng-message="required">You did not enter your email address</div>
122 *
123 *     <!-- this is a brand new message and will appear last in the prioritization -->
124 *     <div ng-message="email">
124Your email address is invalid</div>
125 *
126 *     <!-- and here are the generic error messages -->
127 *     <div ng-messages-include="my-custom-messages"></div>
128 *   </div>
129 * </form>
130 * ```
131 *
132 * In the example HTML code above the message that is set on required will override the corresponding
133 * required message defined within the remote template. Therefore, with particular input fields (such
134 * email addresses, date fields, autocomplete inputs, etc...), specialized error messages can be applied
135 * while more generic messages can be used to handle other, more general input errors.
136 *
137 * ## Dynamic Messaging
138 * ngMessages also supports using expressions to dynamically change key values. Using arrays and
139 * repeaters to list messages is also supported. This means that the code below will be able to
140 * fully adapt itself and display the appropriate message when any of the expression data changes:
141 *
142 * ```html
143 * <form name="myForm">
144 *   <label>
145 *     Email address
146 *     <input type="email"
147 *            name="myEmail"
148 *            ng-model="email"
149 *            minlength="5"
150 *            required />
151 *   </label>
152 *   <div ng-messages="myForm.myEmail.$error" role="alert">
153 *     <div ng-message="required">You did not enter your email address</div>
154 *     <div ng-repeat="errorMessage in errorMessages">
155 *       <!-- use ng-message-exp for a message whose key is given by an expression -->
156 *       <div ng-message-exp="errorMessage.type">{{ errorMessage.text }}</div>
157 *     </div>
158 *   </div>
159 * </form>
160 * ```
161 *
162 * The `errorMessage.type` expression can be a string value or it can be an array so
163 * that multiple errors can be associated with a single error message:
164 *
165 * ```html
166 *   <label>
167 *     Email address
168 *     <input type="email"
169 *            ng-model="data.email"
170 *            name="myEmail"
171 *            ng-minlength="5"
172 *            ng-maxlength="100"
173 *            required />
174 *   </label>
175 *   <div ng-messages="myForm.myEmail.$error" role="alert">
176 *     <div ng-message-exp="'required'">You did not enter your email address</div>
177 *     <div ng-message-exp="['minlength', 'maxlength']">
178 *       Your email must be between 5 and 100 characters long
179 *     </div>
180 *   </div>
181 * ```
182 *
183 * Feel free to use other structural directives such as ng-if and ng-switch to further control
184 * what messages are active and when. Be careful, if you place ng-message on the same element
185 * as these structural directives, Angular may not be able to determine if a message is active
186 * or not. Therefore it is best to place the ng-message on a child element of the structural
187 * directive.
188 *
189 * ```html
190 * <div ng-messages="myForm.myEmail.$error" role="alert">
191 *   <div ng-if="showRequiredError">
192 *     <div ng-message="required">Please enter something</div>
193 *   </div>
194 * </div>
195 * ```
196 *
197 * ## Animations
198 * If the `ngAnimate` module is active within the application then the `ngMessages`, `ngMessage` and
199 * `ngMessageExp` directives will trigger animations whenever any messages are added and removed from
200 * the DOM by the `ngMessages` directive.
201 *
202 * Whenever the `ngMessages` directive contains one or more visible messages then the `.ng-active` CSS
203 * class will be added to the element. The `.ng-inactive` CSS class will be applied when there are no
204 * messages present. Therefore, CSS transitions and keyframes as well as JavaScript animations can
205 * hook into the animations whenever these classes are added/removed.
206 *
207 * Let's say that our HTML code for our messages container looks like so:
208 *
209 * ```html
210 * <div ng-messages="myMessages" class="my-messages" role="alert">
211 *   <div ng-message="alert" class="some-message">...</div>
212 *   <div ng-message="fail" class="some-message">...</div>
213 * </div>
214 * ```
215 *
216 * Then the CSS animation code for the message container looks like so:
217 *
218 * ```css
219 * .my-messages {
220 *   transition:1s linear all;
221 * }
222 * .my-messages.ng-active {
223 *   // messages are visible
224 * }
225 * .my-messages.ng-inactive {
226 *   // messages are hidden
227 * }
228 * ```
229 *
230 * Whenever an inner message is attached (becomes visible) or removed (becomes hidden) then the enter
231 * and leave animation is triggered for each particular element bound to the `ngMessage` directive.
232 *
233 * Therefore, the CSS code for the inner messages looks like so:
234 *
235 * ```css
236 * .some-message {
237 *   transition:1s linear all;
238 * }
239 *
240 * .some-message.ng-enter {}
241 * .some-message.ng-enter.ng-enter-active {}
242 *
243 * .some-message.ng-leave {}
244 * .some-message.ng-leave.ng-leave-active {}
245 * ```
246 *
247 * {@link ngAnimate Click here} to learn how to use JavaScript animations or to learn more about ngAnimate.
248 */
249angular.module('ngMessages', [])
250
251   /**
252    * @ngdoc directive
253    * @module ngMessages
254    * @name ngMessages
255    * @restrict AE
256    *
257    * @description
258    * `ngMessages` is a directive that is designed to show and hide messages based on the state
259    * of a key/value object that it listens on. The directive itself compliments error message
260    * reporting with the `ngModel` $error object (which stores a key/value state of validation errors).
261    *
262    * `ngMessages` manages the state of internal messages within its container element. The internal
263    * messages use the `ngMessage` directive and will be inserted/removed from the page depending
264    * on if they're present within the key/value object. By default, only one message will be displayed
265    * at a time and this depends on the prioritization of the messages within the template. (This can
266    * be changed by using the `ng-messages-multiple` or `multiple` attribute on the directive container.)
267    *
268    * A remote template can also be used to promote message reusability and messages can also be
269    * overridden.
270    *
271    * {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
272    *
273    * @usage
274    * ```html
275    * <!-- using attribute directives -->
276    * <ANY ng-messages="expression" role="alert">
277    *   <ANY ng-message="stringValue">...</ANY>
278    *   <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
279    *   <ANY ng-message-exp="expressionValue">...</ANY>
280    * </ANY>
281    *
282    * <!-- or by using element directives -->
283    * <ng-messages for="expression" role="alert">
284    *   <ng-message when="stringValue">...</ng-message>
285    *   <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
286    *   <ng-message when-exp="expressionValue">...</ng-message>
287    * </ng-messages>
288    * ```
289    *
290    * @param {string} ngMessages an angular expression evaluating to a key/value object
291    *                 (this is typically the $error object on an ngModel instance).
292    * @param {string=} ngMessagesMultiple|multiple when set, all messages will be displayed with true
293    *
294    * @example
295    * <example name="ngMessages-directive" module="ngMessagesExample"
296    *          deps="angular-messages.js"
297    *          animations="true" fixBase="true">
298    *   <file name="index.html">
299    *     <form name="myForm">
300    *       <label>
301    *         Enter your name:
302    *         <input type="text"
303    *                name="myName"
304    *                ng-model="name"
305    *                ng-minlength="5"
306    *                ng-maxlength="20"
307    *                required />
308    *       </label>
309    *       <pre>myForm.myName.$error = {{ myForm.myName.$error | json }}</pre>
310    *
311    *       <div ng-messages="myForm.myName.$error" style="color:maroon" role="alert">
312    *         <div ng-message="required">You did not enter a field</div>
313    *         <div ng-message="minlength">Your field is too short</div>
314    *         <div ng-message="maxlength">Your field is too long</div>
315    *       </div>
316    *     </form>
317    *   </file>
318    *   <file name="script.js">
319    *     angular.module('ngMessagesExample', ['ngMessages']);
320    *   </file>
321    * </example>
322    */
323   .directive('ngMessages', ['$animate', function($animate) {
324     var ACTIVE_CLASS = 'ng-active';
325     var INACTIVE_CLASS = 'ng-inactive';
326
327     return {
328       require: 'ngMessages',
329       restrict: 'AE',
330       controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
331         var ctrl = this;
332         var latestKey = 0;
333
334         var messages = this.messages = {};
335         var renderLater, cachedCollection;
336
337         this.render = function(collection) {
338           collection = collection || {};
339
340           renderLater = false;
341           cachedCollection = collection;
342
343           // this is true if the attribute is empty or if the attribute value is truthy
344           var multiple = isAttrTruthy($scope, $attrs.ngMessagesMultiple) ||
345                          isAttrTruthy($scope, $attrs.multiple);
346
347           var unmatchedMessages = [];
348           var matchedKeys = {};
349           var messageItem = ctrl.head;
350           var messageFound = false;
351           var totalMessages = 0;
352
353           // we use != instead of !== to allow for both undefined and null values
354           while (messageItem != null) {
355             totalMessages++;
356             var messageCtrl = messageItem.message;
357
358             var messageUsed = false;
359             if (!messageFound) {
360               forEach(collection, function(value, key) {
361                 if (!messageUsed && truthy(value) && messageCtrl.test(key)) {
362                   // this is to prevent the same error name from showing up twice
363                   if (matchedKeys[key]) return;
364                   matchedKeys[key] = true;
365
366                   messageUsed = true;
367                   messageCtrl.attach();
368                 }
369               });
370             }
371
372             if (messageUsed) {
373               // unless we want to display multiple messages then we should
374               // set a flag here to avoid displaying the next message in the list
375               messageFound = !multiple;
376             } else {
377               unmatchedMessages.push(messageCtrl);
378             }
379
380             messageItem = messageItem.next;
381           }
382
383           forEach(unmatchedMessages, function(messageCtrl) {
384             messageCtrl.detach();
385           });
386
387           unmatchedMessages.length !== totalMessages
388              ? $animate.setClass($element, ACTIVE_CLASS, INACTIVE_CLASS)
389              : $animate.setClass($element, INACTIVE_CLASS, ACTIVE_CLASS);
390         };
391
392         $scope.$watchCollection($attrs.ngMessages || $attrs['for'], ctrl.render);
393
394         this.reRender = function() {
395           if (!renderLater) {
396             renderLater = true;
397             $scope.$evalAsync(function() {
398               if (renderLater) {
399                 cachedCollection && ctrl.render(cachedCollection);
400               }
401             });
402           }
403         };
404
405         this.register = function(comment, messageCtrl) {
406           var nextKey = latestKey.toString();
407           messages[nextKey] = {
408             message: messageCtrl
409           };
410           insertMessageNode($element[0], comment, nextKey);
411           comment.$$ngMessageNode = nextKey;
412           latestKey++;
413
414           ctrl.reRender();
415         };
416
417         this.deregister = function(comment) {
418           var key = comment.$$ngMessageNode;
419           delete comment.$$ngMessageNode;
420           removeMessageNode($element[0], comment, key);
421           delete messages[key];
422           ctrl.reRender();
423         };
424
425         function findPreviousMessage(parent, comment) {
426           var prevNode = comment;
427           var parentLookup = [];
428           while (prevNode && prevNode !== parent) {
429             var prevKey = prevNode.$$ngMessageNode;
430             if (prevKey && prevKey.length) {
431               return messages[prevKey];
432             }
433
434             // dive deeper into the DOM and examine its children for any ngMessage
435             // comments that may be in an element that appears deeper in the list
436             if (prevNode.childNodes.length && parentLookup.indexOf(prevNode) == -1) {
437               parentLookup.push(prevNode);
438               prevNode = prevNode.childNodes[prevNode.childNodes.length - 1];
439             } else {
440               prevNode = prevNode.previousSibling || prevNode.parentNode;
441             }
442           }
443         }
444
445         function insertMessageNode(parent, comment, key) {
446           var messageNode = messages[key];
447           if (!ctrl.head) {
448             ctrl.head = messageNode;
449           } else {
450             var match = findPreviousMessage(parent, comment);
451             if (match) {
452               messageNode.next = match.next;
453               match.next = messageNode;
454             } else {
455               messageNode.next = ctrl.head;
456               ctrl.head = messageNode;
457             }
458           }
459         }
460
461         function removeMessageNode(parent, comment, key) {
462           var messageNode = messages[key];
463
464           var match = findPreviousMessage(parent, comment);
465           if (match) {
466             match.next = messageNode.next;
467           } else {
468             ctrl.head = messageNode.next;
469           }
470         }
471       }]
472     };
473
474     function isAttrTruthy(scope, attr) {
475      return (isString(attr) && attr.length === 0) || //empty attribute
476             truthy(scope.$eval(attr));
477     }
478
479     function truthy(val) {
480       return isString(val) ? val.length : !!val;
481     }
482   }])
483
484   /**
485    * @ngdoc directive
486    * @name ngMessagesInclude
487    * @restrict AE
488    * @scope
489    *
490    * @description
491    * `ngMessagesInclude` is a directive with the purpose to import existing ngMessage template
492    * code from a remote template and place the downloaded template code into the exact spot
493    * that the ngMessagesInclude directive is placed within the ngMessages container. This allows
494    * for a series of pre-defined messages to be reused and also allows for the developer to
495    * determine what messages are overridden due to the placement of the ngMessagesInclude directive.
496    *
497    * @usage
498    * ```html
499    * <!-- using attribute directives -->
500    * <ANY ng-messages="expression" role="alert">
501    *   <ANY ng-messages-include="remoteTplString">...</ANY>
502    * </ANY>
503    *
504    * <!-- or by using element directives -->
505    * <ng-messages for="expression" role="alert">
506    *   <ng-messages-include src="expressionValue1">...</ng-messages-include>
507    * </ng-messages>
508    * ```
509    *
510    * {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
511    *
512    * @param {string} ngMessagesInclude|src a string value corresponding to the remote template.
513    */
514   .directive('ngMessagesInclude',
515     ['$templateRequest', '$document', '$compile', function($templateRequest, $document, $compile) {
516
517     return {
518       restrict: 'AE',
519       require: '^^ngMessages', // we only require this for validation sake
520       link: function($scope, element, attrs) {
521         var src = attrs.ngMessagesInclude || attrs.src;
522         $templateRequest(src).then(function(html) {
523           $compile(html)($scope, function(contents) {
524             element.after(contents);
525
526             // the anchor is placed for debugging purposes
527             var anchor = jqLite($document[0].createComment(' ngMessagesInclude: ' + src + ' '));
528             element.after(anchor);
529
530             // we don't want to pollute the DOM anymore by keeping an empty directive element
531             element.remove();
532           });
533         });
534       }
535     };
536   }])
537
538   /**
539    * @ngdoc directive
540    * @name ngMessage
541    * @restrict AE
542    * @scope
543    *
544    * @description
545    * `ngMessage` is a directive with the purpose to show and hide a particular message.
546    * For `ngMessage` to operate, a parent `ngMessages` directive on a parent DOM element
547    * must be situated since it determines which messages are visible based on the state
548    * of the provided key/value map that `ngMessages` listens on.
549    *
550    * More information about using `ngMessage` can be found in the
551    * {@link module:ngMessages `ngMessages` module documentation}.
552    *
553    * @usage
554    * ```html
555    * <!-- using attribute directives -->
556    * <ANY ng-messages="expression" role="alert">
557    *   <ANY ng-message="stringValue">...</ANY>
558    *   <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
559    * </ANY>
560    *
561    * <!-- or by using element directives -->
562    * <ng-messages for="expression" role="alert">
563    *   <ng-message when="stringValue">...</ng-message>
564    *   <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
565    * </ng-messages>
566    * ```
567    *
568    * @param {expression} ngMessage|when a string value corresponding to the message key.
569    */
570  .directive('ngMessage', ngMessageDirectiveFactory('AE'))
571
572
573   /**
574    * @ngdoc directive
575    * @name ngMessageExp
576    * @restrict AE
577    * @scope
578    *
579    * @description
580    * `ngMessageExp` is a directive with the purpose to show and hide a particular message.
581    * For `ngMessageExp` to operate, a parent `ngMessages` directive on a parent DOM element
582    * must be situated since it determines which messages are visible based on the state
583    * of the provided key/value map that `ngMessages` listens on.
584    *
585    * @usage
586    * ```html
587    * <!-- using attribute directives -->
588    * <ANY ng-messages="expression">
589    *   <ANY ng-message-exp="expressionValue">...</ANY>
590    * </ANY>
591    *
592    * <!-- or by using element directives -->
593    * <ng-messages for="expression">
594    *   <ng-message when-exp="expressionValue">...</ng-message>
595    * </ng-messages>
596    * ```
597    *
598    * {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
599    *
600    * @param {expression} ngMessageExp|whenExp an expression value corresponding to the message 
600key.
601    */
602  .directive('ngMessageExp', ngMessageDirectiveFactory('A'));
603
604function ngMessageDirectiveFactory(restrict) {
605  return ['$animate', function($animate) {
606    return {
607      restrict: 'AE',
608      transclude: 'element',
609      terminal: true,
610      require: '^^ngMessages',
611      link: function(scope, element, attrs, ngMessagesCtrl, $transclude) {
612        var commentNode = element[0];
613
614        var records;
615        var staticExp = attrs.ngMessage || attrs.when;
616        var dynamicExp = attrs.ngMessageExp || attrs.whenExp;
617        var assignRecords = function(items) {
618          records = items
619              ? (isArray(items)
620                    ? items
621                    : items.split(/[\s,]+/))
622              : null;
623          ngMessagesCtrl.reRender();
624        };
625
626        if (dynamicExp) {
627          assignRecords(scope.$eval(dynamicExp));
628          scope.$watchCollection(dynamicExp, assignRecords);
629        } else {
630          assignRecords(staticExp);
631        }
632
633        var currentElement, messageCtrl;
634        ngMessagesCtrl.register(commentNode, messageCtrl = {
635          test: function(name) {
636            return contains(records, name);
637          },
638          attach: function() {
639            if (!currentElement) {
640              $transclude(scope, function(elm) {
641                $animate.enter(elm, null, element);
642                currentElement = elm;
643
644                // in the event that the parent element is destroyed
645                // by any other structural directive then it's time
646                // to deregister the message from the controller
647                currentElement.on('$destroy', function() {
648                  if (currentElement) {
649                    ngMessagesCtrl.deregister(commentNode);
650                    messageCtrl.detach();
651                  }
652                });
653              });
654            }
655          },
656          detach: function() {
657            if (currentElement) {
658              var elm = currentElement;
659              currentElement = null;
660              $animate.leave(elm);
661            }
662          }
663        });
664      }
665    };
666  }];
667
668  function contains(collection, key) {
669    if (collection) {
670      return isArray(collection)
671          ? collection.indexOf(key) >= 0
672          : collection.hasOwnProperty(key);
673    }
674  }
675}
676
677
678})(window, window.angular);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.