8 Reusable Functions To Make Your Angular App Accessible
Tags: Accessibility, JavaScript, Front End Development, Web Development, Angular

8 Reusable Functions To Make Your Angular App Accessible

Angular Accessibility Part 3

Accessibility plays a vital role in any Single Page Applications. As Angular growing by leaps and bounds, supporting the accessibility in Angular Application becomes quite complex until we know all accessibility concepts.

Here I am coming with the different guidelines to implement accessibility in Angular Applications.

Before starting the implementation techniques, Let’s check common rules to follow while implementing accessibility.

If you would like to learn about how to implement skip to main content in your application please check out this article.

Let’s Start our main discussion

Today we are planning to create a reusable directive that helps to maintain the accessibility in Angular Applications.

We will create eight different reusable components to support the accessibility in our application.

1. Assigning an ID to the controls

The id the attribute specifies a unique id for an HTML element. The value of the id attribute must be unique within the HTML document.

We can create the function to add the id to the form controls automatically based on the form controls.

Let’s check the function.

this.renderer.setAttribute(

this.hostElement.nativeElement,

'id',

this.control.name,

);

What are renderer and hostElement here?

Let’s understand in brief.

renderer: renderer is defined from the Renderer2 which allows manipulating the DOM elements without having the whole DOM access. We can create an element from Renderer2 and can change or add the property with the Renderer2. Here we have used renderer to add the id to the controls.

hostElement: hostElement is defined from the ElementRef which is the wrapper around the native DOM element. It contains nativeElement property which holds the reference of the DOM Object. To manipulate DOM we can use nativeElement property.

2.Setting aria-labelledby tag to the controls

The aria-labelledby attribute establishes relationships between objects and their label(s), and its value should be one or more element IDs, which refer to elements that have the text needed for labeling. List multiple-element IDs in a space-delimited fashion.

Let’s take a small example.

<div id="signup">Create User</div>



<div>

<div id="name">Name</div>

<input type="text" aria-labelledby="signup name"/>

</div>

<div>

<div id="address">Address</div>

<input type="text" aria-labelledby="signup address"/>

</div>

Here the aria-labelledby will take multiple IDs to provide the reference to the child elements. This is very useful when the screen reader is announcing the label with the parent form name.

Let’s check the function to add the aria-labelledby tag dynamically.

this.renderer.setAttribute(

this.hostElement.nativeElement,

'aria-labelledby',

`${this.control.name}-label`,

);

3. Setting For Label to the controls

To understand this, Let’s check one of the answers given in the stack overflow by Darin Dimitrov.

The for the attribute is used in labels. It refers to the id of the element this label is associated with.

For example:

<label for="username">Username</label>

<input type="text" id="username" name="username" />

Now when the user clicks with the mouse on the username text the browser will automatically put the focus on the corresponding input field. This also works with other input elements such as <textbox> and <select> .

Let’s create a function to add for label with the id dynamically in the application.

const closestPrevSiblingLabel = $(this.hostElement.nativeElement)

.prevAll('label:first')

.get(0); if (closestPrevSiblingLabel) {

if (typeof this.control.name === 'string') {

this.renderer.setAttribute(

closestPrevSiblingLabel,

'for',

this.control.name,

);

}

this.renderer.setAttribute(

closestPrevSiblingLabel,

'id',

`${this.control.name}-label`,

);

}

What is closestPrevSiblingLabel doing?

We are using the prevAll() method which returns all previous sibling elements of the selected element and we are selecting the first element label using get(0)

4.Setting aria-required or aria-invalid tag to the controls

Let’s understand what is this aria-required and aria-invalid.

aria-required: The aria-required the attribute is used to indicate that user input is required on an element before a form can be submitted. This attribute can be used with any typical HTML form element; it is not limited to elements that have an ARIA role assigned.

aria-invalid: The aria-invalid the attribute is used to indicate that the value entered into an input field does not conform to the format expected by the application. This may include formats such as email addresses or telephone numbers. aria-invalid can also be used to indicate that a required field has not been filled in. The attribute should be programmatically set as a result of a validation process.

Let’s create a function which checks the control has the required field or not? If it has we will set the aria-required field as true. Same if the control is invalid then we will add the aria-invalid as true.

