@@ -18,7 +18,7 @@ var jqLite;
18
18
* sequencing based on the order of how the messages are defined in the template.
19
19
*
20
20
* Currently, the ngMessages module only contains the code for the `ngMessages`, `ngMessagesInclude`
21
- * `ngMessage` and `ngMessageExp ` directives.
21
+ * `ngMessage`, `ngMessageExp` and `ngMessageDefault ` directives.
22
22
*
23
23
* ## Usage
24
24
* The `ngMessages` directive allows keys in a key/value collection to be associated with a child element
@@ -257,6 +257,19 @@ var jqLite;
257
257
* .some-message.ng-leave.ng-leave-active {}
258
258
* ```
259
259
*
260
+ * ## Displaying a default message
261
+ * If the ngMessages renders no inner ngMessage directive (that is to say when the key values does not
262
+ * match the attribute value present on each ngMessage directive), then it will render a default message
263
+ * using the ngMessageDefault directive.
264
+ *
265
+ * ```html
266
+ * <div ng-messages="myForm.myField.$error" role="alert">
267
+ * <div ng-message="required">This field is required</div>
268
+ * <div ng-message="minlength">This field is too short</div>
269
+ * <div ng-message-default>This is a default message</div>
270
+ * </div>
271
+ * ```
272
+ *
260
273
* {@link ngAnimate Click here} to learn how to use JavaScript animations or to learn more about ngAnimate.
261
274
*/
262
275
angular . module ( 'ngMessages' , [ ] , function initAngularHelpers ( ) {
@@ -289,6 +302,9 @@ angular.module('ngMessages', [], function initAngularHelpers() {
289
302
* A remote template can also be used to promote message reusability and messages can also be
290
303
* overridden.
291
304
*
305
+ * A default message can also be displayed when no `ngMessage` directive is inserted, using the
306
+ * `ngMessageDefault` directive.
307
+ *
292
308
* {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
293
309
*
294
310
* @usage
@@ -298,13 +314,15 @@ angular.module('ngMessages', [], function initAngularHelpers() {
298
314
* <ANY ng-message="stringValue">...</ANY>
299
315
* <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
300
316
* <ANY ng-message-exp="expressionValue">...</ANY>
317
+ * <ANY ng-message-default>...</ANY>
301
318
* </ANY>
302
319
*
303
320
* <!-- or by using element directives -->
304
321
* <ng-messages for="expression" role="alert">
305
322
* <ng-message when="stringValue">...</ng-message>
306
323
* <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
307
324
* <ng-message when-exp="expressionValue">...</ng-message>
325
+ * <ng-message-default>...</ng-message-default>
308
326
* </ng-messages>
309
327
* ```
310
328
*
@@ -333,6 +351,7 @@ angular.module('ngMessages', [], function initAngularHelpers() {
333
351
* <div ng-message="required">You did not enter a field</div>
334
352
* <div ng-message="minlength">Your field is too short</div>
335
353
* <div ng-message="maxlength">Your field is too long</div>
354
+ * <div ng-message-default>This field has an input error</div>
336
355
* </div>
337
356
* </form>
338
357
* </file>
@@ -409,8 +428,15 @@ angular.module('ngMessages', [], function initAngularHelpers() {
409
428
} ) ;
410
429
411
430
if ( unmatchedMessages . length !== totalMessages ) {
431
+ // Unset default message if set
432
+ if ( ctrl . default ) ctrl . default . detach ( ) ;
433
+
412
434
$animate . setClass ( $element , ACTIVE_CLASS , INACTIVE_CLASS ) ;
413
435
} else {
436
+
437
+ // Set default message if no other matched
438
+ if ( ctrl . default ) ctrl . default . attach ( ) ;
439
+
414
440
$animate . setClass ( $element , INACTIVE_CLASS , ACTIVE_CLASS ) ;
415
441
}
416
442
} ;
@@ -428,23 +454,31 @@ angular.module('ngMessages', [], function initAngularHelpers() {
428
454
}
429
455
} ;
430
456
431
- this . register = function ( comment , messageCtrl ) {
432
- var nextKey = latestKey . toString ( ) ;
433
- messages [ nextKey ] = {
434
- message : messageCtrl
435
- } ;
436
- insertMessageNode ( $element [ 0 ] , comment , nextKey ) ;
437
- comment . $$ngMessageNode = nextKey ;
438
- latestKey ++ ;
457
+ this . register = function ( comment , messageCtrl , isDefault ) {
458
+ if ( isDefault ) {
459
+ ctrl . default = messageCtrl ;
460
+ } else {
461
+ var nextKey = latestKey . toString ( ) ;
462
+ messages [ nextKey ] = {
463
+ message : messageCtrl
464
+ } ;
465
+ insertMessageNode ( $element [ 0 ] , comment , nextKey ) ;
466
+ comment . $$ngMessageNode = nextKey ;
467
+ latestKey ++ ;
468
+ }
439
469
440
470
ctrl . reRender ( ) ;
441
471
} ;
442
472
443
- this . deregister = function ( comment ) {
444
- var key = comment . $$ngMessageNode ;
445
- delete comment . $$ngMessageNode ;
446
- removeMessageNode ( $element [ 0 ] , comment , key ) ;
447
- delete messages [ key ] ;
473
+ this . deregister = function ( comment , isDefault ) {
474
+ if ( isDefault ) {
475
+ delete ctrl . default ;
476
+ } else {
477
+ var key = comment . $$ngMessageNode ;
478
+ delete comment . $$ngMessageNode ;
479
+ removeMessageNode ( $element [ 0 ] , comment , key ) ;
480
+ delete messages [ key ] ;
481
+ }
448
482
ctrl . reRender ( ) ;
449
483
} ;
450
484
@@ -647,9 +681,41 @@ angular.module('ngMessages', [], function initAngularHelpers() {
647
681
*
648
682
* @param {expression } ngMessageExp|whenExp an expression value corresponding to the message key.
649
683
*/
650
- . directive ( 'ngMessageExp' , ngMessageDirectiveFactory ( ) ) ;
684
+ . directive ( 'ngMessageExp' , ngMessageDirectiveFactory ( ) )
685
+
686
+ /**
687
+ * @ngdoc directive
688
+ * @name ngMessageDefault
689
+ * @restrict AE
690
+ * @scope
691
+ *
692
+ * @description
693
+ * `ngMessageDefault` is a directive with the purpose to show and hide a default message for
694
+ * {@link ngMessages}, when none of provided messages matches.
695
+ *
696
+ * More information about using `ngMessageDefault` can be found in the
697
+ * {@link module:ngMessages `ngMessages` module documentation}.
698
+ *
699
+ * @usage
700
+ * ```html
701
+ * <!-- using attribute directives -->
702
+ * <ANY ng-messages="expression" role="alert">
703
+ * <ANY ng-message="stringValue">...</ANY>
704
+ * <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
705
+ * <ANY ng-message-default>...</ANY>
706
+ * </ANY>
707
+ *
708
+ * <!-- or by using element directives -->
709
+ * <ng-messages for="expression" role="alert">
710
+ * <ng-message when="stringValue">...</ng-message>
711
+ * <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
712
+ * <ng-message-default>...</ng-message-default>
713
+ * </ng-messages>
714
+ *
715
+ */
716
+ . directive ( 'ngMessageDefault' , ngMessageDirectiveFactory ( true ) ) ;
651
717
652
- function ngMessageDirectiveFactory ( ) {
718
+ function ngMessageDirectiveFactory ( isDefault ) {
653
719
return [ '$animate' , function ( $animate ) {
654
720
return {
655
721
restrict : 'AE' ,
@@ -658,25 +724,28 @@ function ngMessageDirectiveFactory() {
658
724
terminal : true ,
659
725
require : '^^ngMessages' ,
660
726
link : function ( scope , element , attrs , ngMessagesCtrl , $transclude ) {
661
- var commentNode = element [ 0 ] ;
662
-
663
- var records ;
664
- var staticExp = attrs . ngMessage || attrs . when ;
665
- var dynamicExp = attrs . ngMessageExp || attrs . whenExp ;
666
- var assignRecords = function ( items ) {
667
- records = items
668
- ? ( isArray ( items )
669
- ? items
670
- : items . split ( / [ \s , ] + / ) )
671
- : null ;
672
- ngMessagesCtrl . reRender ( ) ;
673
- } ;
727
+ var commentNode , records , staticExp , dynamicExp ;
728
+
729
+ if ( ! isDefault ) {
730
+ commentNode = element [ 0 ] ;
731
+ staticExp = attrs . ngMessage || attrs . when ;
732
+ dynamicExp = attrs . ngMessageExp || attrs . whenExp ;
733
+
734
+ var assignRecords = function ( items ) {
735
+ records = items
736
+ ? ( isArray ( items )
737
+ ? items
738
+ : items . split ( / [ \s , ] + / ) )
739
+ : null ;
740
+ ngMessagesCtrl . reRender ( ) ;
741
+ } ;
674
742
675
- if ( dynamicExp ) {
676
- assignRecords ( scope . $eval ( dynamicExp ) ) ;
677
- scope . $watchCollection ( dynamicExp , assignRecords ) ;
678
- } else {
679
- assignRecords ( staticExp ) ;
743
+ if ( dynamicExp ) {
744
+ assignRecords ( scope . $eval ( dynamicExp ) ) ;
745
+ scope . $watchCollection ( dynamicExp , assignRecords ) ;
746
+ } else {
747
+ assignRecords ( staticExp ) ;
748
+ }
680
749
}
681
750
682
751
var currentElement , messageCtrl ;
@@ -701,7 +770,7 @@ function ngMessageDirectiveFactory() {
701
770
// If the message element was removed via a call to `detach` then `currentElement` will be null
702
771
// So this handler only handles cases where something else removed the message element.
703
772
if ( currentElement && currentElement . $$attachId === $$attachId ) {
704
- ngMessagesCtrl . deregister ( commentNode ) ;
773
+ ngMessagesCtrl . deregister ( commentNode , isDefault ) ;
705
774
messageCtrl . detach ( ) ;
706
775
}
707
776
newScope . $destroy ( ) ;
@@ -716,14 +785,14 @@ function ngMessageDirectiveFactory() {
716
785
$animate . leave ( elm ) ;
717
786
}
718
787
}
719
- } ) ;
788
+ } , isDefault ) ;
720
789
721
790
// We need to ensure that this directive deregisters itself when it no longer exists
722
791
// Normally this is done when the attached element is destroyed; but if this directive
723
792
// gets removed before we attach the message to the DOM there is nothing to watch
724
793
// in which case we must deregister when the containing scope is destroyed.
725
794
scope . $on ( '$destroy' , function ( ) {
726
- ngMessagesCtrl . deregister ( commentNode ) ;
795
+ ngMessagesCtrl . deregister ( commentNode , isDefault ) ;
727
796
} ) ;
728
797
}
729
798
} ;
0 commit comments