@@ -21,7 +21,7 @@ var jqLite = angular.element;
21
21
* sequencing based on the order of how the messages are defined in the template.
22
22
*
23
23
* Currently, the ngMessages module only contains the code for the `ngMessages`, `ngMessagesInclude`
24
- * `ngMessage` and `ngMessageExp ` directives.
24
+ * `ngMessage`, `ngMessageExp` and `ngMessageDefault ` directives.
25
25
*
26
26
* # Usage
27
27
* The `ngMessages` directive listens on a key/value collection which is set on the ngMessages attribute.
@@ -239,6 +239,19 @@ var jqLite = angular.element;
239
239
* .some-message.ng-leave.ng-leave-active {}
240
240
* ```
241
241
*
242
+ * ## Displaying a default message
243
+ * If the ngMessages renders no inner ngMessage directive (that is to say when the key values does not
244
+ * match the attribute value present on each ngMessage directive), then it will render a default message
245
+ * using the ngMessageDefault directive.
246
+ *
247
+ * ```html
248
+ * <div ng-messages="myForm.myField.$error" role="alert">
249
+ * <div ng-message="required">This field is required</div>
250
+ * <div ng-message="minlength">This field is too short</div>
251
+ * <div ng-message-default>This is a default message</div>
252
+ * </div>
253
+ * ```
254
+ *
242
255
* {@link ngAnimate Click here} to learn how to use JavaScript animations or to learn more about ngAnimate.
243
256
*/
244
257
angular . module ( 'ngMessages' , [ ] )
@@ -260,6 +273,9 @@ angular.module('ngMessages', [])
260
273
* at a time and this depends on the prioritization of the messages within the template. (This can
261
274
* be changed by using the `ng-messages-multiple` or `multiple` attribute on the directive container.)
262
275
*
276
+ * A default message can also be displayed when no `ngMessage` directive is inserted, using the
277
+ * `ngMessageDefault` directive.
278
+ *
263
279
* A remote template can also be used to promote message reusability and messages can also be
264
280
* overridden.
265
281
*
@@ -272,13 +288,15 @@ angular.module('ngMessages', [])
272
288
* <ANY ng-message="stringValue">...</ANY>
273
289
* <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
274
290
* <ANY ng-message-exp="expressionValue">...</ANY>
291
+ * <ANY ng-message-default>...</ANY>
275
292
* </ANY>
276
293
*
277
294
* <!-- or by using element directives -->
278
295
* <ng-messages for="expression" role="alert">
279
296
* <ng-message when="stringValue">...</ng-message>
280
297
* <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
281
298
* <ng-message when-exp="expressionValue">...</ng-message>
299
+ * <ng-message-default>...</ng-message-default>
282
300
* </ng-messages>
283
301
* ```
284
302
*
@@ -307,6 +325,7 @@ angular.module('ngMessages', [])
307
325
* <div ng-message="required">You did not enter a field</div>
308
326
* <div ng-message="minlength">Your field is too short</div>
309
327
* <div ng-message="maxlength">Your field is too long</div>
328
+ * <div ng-message-default>This is a default message</div>
310
329
* </div>
311
330
* </form>
312
331
* </file>
@@ -379,9 +398,17 @@ angular.module('ngMessages', [])
379
398
messageCtrl . detach ( ) ;
380
399
} ) ;
381
400
382
- unmatchedMessages . length !== totalMessages
383
- ? $animate . setClass ( $element , ACTIVE_CLASS , INACTIVE_CLASS )
384
- : $animate . setClass ( $element , INACTIVE_CLASS , ACTIVE_CLASS ) ;
401
+ if ( unmatchedMessages . length !== totalMessages ) {
402
+ // Unset default message if setted
403
+ if ( ctrl . default ) { ctrl . default . detach ( ) ; }
404
+
405
+ $animate . setClass ( $element , ACTIVE_CLASS , INACTIVE_CLASS ) ;
406
+ } else {
407
+ // Set default message when no other one matched
408
+ if ( ctrl . default ) { ctrl . default . attach ( ) ; }
409
+
410
+ $animate . setClass ( $element , INACTIVE_CLASS , ACTIVE_CLASS ) ;
411
+ }
385
412
} ;
386
413
387
414
$scope . $watchCollection ( $attrs . ngMessages || $attrs [ 'for' ] , ctrl . render ) ;
@@ -397,23 +424,32 @@ angular.module('ngMessages', [])
397
424
}
398
425
} ;
399
426
400
- this . register = function ( comment , messageCtrl ) {
401
- var nextKey = latestKey . toString ( ) ;
402
- messages [ nextKey ] = {
403
- message : messageCtrl
404
- } ;
405
- insertMessageNode ( $element [ 0 ] , comment , nextKey ) ;
406
- comment . $$ngMessageNode = nextKey ;
407
- latestKey ++ ;
427
+ this . register = function ( comment , messageCtrl , isDefault ) {
428
+ if ( isDefault ) {
429
+ ctrl . default = messageCtrl ;
430
+ } else {
431
+ var nextKey = latestKey . toString ( ) ;
432
+ messages [ nextKey ] = {
433
+ message : messageCtrl
434
+ } ;
435
+ insertMessageNode ( $element [ 0 ] , comment , nextKey ) ;
436
+ comment . $$ngMessageNode = nextKey ;
437
+ latestKey ++ ;
438
+ }
408
439
409
440
ctrl . reRender ( ) ;
410
441
} ;
411
442
412
- this . deregister = function ( comment ) {
413
- var key = comment . $$ngMessageNode ;
414
- delete comment . $$ngMessageNode ;
415
- removeMessageNode ( $element [ 0 ] , comment , key ) ;
416
- delete messages [ key ] ;
443
+ this . deregister = function ( comment , isDefault ) {
444
+ if ( isDefault ) {
445
+ delete ctrl . default ;
446
+ } else {
447
+ var key = comment . $$ngMessageNode ;
448
+ delete comment . $$ngMessageNode ;
449
+ removeMessageNode ( $element [ 0 ] , comment , key ) ;
450
+ delete messages [ key ] ;
451
+ }
452
+
417
453
ctrl . reRender ( ) ;
418
454
} ;
419
455
@@ -594,7 +630,43 @@ angular.module('ngMessages', [])
594
630
*
595
631
* @param {expression } ngMessageExp|whenExp an expression value corresponding to the message key.
596
632
*/
597
- . directive ( 'ngMessageExp' , ngMessageDirectiveFactory ( 'A' ) ) ;
633
+ . directive ( 'ngMessageExp' , ngMessageDirectiveFactory ( 'A' ) )
634
+
635
+ /**
636
+ * @ngdoc directive
637
+ * @name ngMessageDefault
638
+ * @restrict AE
639
+ * @scope
640
+ *
641
+ * @description
642
+ * `ngMessageDefault` is a directive with the purpose to show and hide a default message.
643
+ * For `ngMessageDefault` to operate, no `ngMessage` inner directive should be displayed
644
+ * in the parent `ngMessages` directive.
645
+ *
646
+ * More information about using `ngMessageDefault` can be found in the
647
+ * {@link module:ngMessages `ngMessages` module documentation}.
648
+ *
649
+ * @usage
650
+ * ```html
651
+ * <!-- using attribute directives -->
652
+ * <ANY ng-messages="expression" role="alert">
653
+ * <ANY ng-message="stringValue">...</ANY>
654
+ * <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
655
+ * <ANY ng-message-default>...</ANY>
656
+ * </ANY>
657
+ *
658
+ * <!-- or by using element directives -->
659
+ * <ng-messages for="expression" role="alert">
660
+ * <ng-message when="stringValue">...</ng-message>
661
+ * <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
662
+ * <ng-message-default>...</ng-message-default>
663
+ * </ng-messages>
664
+ * ```
665
+ *
666
+ * @param {expression } ngMessageDefault|when no ngMessage matches.
667
+ */
668
+ . directive ( 'ngMessageDefault' , ngMessageDefaultDirectiveFactory ( 'AE' ) ) ;
669
+
598
670
599
671
function ngMessageDirectiveFactory ( restrict ) {
600
672
return [ '$animate' , function ( $animate ) {
@@ -668,3 +740,47 @@ function ngMessageDirectiveFactory(restrict) {
668
740
}
669
741
}
670
742
}
743
+
744
+ function ngMessageDefaultDirectiveFactory ( restrict ) {
745
+ return [ '$animate' , function ( $animate ) {
746
+ return {
747
+ restrict : 'AE' ,
748
+ transclude : 'element' ,
749
+ terminal : true ,
750
+ require : '^^ngMessages' ,
751
+ link : function ( scope , element , attrs , ngMessagesCtrl , $transclude ) {
752
+ var commentNode = element [ 0 ] ;
753
+
754
+ var currentElement , messageCtrl ;
755
+ ngMessagesCtrl . register ( commentNode , messageCtrl = {
756
+ attach : function ( ) {
757
+ if ( ! currentElement ) {
758
+ $transclude ( scope , function ( elm ) {
759
+ $animate . enter ( elm , null , element ) ;
760
+ currentElement = elm ;
761
+
762
+ // in the event that the parent element is destroyed
763
+ // by any other structural directive then it's time
764
+ // to deregister the default message (boolean set to true)
765
+ // from the controller
766
+ currentElement . on ( '$destroy' , function ( ) {
767
+ if ( currentElement ) {
768
+ ngMessagesCtrl . deregister ( commentNode , true ) ;
769
+ messageCtrl . detach ( ) ;
770
+ }
771
+ } ) ;
772
+ } ) ;
773
+ }
774
+ } ,
775
+ detach : function ( ) {
776
+ if ( currentElement ) {
777
+ var elm = currentElement ;
778
+ currentElement = null ;
779
+ $animate . leave ( elm ) ;
780
+ }
781
+ }
782
+ } , true ) ; // boolean set to true to specify default message
783
+ }
784
+ } ;
785
+ } ] ;
786
+ }
0 commit comments