@@ -12,7 +12,13 @@ angular.module('material.components.input', [
12
12
. directive ( 'textarea' , inputTextareaDirective )
13
13
. directive ( 'mdMaxlength' , mdMaxlengthDirective )
14
14
. directive ( 'placeholder' , placeholderDirective )
15
- . directive ( 'ngMessages' , ngMessagesDirective ) ;
15
+ . directive ( 'ngMessages' , ngMessagesDirective )
16
+ . directive ( 'ngMessage' , ngMessageDirective )
17
+ . directive ( 'ngMessageExp' , ngMessageDirective )
18
+
19
+ . animation ( '.md-input-invalid' , mdInputInvalidMessagesAnimation )
20
+ . animation ( '.md-input-messages-animation' , ngMessagesAnimation )
21
+ . animation ( '.md-input-message-animation' , ngMessageAnimation ) ;
16
22
17
23
/**
18
24
* @ngdoc directive
@@ -59,7 +65,7 @@ function mdInputContainerDirective($mdTheming, $parse) {
59
65
if ( element . find ( 'md-icon' ) . length ) element . addClass ( 'md-has-icon' ) ;
60
66
}
61
67
62
- function ContainerCtrl ( $scope , $element , $attrs ) {
68
+ function ContainerCtrl ( $scope , $element , $attrs , $animate ) {
63
69
var self = this ;
64
70
65
71
self . isErrorGetter = $attrs . mdIsError && $parse ( $attrs . mdIsError ) ;
@@ -81,7 +87,11 @@ function mdInputContainerDirective($mdTheming, $parse) {
81
87
$element . toggleClass ( 'md-input-has-placeholder' , ! ! hasPlaceholder ) ;
82
88
} ;
83
89
self . setInvalid = function ( isInvalid ) {
84
- $element . toggleClass ( 'md-input-invalid' , ! ! isInvalid ) ;
90
+ if ( isInvalid ) {
91
+ $animate . addClass ( $element , 'md-input-invalid' ) ;
92
+ } else {
93
+ $animate . removeClass ( $element , 'md-input-invalid' ) ;
94
+ }
85
95
} ;
86
96
$scope . $watch ( function ( ) {
87
97
return self . label && self . input ;
@@ -490,10 +500,13 @@ function ngMessagesDirective() {
490
500
require : '^^?mdInputContainer'
491
501
} ;
492
502
493
- function postLink ( scope , element , attr , inputContainer ) {
503
+ function postLink ( scope , element , attrs , inputContainer ) {
494
504
// If we are not a child of an input container, don't do anything
495
505
if ( ! inputContainer ) return ;
496
506
507
+ // Add our animation class
508
+ element . toggleClass ( 'md-input-messages-animation' , true ) ;
509
+
497
510
// Tell our parent input container we have messages so we can set the proper classes
498
511
inputContainer . setHasMessages ( true ) ;
499
512
@@ -503,3 +516,176 @@ function ngMessagesDirective() {
503
516
} ) ;
504
517
}
505
518
}
519
+
520
+ function ngMessageDirective ( $mdUtil ) {
521
+ return {
522
+ restrict : 'EA' ,
523
+ compile : compile ,
524
+ priority : 100
525
+ } ;
526
+
527
+ function compile ( element ) {
528
+ var inputContainer = $mdUtil . getClosest ( element , "md-input-container" ) ;
529
+
530
+ // If we are not a child of an input container, don't do anything
531
+ if ( ! inputContainer ) return ;
532
+
533
+ // Add our animation class
534
+ element . toggleClass ( 'md-input-message-animation' , true ) ;
535
+
536
+ return { } ;
537
+ }
538
+ }
539
+
540
+ function mdInputInvalidMessagesAnimation ( $q , $animateCss ) {
541
+ return {
542
+ addClass : function ( element , className , done ) {
543
+ console . log ( ' *** addClass: ' , className ) ;
544
+ if ( className == "md-input-invalid" && shouldShowMessages ( element ) ) {
545
+ showInputMessages ( element , $animateCss , $q ) . finally ( done ) ;
546
+ }
547
+ }
548
+ // NOTE: We do not need the removeClass method, because the message ng-leave animation will fire
549
+
550
+ /*
551
+ removeClass: function(element, className, done) {
552
+ console.log(' *** removeClass: ', className);
553
+ if (className == "md-input-invalid") {
554
+ hideInputMessages(element, $animateCss, $q).finally(done);
555
+ }
556
+ }
557
+ */
558
+ }
559
+ }
560
+
561
+ function ngMessagesAnimation ( $q , $animateCss ) {
562
+ return {
563
+ addClass : function ( element , className , done ) {
564
+ if ( className == "ng-hide" ) {
565
+ console . log ( ' *** messages hide' ) ;
566
+ hideInputMessages ( element , $animateCss , $q ) . finally ( done ) ;
567
+ } else {
568
+ done ( ) ;
569
+ }
570
+ } ,
571
+
572
+ removeClass : function ( element , className , done ) {
573
+ if ( className == "ng-hide" && shouldShowMessages ( element ) ) {
574
+ console . log ( ' *** messages show' ) ;
575
+ showInputMessages ( element , $animateCss , $q ) . finally ( done ) ;
576
+ } else {
577
+ done ( ) ;
578
+ }
579
+ }
580
+ }
581
+ }
582
+
583
+ function ngMessageAnimation ( $animateCss ) {
584
+ return {
585
+ enter : function ( element , done ) {
586
+ console . log ( ' *** message enter' ) ;
587
+ if ( shouldShowMessages ( element ) ) {
588
+ return showMessage ( element , $animateCss ) ;
589
+ } else {
590
+ done ( ) ;
591
+ }
592
+ } ,
593
+
594
+ leave : function ( element ) {
595
+ console . log ( ' *** message leave' ) ;
596
+ return hideMessage ( element , $animateCss ) ;
597
+ }
598
+ }
599
+ }
600
+
601
+ function showInputMessages ( element , $animateCss , $q ) {
602
+ var animators = [ ] , animator ;
603
+ var messages = getMessagesElement ( element ) ;
604
+
605
+ angular . forEach ( messages . children ( ) , function ( child ) {
606
+ if ( ! child . classList . contains ( "md-char-counter" ) ) {
607
+ animator = showMessage ( angular . element ( child ) , $animateCss ) ;
608
+
609
+ animators . push ( animator . start ( ) ) ;
610
+ }
611
+ } ) ;
612
+
613
+ return $q . all ( animators ) ;
614
+ }
615
+
616
+ function hideInputMessages ( element , $animateCss , $q ) {
617
+ var animators = [ ] , animator ;
618
+ var messages = getMessagesElement ( element ) ;
619
+
620
+ angular . forEach ( messages . children ( ) , function ( child ) {
621
+ if ( ! child . classList . contains ( "md-char-counter" ) ) {
622
+ animator = showMessage ( angular . element ( child ) , $animateCss ) ;
623
+
624
+ animators . push ( animator . start ( ) ) ;
625
+ }
626
+ } ) ;
627
+
628
+ return $q . all ( animators ) ;
629
+ }
630
+
631
+ function showMessage ( element , $animateCss ) {
632
+ var height = element [ 0 ] . offsetHeight ;
633
+ var styles = window . getComputedStyle ( element [ 0 ] ) ;
634
+
635
+ return $animateCss ( element , {
636
+ from : { "opacity" : styles . opacity , "margin-top" : - height + "px" } ,
637
+ to : { "opacity" : 1 , "margin-top" : "0" } ,
638
+ duration : 0.3
639
+ } ) ;
640
+ }
641
+
642
+ function hideMessage ( element , $animateCss ) {
643
+ var height = element [ 0 ] . offsetHeight ;
644
+ var styles = window . getComputedStyle ( element [ 0 ] ) ;
645
+
646
+ return $animateCss ( element , {
647
+ from : { "opacity" : styles . opacity , "margin-top" : 0 } ,
648
+ to : { "opacity" : 0 , "margin-top" : - height + "px" } ,
649
+ duration : 0.3
650
+ } ) ;
651
+ }
652
+
653
+ function shouldShowMessages ( element ) {
654
+ var input = getInputElement ( element ) ;
655
+
656
+ var shouldShow = (
657
+ // If we are invalid
658
+ input . hasClass ( 'md-input-invalid' )
659
+
660
+ // or the user tells us not to auto-hide
661
+ || input . hasClass ( 'md-no-auto-hide-messages' )
662
+
663
+ // or we see a known show/hide/switch directive
664
+ || hasVisibilityDirective ( input )
665
+ ) ;
666
+
667
+ console . log ( ' *** shouldShow: ' , shouldShow ) ;
668
+
669
+ return shouldShow ;
670
+ }
671
+
672
+ function getInputElement ( element ) {
673
+ var inputContainer = element . controller ( 'mdInputContainer' ) ;
674
+
675
+ return inputContainer . element ;
676
+ }
677
+
678
+ function getMessagesElement ( element ) {
679
+ var input = getInputElement ( element ) ;
680
+ var selector = 'ng-messages,data-ng-messages,x-ng-messages,' +
681
+ '[ng-messages],[data-ng-messages],[x-ng-messages]' ;
682
+
683
+ console . log ( ' *** getting messages: ' , element , input , input [ 0 ] . querySelector ( selector ) ) ;
684
+
685
+ return angular . element ( input [ 0 ] . querySelector ( selector ) ) ;
686
+ }
687
+
688
+ function hasVisibilityDirective ( input ) {
689
+ // TODO add functionality to grab the child messages
690
+ return false ;
691
+ }
0 commit comments