1
- angular . module ( 'field-directive' , [ 'input.html' , 'textarea.html' , 'select.html' , 'validationMessages.html' ] )
1
+ angular . module ( 'field-directive' , [ 'input.html' , 'textarea.html' , 'select.html' ] )
2
2
3
3
. directive ( 'field' , function ( $compile , $http , $templateCache , $interpolate ) {
4
4
@@ -24,46 +24,32 @@ angular.module('field-directive', ['input.html', 'textarea.html', 'select.html',
24
24
// Search through the originalDirective's element for elements that contain information about how to map
25
25
// validation keys to messages
26
26
function getValidationMessageMap ( originalElement ) {
27
- // Find all the <validator> child elements and extract their (key, message) info
28
- var validationMessages = { } ;
29
- angular . forEach ( originalElement . find ( 'validator' ) , function ( element ) {
30
- // Wrap the element in jqLite/jQuery
31
- element = angular . element ( element ) ;
32
- // Store the message info to be provided to the scope later
33
- // The content of the validation element may include interpolation {{}}
34
- // so we will actually store a function created by the $interpolate service
35
- // To get the interpolated message we will call this function with the scope. e.g.
36
- // var messageString = getMessage(scope);
37
- validationMessages [ element . attr ( 'key' ) ] = $interpolate ( element . text ( ) ) ;
38
- } ) ;
39
- return validationMessages ;
27
+ // Find all the <validator> child elements and extract their (key, message) info
28
+ var validationMessages = { } ;
29
+ angular . forEach ( originalElement . find ( 'validator' ) , function ( element ) {
30
+ // Wrap the element in jqLite/jQuery
31
+ element = angular . element ( element ) ;
32
+ // Store the message info to be provided to the scope later
33
+ // The content of the validation element may include interpolation {{}}
34
+ // so we will actually store a function created by the $interpolate service
35
+ // To get the interpolated message we will call this function with the scope. e.g.
36
+ // var messageString = getMessage(scope);
37
+ validationMessages [ element . attr ( 'key' ) ] = $interpolate ( element . text ( ) ) ;
38
+ } ) ;
39
+ return validationMessages ;
40
40
}
41
41
42
42
// Find the content that will go into the new label
43
- // label="..." attribute trumps child <label> element
44
- function extractLabelContent ( originalElement , attrs ) {
45
- var labelContent = '' ;
46
- if ( attrs . fieldLabel ) {
47
- // Label is provided as an attribute on the originalElement
48
- labelContent = attrs . fieldLabel ;
49
- } else if ( originalElement . find ( 'label' ) [ 0 ] ) {
50
- // Label is provided as a <label> child element of the originalElement
51
- labelContent = originalElement . find ( 'label' ) . html ( ) ;
52
- }
53
- if ( ! labelContent ) {
54
- throw new Error ( 'No label provided' ) ;
55
- }
56
- return labelContent ;
43
+ // Label is provided as a <label> child element of the original element
44
+ function getLabelContent ( element ) {
45
+ var label = element . find ( 'label' ) ;
46
+ return label [ 0 ] && label . html ( ) ;
57
47
}
58
48
59
49
return {
60
50
restrict :'E' ,
61
51
priority : 100 , // We need this directive to happen before ng-model
62
52
terminal : true , // We are going to deal with this element
63
- controller : function ( $scope ) {
64
- // We will store the validation messages here, in the field's controller,
65
- // and the bind-validation-message directive will be able to access it
66
- } ,
67
53
compile : function ( element , attrs ) {
68
54
if ( attrs . ngRepeat || attrs . ngSwitch || attrs . uiIf ) {
69
55
throw new Error ( 'The ng-repeat, ng-switch and ui-if directives are not supported on the same element as the field directive.' ) ;
@@ -74,31 +60,40 @@ angular.module('field-directive', ['input.html', 'textarea.html', 'select.html',
74
60
75
61
// Extract the label and validation message info from the directive's original element
76
62
var messageMap = getValidationMessageMap ( element ) ;
77
- var labelContent = extractLabelContent ( element , attrs ) ;
63
+ var labelContent = getLabelContent ( element ) ;
78
64
79
65
// Clear the directive's original element now that we have extracted what we need from it
80
66
element . html ( '' ) ;
81
67
82
- return function ( scope , element , attrs , fieldController ) {
83
- // Attach a copy of the message map to this field's controller
84
- fieldController . messageMap = angular . copy ( messageMap ) ;
85
-
86
- // Load up the template for this kind of field
87
- var template = attrs . template || 'input.html' ; // Default to the simple input if none given
88
- var getFieldElement = loadTemplate ( template ) . then ( function ( newElement ) {
89
-
90
- // Update the label's contents
91
- var labelElement = newElement . find ( 'label' ) ;
92
- labelElement . html ( labelContent ) ;
93
- // Our template will have its own child scope
68
+ return function postLink ( scope , element , attrs ) {
69
+ // Load up the template for this kind of field, default to the simple input if none given
70
+ loadTemplate ( attrs . template || 'input.html' ) . then ( function ( newElement ) {
71
+ // Set up the scope - the template will have its own scope, which is a child of the directive's scope
94
72
var childScope = scope . $new ( ) ;
95
-
73
+ // Attach a copy of the message map to the scope
74
+ childScope . $messageMap = angular . copy ( messageMap ) ;
96
75
// Generate an id for the field from the ng-model expression and the current scope
97
76
// We replace dots with underscores to work with browsers and ngModel lookup on the FormController
98
77
// We couldn't do this in the compile function as we need to be able to calculate the unique id from the scope
99
78
childScope . $fieldId = attrs . ngModel . replace ( '.' , '_' ) . toLowerCase ( ) + '_' + childScope . $id ;
100
79
childScope . $fieldLabel = labelContent ;
101
80
81
+ // Update the $fieldErrors array when the validity of the field changes
82
+ childScope . $watch ( '$field.$dirty && $field.$error' , function ( errorList ) {
83
+ childScope . $fieldErrors = [ ] ;
84
+ if ( errorList ) {
85
+ angular . forEach ( errorList , function ( invalid , key ) {
86
+ if ( invalid ) {
87
+ childScope . $fieldErrors . push ( key ) ;
88
+ }
89
+ } ) ;
90
+ }
91
+ } , true ) ;
92
+
93
+ // Update the label's contents
94
+ var labelElement = newElement . find ( 'label' ) ;
95
+ labelElement . html ( labelContent ) ;
96
+
102
97
// Copy over all left over attributes to the input element
103
98
// We can't use interpolation in the template for directives such as ng-model
104
99
var inputElement = findInputElement ( newElement ) ;
@@ -129,13 +124,12 @@ angular.module('field-directive', ['input.html', 'textarea.html', 'select.html',
129
124
} ;
130
125
} )
131
126
132
- // A directive to bind the interpolation function of a validation message to the content of an element
133
- // use it as a attribute providing the validation key as the value of the attribute:
127
+ // A directive to bind the interpolation function of a validation message to the content of an element.
128
+ // Use it as a attribute providing the validation key as the value of the attribute:
134
129
// <span bind-validation-message="required"></span>
135
130
. directive ( 'bindValidationMessage' , function ( ) {
136
131
return {
137
- require : '^field' ,
138
- link : function ( scope , element , attrs , fieldController ) {
132
+ link : function ( scope , element , attrs ) {
139
133
var removeWatch = null ;
140
134
// The key may be dynamic (i.e. use interpolation) so we need to $observe it
141
135
attrs . $observe ( 'bindValidationMessage' , function ( key ) {
@@ -144,21 +138,15 @@ angular.module('field-directive', ['input.html', 'textarea.html', 'select.html',
144
138
removeWatch ( ) ;
145
139
removeWatch = null ;
146
140
}
147
- if ( key && fieldController . messageMap [ key ] ) {
148
- // Watch the message map interpolation function for this key
149
- removeWatch = scope . $watch ( fieldController . messageMap [ key ] , function ( message ) {
150
- // and update the contents of this element with the interpolated text
141
+ if ( key && scope . $messageMap [ key ] ) {
142
+ // scope.$messageMap[key] returns an interpolation function.
143
+ // Watch the value of calling this function with the current scope.
144
+ // Update the contents of the current element if the message changes
145
+ removeWatch = scope . $watch ( scope . $messageMap [ key ] , function ( message ) {
151
146
element . html ( message ) ;
152
147
} ) ;
153
148
}
154
149
} ) ;
155
150
}
156
151
} ;
157
- } )
158
-
159
- . directive ( 'validationMessages' , function ( ) {
160
- return {
161
- restrict : 'E' ,
162
- templateUrl : 'validationMessages.html'
163
- } ;
164
152
} ) ;
0 commit comments