@@ -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,44 @@ 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 ( ) )
651
685
652
- function ngMessageDirectiveFactory ( ) {
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.
694
+ * For `ngMessageDefault` to operate, no `ngMessage` inner directive should be displayed
695
+ * in the parent `ngMessages` directive.
696
+ *
697
+ * More information about using `ngMessageDefault` can be found in the
698
+ * {@link module:ngMessages `ngMessages` module documentation}.
699
+ *
700
+ * @usage
701
+ * ```html
702
+ * <!-- using attribute directives -->
703
+ * <ANY ng-messages="expression" role="alert">
704
+ * <ANY ng-message="stringValue">...</ANY>
705
+ * <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
706
+ * <ANY ng-message-default>...</ANY>
707
+ * </ANY>
708
+ *
709
+ * <!-- or by using element directives -->
710
+ * <ng-messages for="expression" role="alert">
711
+ * <ng-message when="stringValue">...</ng-message>
712
+ * <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
713
+ * <ng-message-default>...</ng-message-default>
714
+ * </ng-messages>
715
+ * ```
716
+ *
717
+ * @param ngMessageDefault|when no ngMessage matches.
718
+ */
719
+ . directive ( 'ngMessageDefault' , ngMessageDirectiveFactory ( true ) ) ;
720
+
721
+ function ngMessageDirectiveFactory ( isDefault ) {
653
722
return [ '$animate' , function ( $animate ) {
654
723
return {
655
724
restrict : 'AE' ,
@@ -658,25 +727,28 @@ function ngMessageDirectiveFactory() {
658
727
terminal : true ,
659
728
require : '^^ngMessages' ,
660
729
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
- } ;
730
+ var commentNode , records , staticExp , dynamicExp ;
731
+
732
+ if ( ! isDefault ) {
733
+ commentNode = element [ 0 ] ;
734
+ staticExp = attrs . ngMessage || attrs . when ;
735
+ dynamicExp = attrs . ngMessageExp || attrs . whenExp ;
736
+
737
+ var assignRecords = function ( items ) {
738
+ records = items
739
+ ? ( isArray ( items )
740
+ ? items
741
+ : items . split ( / [ \s , ] + / ) )
742
+ : null ;
743
+ ngMessagesCtrl . reRender ( ) ;
744
+ } ;
674
745
675
- if ( dynamicExp ) {
676
- assignRecords ( scope . $eval ( dynamicExp ) ) ;
677
- scope . $watchCollection ( dynamicExp , assignRecords ) ;
678
- } else {
679
- assignRecords ( staticExp ) ;
746
+ if ( dynamicExp ) {
747
+ assignRecords ( scope . $eval ( dynamicExp ) ) ;
748
+ scope . $watchCollection ( dynamicExp , assignRecords ) ;
749
+ } else {
750
+ assignRecords ( staticExp ) ;
751
+ }
680
752
}
681
753
682
754
var currentElement , messageCtrl ;
@@ -701,7 +773,7 @@ function ngMessageDirectiveFactory() {
701
773
// If the message element was removed via a call to `detach` then `currentElement` will be null
702
774
// So this handler only handles cases where something else removed the message element.
703
775
if ( currentElement && currentElement . $$attachId === $$attachId ) {
704
- ngMessagesCtrl . deregister ( commentNode ) ;
776
+ ngMessagesCtrl . deregister ( commentNode , isDefault ) ;
705
777
messageCtrl . detach ( ) ;
706
778
}
707
779
newScope . $destroy ( ) ;
@@ -716,14 +788,14 @@ function ngMessageDirectiveFactory() {
716
788
$animate . leave ( elm ) ;
717
789
}
718
790
}
719
- } ) ;
791
+ } , isDefault ) ;
720
792
721
793
// We need to ensure that this directive deregisters itself when it no longer exists
722
794
// Normally this is done when the attached element is destroyed; but if this directive
723
795
// gets removed before we attach the message to the DOM there is nothing to watch
724
796
// in which case we must deregister when the containing scope is destroyed.
725
797
scope . $on ( '$destroy' , function ( ) {
726
- ngMessagesCtrl . deregister ( commentNode ) ;
798
+ ngMessagesCtrl . deregister ( commentNode , isDefault ) ;
727
799
} ) ;
728
800
}
729
801
} ;
0 commit comments