16
16
*
17
17
* For ngAria to do its magic, simply include the module `ngAria` as a dependency. The following
18
18
* directives are supported:
19
- * `ngModel`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`, `ngDblClick`, and `ngMessages`.
19
+ * `ngModel`, `ngChecked`, `ngRequired`, `ngValue`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`,
20
+ * `ngDblClick`, and `ngMessages`.
20
21
*
21
22
* Below is a more detailed breakdown of the attributes handled by ngAria:
22
23
*
23
24
* | Directive | Supported Attributes |
24
25
* |---------------------------------------------|----------------------------------------------------------------------------------------|
26
+ * | {@link ng.directive:ngModel ngModel} | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required, input roles |
25
27
* | {@link ng.directive:ngDisabled ngDisabled} | aria-disabled |
28
+ * | {@link ng.directive:input ngRequired} | aria-required |
29
+ * | {@link ng.directive:ngChecked ngChecked} | aria-checked |
30
+ * | {@link ng.directive:ngValue ngValue} | aria-checked |
26
31
* | {@link ng.directive:ngShow ngShow} | aria-hidden |
27
32
* | {@link ng.directive:ngHide ngHide} | aria-hidden |
28
33
* | {@link ng.directive:ngDblclick ngDblclick} | tabindex |
29
34
* | {@link module:ngMessages ngMessages} | aria-live |
30
- * | {@link ng.directive:ngModel ngModel} | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required, input roles |
31
- * | {@link ng.directive:ngClick ngClick} | tabindex, keypress event, button role |
35
+ * | {@link ng.directive:ngClick ngClick} | tabindex, keypress event, button role |
32
36
*
33
37
* Find out more information about each directive by reading the
34
38
* {@link guide/accessibility ngAria Developer Guide}.
@@ -90,7 +94,6 @@ function $AriaProvider() {
90
94
ariaDisabled : true ,
91
95
ariaRequired : true ,
92
96
ariaInvalid : true ,
93
- ariaMultiline : true ,
94
97
ariaValue : true ,
95
98
tabindex : true ,
96
99
bindKeypress : true ,
@@ -108,11 +111,10 @@ function $AriaProvider() {
108
111
* - **ariaDisabled** – `{boolean}` – Enables/disables aria-disabled tags
109
112
* - **ariaRequired** – `{boolean}` – Enables/disables aria-required tags
110
113
* - **ariaInvalid** – `{boolean}` – Enables/disables aria-invalid tags
111
- * - **ariaMultiline** – `{boolean}` – Enables/disables aria-multiline tags
112
114
* - **ariaValue** – `{boolean}` – Enables/disables aria-valuemin, aria-valuemax and aria-valuenow tags
113
115
* - **tabindex** – `{boolean}` – Enables/disables tabindex tags
114
- * - **bindKeypress** – `{boolean}` – Enables/disables keypress event binding on `< div> ` and
115
- * `<li> ` elements with ng-click
116
+ * - **bindKeypress** – `{boolean}` – Enables/disables keypress event binding on `div` and
117
+ * `li ` elements with ng-click
116
118
* - **bindRoleForClick** – `{boolean}` – Adds role=button to non-interactive elements like `div`
117
119
* using ng-click, making them more accessible to users of assistive technologies
118
120
*
@@ -151,28 +153,31 @@ function $AriaProvider() {
151
153
*
152
154
*```js
153
155
* ngAriaModule.directive('ngDisabled', ['$aria', function($aria) {
154
- * return $aria.$$watchExpr('ngDisabled', 'aria-disabled');
156
+ * return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nodeBlackList, false );
155
157
* }])
156
158
*```
157
159
* Shown above, the ngAria module creates a directive with the same signature as the
158
160
* traditional `ng-disabled` directive. But this ngAria version is dedicated to
159
- * solely managing accessibility attributes. The internal `$aria` service is used to watch the
160
- * boolean attribute `ngDisabled`. If it has not been explicitly set by the developer,
161
- * `aria-disabled` is injected as an attribute with its value synchronized to the value in
162
- * `ngDisabled`.
161
+ * solely managing accessibility attributes on custom elements . The internal `$aria` service is
162
+ * used to watch the boolean attribute `ngDisabled`. If it has not been explicitly set by the
163
+ * developer, `aria-disabled` is injected as an attribute with its value synchronized to the
164
+ * value in `ngDisabled`.
163
165
*
164
166
* Because ngAria hooks into the `ng-disabled` directive, developers do not have to do
165
167
* anything to enable this feature. The `aria-disabled` attribute is automatically managed
166
168
* simply as a silent side-effect of using `ng-disabled` with the ngAria module.
167
169
*
168
170
* The full list of directives that interface with ngAria:
169
171
* * **ngModel**
172
+ * * **ngChecked**
173
+ * * **ngRequired**
174
+ * * **ngDisabled**
175
+ * * **ngValue**
170
176
* * **ngShow**
171
177
* * **ngHide**
172
178
* * **ngClick**
173
179
* * **ngDblclick**
174
180
* * **ngMessages**
175
- * * **ngDisabled**
176
181
*
177
182
* Read the {@link guide/accessibility ngAria Developer Guide} for a thorough explanation of each
178
183
* directive.
@@ -198,13 +203,25 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
198
203
. directive ( 'ngHide' , [ '$aria' , function ( $aria ) {
199
204
return $aria . $$watchExpr ( 'ngHide' , 'aria-hidden' , [ ] , false ) ;
200
205
} ] )
201
- . directive ( 'ngModel' , [ '$aria' , function ( $aria ) {
206
+ . directive ( 'ngValue' , [ '$aria' , function ( $aria ) {
207
+ return $aria . $$watchExpr ( 'ngValue' , 'aria-checked' , nodeBlackList , false ) ;
208
+ } ] )
209
+ . directive ( 'ngChecked' , [ '$aria' , function ( $aria ) {
210
+ return $aria . $$watchExpr ( 'ngChecked' , 'aria-checked' , nodeBlackList , false ) ;
211
+ } ] )
212
+ . directive ( 'ngRequired' , [ '$aria' , function ( $aria ) {
213
+ return $aria . $$watchExpr ( 'ngRequired' , 'aria-required' , nodeBlackList , false ) ;
214
+ } ] )
215
+ . directive ( 'ngModel' , [ '$aria' , '$parse' , function ( $aria , $parse ) {
202
216
203
- function shouldAttachAttr ( attr , normalizedAttr , elem ) {
204
- return $aria . config ( normalizedAttr ) && ! elem . attr ( attr ) ;
217
+ function shouldAttachAttr ( attr , normalizedAttr , elem , allowBlacklistEls ) {
218
+ return $aria . config ( normalizedAttr ) && ! elem . attr ( attr ) && ( allowBlacklistEls || ! isNodeOneOf ( elem , nodeBlackList ) ) ;
205
219
}
206
220
207
221
function shouldAttachRole ( role , elem ) {
222
+ // if element does not have role attribute
223
+ // AND element type is equal to role (if custom element has a type equaling shape) <-- remove?
224
+ // AND element is not INPUT
208
225
return ! elem . attr ( 'role' ) && ( elem . attr ( 'type' ) === role ) && ( elem [ 0 ] . nodeName !== 'INPUT' ) ;
209
226
}
210
227
@@ -214,8 +231,7 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
214
231
215
232
return ( ( type || role ) === 'checkbox' || role === 'menuitemcheckbox' ) ? 'checkbox' :
216
233
( ( type || role ) === 'radio' || role === 'menuitemradio' ) ? 'radio' :
217
- ( type === 'range' || role === 'progressbar' || role === 'slider' ) ? 'range' :
218
- ( type || role ) === 'textbox' || elem [ 0 ] . nodeName === 'TEXTAREA' ? 'multiline' : '' ;
234
+ ( type === 'range' || role === 'progressbar' || role === 'slider' ) ? 'range' : '' ;
219
235
}
220
236
221
237
return {
@@ -227,37 +243,26 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
227
243
228
244
return {
229
245
pre : function ( scope , elem , attr , ngModel ) {
230
- if ( shape === 'checkbox' && attr . type !== 'checkbox' ) {
246
+ if ( shape === 'checkbox' ) {
231
247
//Use the input[checkbox] $isEmpty implementation for elements with checkbox roles
232
248
ngModel . $isEmpty = function ( value ) {
233
249
return value === false ;
234
250
} ;
235
251
}
236
252
} ,
237
253
post : function ( scope , elem , attr , ngModel ) {
238
- var needsTabIndex = shouldAttachAttr ( 'tabindex' , 'tabindex' , elem )
239
- && ! isNodeOneOf ( elem , nodeBlackList ) ;
254
+ var needsTabIndex = shouldAttachAttr ( 'tabindex' , 'tabindex' , elem , false ) ;
240
255
241
256
function ngAriaWatchModelValue ( ) {
242
257
return ngModel . $modelValue ;
243
258
}
244
259
245
- function getRadioReaction ( ) {
246
- if ( needsTabIndex ) {
247
- needsTabIndex = false ;
248
- return function ngAriaRadioReaction ( newVal ) {
249
- var boolVal = ( attr . value == ngModel . $viewValue ) ;
250
- elem . attr ( 'aria-checked' , boolVal ) ;
251
- elem . attr ( 'tabindex' , 0 - ! boolVal ) ;
252
- } ;
253
- } else {
254
- return function ngAriaRadioReaction ( newVal ) {
255
- elem . attr ( 'aria-checked' , ( attr . value == ngModel . $viewValue ) ) ;
256
- } ;
257
- }
260
+ function getRadioReaction ( newVal ) {
261
+ var boolVal = ( attr . value == ngModel . $viewValue ) ;
262
+ elem . attr ( 'aria-checked' , boolVal ) ;
258
263
}
259
264
260
- function ngAriaCheckboxReaction ( ) {
265
+ function getCheckboxReaction ( ) {
261
266
elem . attr ( 'aria-checked' , ! ngModel . $isEmpty ( ngModel . $viewValue ) ) ;
262
267
}
263
268
@@ -267,9 +272,9 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
267
272
if ( shouldAttachRole ( shape , elem ) ) {
268
273
elem . attr ( 'role' , shape ) ;
269
274
}
270
- if ( shouldAttachAttr ( 'aria-checked' , 'ariaChecked' , elem ) ) {
275
+ if ( shouldAttachAttr ( 'aria-checked' , 'ariaChecked' , elem , false ) ) {
271
276
scope . $watch ( ngAriaWatchModelValue , shape === 'radio' ?
272
- getRadioReaction ( ) : ngAriaCheckboxReaction ) ;
277
+ getRadioReaction : getCheckboxReaction ) ;
273
278
}
274
279
if ( needsTabIndex ) {
275
280
elem . attr ( 'tabindex' , 0 ) ;
@@ -306,22 +311,17 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
306
311
elem . attr ( 'tabindex' , 0 ) ;
307
312
}
308
313
break ;
309
- case 'multiline' :
310
- if ( shouldAttachAttr ( 'aria-multiline' , 'ariaMultiline' , elem ) ) {
311
- elem . attr ( 'aria-multiline' , true ) ;
312
- }
313
- break ;
314
314
}
315
315
316
- if ( ngModel . $validators . required && shouldAttachAttr ( 'aria-required' , 'ariaRequired' , elem ) ) {
317
- scope . $watch ( function ngAriaRequiredWatch ( ) {
318
- return ngModel . $error . required ;
319
- } , function ngAriaRequiredReaction ( newVal ) {
320
- elem . attr ( 'aria-required' , ! ! newVal ) ;
316
+ if ( ! attr . hasOwnProperty ( 'ngRequired' ) && ngModel . $validators . required
317
+ && shouldAttachAttr ( 'aria-required' , 'ariaRequired' , elem , false ) ) {
318
+ // ngModel.$error.required is undefined on custom controls
319
+ attr . $observe ( 'required' , function ( ) {
320
+ elem . attr ( 'aria-required' , ! ! attr [ 'required' ] ) ;
321
321
} ) ;
322
322
}
323
323
324
- if ( shouldAttachAttr ( 'aria-invalid' , 'ariaInvalid' , elem ) ) {
324
+ if ( shouldAttachAttr ( 'aria-invalid' , 'ariaInvalid' , elem , true ) ) {
325
325
scope . $watch ( function ngAriaInvalidWatch ( ) {
326
326
return ngModel . $invalid ;
327
327
} , function ngAriaInvalidReaction ( newVal ) {
@@ -334,7 +334,7 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
334
334
} ;
335
335
} ] )
336
336
. directive ( 'ngDisabled' , [ '$aria' , function ( $aria ) {
337
- return $aria . $$watchExpr ( 'ngDisabled' , 'aria-disabled' , [ ] ) ;
337
+ return $aria . $$watchExpr ( 'ngDisabled' , 'aria-disabled' , nodeBlackList , false ) ;
338
338
} ] )
339
339
. directive ( 'ngMessages' , function ( ) {
340
340
return {
0 commit comments