ValidatorUtils.hasRequiredField(this.control.control as AbstractControl) ?

this.renderer.setAttribute(

this.hostElement.nativeElement,

'aria-required',

`true`,

) :

this.renderer.removeAttribute(

this.hostElement.nativeElement,

'aria-required',

); // Set `aria-invalid` for screen reader.

if (this.control.touched) {

this.control.invalid ?

this.renderer.setAttribute(

this.hostElement.nativeElement,

'aria-invalid',

`true`,

) :

this.renderer.removeAttribute(

this.hostElement.nativeElement,

'aria-invalid',

);

}

Here we have added one utility function to check that control has the required field or not?



if (abstractControl.validator) {

const validator = abstractControl.validator({}

as AbstractControl);

if (validator && validator.required) {

return true;

}

}

//

// tslint:disable-next-line:no-string-literal

if (abstractControl['controls']) {

//

// tslint:disable-next-line:no-string-literal

for (const controlName in abstractControl['controls']) {

//

if (abstractControl['controls'][controlName]) {

//

if (

ValidatorUtils.hasRequiredField(

//

abstractControl['controls'][controlName],

)

) {

return true;

}

}

}

}

return false;

} static hasRequiredField(abstractControl: AbstractControl): boolean {if (abstractControl.validator) {const validator = abstractControl.validator({}as AbstractControl);if (validator && validator.required) {return true;// @ts -ignore// tslint:disable-next-line:no-string-literalif (abstractControl['controls']) {// @ts -ignore// tslint:disable-next-line:no-string-literalfor (const controlName in abstractControl['controls']) {// @ts -ignoreif (abstractControl['controls'][controlName]) {// @ts -ignoreif (ValidatorUtils.hasRequiredField(// @ts -ignoreabstractControl['controls'][controlName],) {return true;return false;

5. Setting the aria-expanded tag to the dropdowns

Let’s understand what is an aria-expanded tag.

aria-expanded: Indicates whether the element or another grouping element it controls, is currently expanded or collapsed.

Let’s create a function to add the aria-expanded tag to the controls.

this.renderer.setAttribute(

this.hostElement.nativeElement,

'aria-expanded',

`${this.hostNgSelect.isOpen}`,

);

What is the hostNgSelect here?

hostNgSelect is the property of the NgSelectComponent which provides all the properties of the ng-select. Here we have used to set the aria-expanded tag in the control.

6. Supporting other dropdown features for accessibility

Let’s understand some of the tags before jumping to the functions.

role: The role the attribute describes the role of an element in programs that can make use of it, such as screen readers or magnifiers.

combobox: Combobox is used for building forms in HTML. In which users are able to select an option from the drop-down list as per their selections.

aria-autocomplete: Indicates whether inputting text could trigger the display of one or more predictions of the user’s intended value for input and specifies how predictions would be presented if they are made.

tabindex: The tabindex the global attribute indicates that its elements can be focused, and where it participates in sequential keyboard navigation (usually with the Tab key, hence the name).

Let’s create the functions to add these tags dynamically.

this.renderer.setAttribute(

this.hostElement.nativeElement,

'role',

'combobox',

);

this.renderer.setAttribute(

this.hostElement.nativeElement,

'tabindex',

'0',

);

this.renderer.setAttribute(

this.hostElement.nativeElement,

'aria-autocomplete',

'none',

);

7. Trim and ignore whitespace in the controls.

Unnecessary whitespace sometimes causes issues when we are sending data to backend services. We can remove the whitespace from the controls directly using the function.

if (

!this.ignoreWhitespaceTrim &&

this.isTruthy(this.control.value) &&

this.isString(this.control.value)

) {

this.control.control?.setValue((this.control.value as string).trim(), {

emitEvent: false,

});

}

8. Disable password Managers

data-lpignore used to ignore the password managers for the input.

Let’s create a function to add this attribute.

this.renderer.setAttribute(

this.hostElement.nativeElement,

'data-lpignore',

'true',

);

Yeah! we have completed all the functions. Let’s add them together to create reusable components to support the accessibility in the angular application.

References:

Are you preparing for interviews? Here are frequently asked interview questions in Angular. It covers the Latest interview questions for Angular and Frontend development. Let’s check how many of these questions you can answer?