docs: improve accessibility of reactive-forms example (#41252)

PR Close #41252
This commit is contained in:
Kapunahele Wong 2021-03-16 14:22:09 -04:00 committed by Jessica Janiuk
parent 0031c8cf41
commit 386550cdf3
16 changed files with 71 additions and 150 deletions

View File

@ -54,7 +54,7 @@ describe('Reactive forms', () => {
describe('Profile Editor', () => { describe('Profile Editor', () => {
const firstNameInput = getInput('firstName'); const firstNameInput = getInput('firstName');
const streetInput = getInput('street'); const streetInput = getInput('street');
const addAliasButton = element(by.buttonText('Add Alias')); const addAliasButton = element(by.buttonText('+ Add another alias'));
const updateButton = profileEditor.element(by.buttonText('Update Profile')); const updateButton = profileEditor.element(by.buttonText('Update Profile'));
const profile: Record<string, string | number> = { const profile: Record<string, string | number> = {
firstName: 'John', firstName: 'John',
@ -121,8 +121,7 @@ describe('Reactive forms', () => {
) )
); );
const aliasInputs = profileEditor.all(by.cssContainingText('label', 'Alias')); const aliasInput = profileEditor.all(by.css('#alias-0'));
const aliasInput = aliasInputs.get(0).element(by.css('input'));
await aliasInput.sendKeys(aliasText); await aliasInput.sendKeys(aliasText);
const formValueElement = profileEditor.all(by.cssContainingText('p', 'Form Value:')); const formValueElement = profileEditor.all(by.cssContainingText('p', 'Form Value:'));
const formValue = await formValueElement.getText(); const formValue = await formValueElement.getText();

View File

@ -0,0 +1,3 @@
nav a {
padding: 1rem;
}

View File

@ -1,19 +1,10 @@
:host {
display: flex;
flex-direction: column;
padding-top: 24px;
}
label { label {
display: block;
width: 6em;
margin: .5em 0;
color: #607D8B;
font-weight: bold; font-weight: bold;
padding-bottom: .5rem;
padding-top: 1rem;
display: inline-block;
} }
input { button {
height: 2em; max-width: 300px;
font-size: 1em; }
padding-left: .4em;
}

View File

@ -1,21 +1,12 @@
<!-- #docregion control-binding --> <!-- #docregion control-binding -->
<label> <label for="name">Name: </label>
Name: <input id="name" type="text" [formControl]="name">
<input type="text" [formControl]="name">
</label>
<!-- #enddocregion control-binding --> <!-- #enddocregion control-binding -->
<!-- #docregion display-value --> <!-- #docregion display-value -->
<p>Value: {{ name.value }}</p>
<p>
Value: {{ name.value }}
</p>
<!-- #enddocregion display-value --> <!-- #enddocregion display-value -->
<!-- #docregion update-value --> <!-- #docregion update-value -->
<button (click)="updateName()">Update Name</button>
<p>
<button (click)="updateName()">Update Name</button>
</p>
<!-- #enddocregion update-value --> <!-- #enddocregion update-value -->

View File

@ -1,65 +1,48 @@
<!-- #docplaster --> <!-- #docplaster -->
<!-- #docregion formgroup --> <!-- #docregion formgroup -->
<form [formGroup]="profileForm"> <form [formGroup]="profileForm">
<label>
First Name:
<input type="text" formControlName="firstName">
</label>
<label> <label for="first-name">First Name: </label>
Last Name: <input id="first-name" type="text" formControlName="firstName">
<input type="text" formControlName="lastName">
</label> <label for="last-name">Last Name: </label>
<input id="last-name" type="text" formControlName="lastName">
<!-- #enddocregion formgroup --> <!-- #enddocregion formgroup -->
<!-- #docregion formgroupname --> <!-- #docregion formgroupname -->
<div formGroupName="address"> <div formGroupName="address">
<h3>Address</h3> <h2>Address</h2>
<label> <label for="street">Street: </label>
Street: <input id="street" type="text" formControlName="street">
<input type="text" formControlName="street">
</label>
<label> <label for="city">City: </label>
City: <input id="city" type="text" formControlName="city">
<input type="text" formControlName="city">
</label>
<label>
State:
<input type="text" formControlName="state">
</label>
<label> <label for="state">State: </label>
Zip Code: <input id="state" type="text" formControlName="state">
<input type="text" formControlName="zip">
</label> <label for="zip">Zip Code: </label>
<input id="zip" type="text" formControlName="zip">
</div> </div>
<!-- #enddocregion formgroupname --> <!-- #enddocregion formgroupname -->
<div formArrayName="aliases"> <div formArrayName="aliases">
<h3>Aliases</h3> <button (click)="addAlias()">Add Alias</button> <h2>Aliases</h2>
<button (click)="addAlias()">+ Add another alias</button>
<div *ngFor="let address of aliases.controls; let i=index"> <div *ngFor="let address of aliases.controls; let i=index">
<!-- The repeated alias template --> <!-- The repeated alias template -->
<label> <label for="alias-{{ i }}">Alias: </label>
Alias: <input id="alias-{{ i }}" type="text" [formControlName]="i">
<input type="text" [formControlName]="i">
</label>
</div> </div>
</div> </div>
<!-- #docregion formgroup --> <!-- #docregion formgroup -->
</form> </form>
<!-- #enddocregion formgroup --> <!-- #enddocregion formgroup -->
<p> <p>Form Value: {{ profileForm.value | json }}</p>
Form Value: {{ profileForm.value | json }}
</p>
<!-- #docregion patch-value --> <!-- #docregion patch-value -->
<p> <button (click)="updateProfile()">Update Profile</button>
<button (click)="updateProfile()">Update Profile</button> <!-- #enddocregion patch-value -->
</p>
<!-- #enddocregion patch-value -->

View File

@ -1,39 +1,11 @@
/* ProfileEditorComponent's private CSS styles */ /* ProfileEditorComponent's private CSS styles */
:host {
display: flex; form {
flex-direction: column; padding-top: 1rem;
padding-top: 24px;
} }
label { label {
display: block; display: block;
width: 6em;
margin: .5em 0; margin: .5em 0;
color: #607D8B;
font-weight: bold; font-weight: bold;
} }
input {
height: 2em;
font-size: 1em;
padding-left: .4em;
}
button {
font-family: Arial, sans-serif;
background-color: #eee;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #cfd8dc;
}
button:disabled {
background-color: #eee;
color: #ccc;
cursor: auto;
}

View File

@ -2,72 +2,54 @@
<!-- #docregion ng-submit --> <!-- #docregion ng-submit -->
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()"> <form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
<!-- #enddocregion ng-submit --> <!-- #enddocregion ng-submit -->
<label> <label for="first-name">First Name: </label>
First Name: <input id="first-name" type="text" formControlName="firstName" required>
<input type="text" formControlName="firstName" required>
</label>
<label> <label for="last-name">Last Name: </label>
Last Name: <input id="last-name" type="text" formControlName="lastName">
<input type="text" formControlName="lastName">
</label>
<div formGroupName="address"> <div formGroupName="address">
<h3>Address</h3> <h2>Address</h2>
<label> <label for="street">Street: </label>
Street: <input id="street" type="text" formControlName="street">
<input type="text" formControlName="street">
</label>
<label> <label for="city">City: </label>
City: <input id="city" type="text" formControlName="city">
<input type="text" formControlName="city">
</label>
<label>
State:
<input type="text" formControlName="state">
</label>
<label> <label for="state">State: </label>
Zip Code: <input id="state" type="text" formControlName="state">
<input type="text" formControlName="zip">
</label> <label for="zip">Zip Code: </label>
<input id="zip"type="text" formControlName="zip">
</div> </div>
<!-- #docregion formarrayname --> <!-- #docregion formarrayname -->
<div formArrayName="aliases"> <div formArrayName="aliases">
<h3>Aliases</h3> <button (click)="addAlias()">Add Alias</button> <h2>Aliases</h2>
<button (click)="addAlias()">+ Add another alias</button>
<div *ngFor="let alias of aliases.controls; let i=index"> <div *ngFor="let alias of aliases.controls; let i=index">
<!-- The repeated alias template --> <!-- The repeated alias template -->
<label> <label for="alias-{{ i }}">Alias:</label>
Alias: <input id="alias-{{ i }}" type="text" [formControlName]="i">
<input type="text" [formControlName]="i">
</label>
</div> </div>
</div> </div>
<!-- #enddocregion formarrayname --> <!-- #enddocregion formarrayname -->
<!-- #docregion submit-button --> <!-- #docregion submit-button -->
<p>Complete the form to enable button.</p>
<button type="submit" [disabled]="!profileForm.valid">Submit</button> <button type="submit" [disabled]="!profileForm.valid">Submit</button>
<!-- #enddocregion submit-button --> <!-- #enddocregion submit-button -->
</form> </form>
<hr> <hr>
<p> <p>Form Value: {{ profileForm.value | json }}</p>
Form Value: {{ profileForm.value | json }}
</p>
<!-- #docregion display-status --> <!-- #docregion display-status -->
<p>Form Status: {{ profileForm.status }}</p>
<p>
Form Status: {{ profileForm.status }}
</p>
<!-- #enddocregion display-status --> <!-- #enddocregion display-status -->
<p> <button (click)="updateProfile()">Update Profile</button>
<button (click)="updateProfile()">Update Profile</button>
</p>

View File

@ -13,7 +13,7 @@ Try this <live-example title="Reactive Forms in Stackblitz">Reactive Forms live-
Before going further into reactive forms, you should have a basic understanding of the following: Before going further into reactive forms, you should have a basic understanding of the following:
* [TypeScript](https://www.typescriptlang.org/ "The TypeScript language") programming. * [TypeScript](https://www.typescriptlang.org/ "The TypeScript language") programming.
* Angular app-design fundamentals, as described in [Angular Concepts](guide/architecture "Introduction to Angular concepts."). * Angular application-design fundamentals, as described in [Angular Concepts](guide/architecture "Introduction to Angular concepts.").
* The form-design concepts that are presented in [Introduction to Forms](guide/forms-overview "Overview of Angular forms."). * The form-design concepts that are presented in [Introduction to Forms](guide/forms-overview "Overview of Angular forms.").
{@a intro} {@a intro}
@ -85,7 +85,7 @@ The form control assigned to `name` is displayed when the component is added to
<code-example path="reactive-forms/src/app/app.component.1.html" region="app-name-editor" header="src/app/app.component.html (name editor)"></code-example> <code-example path="reactive-forms/src/app/app.component.1.html" region="app-name-editor" header="src/app/app.component.html (name editor)"></code-example>
<div class="lightbox"> <div class="lightbox">
<img src="generated/images/guide/reactive-forms/name-editor-1.png" alt="Name Editor"> <img src="generated/images/guide/reactive-forms/name-editor-1.png" alt="Name Editor, which has a name label and an input so the user can enter a name">
</div> </div>
{@a display-value} {@a display-value}
@ -126,7 +126,7 @@ Update the template with a button to simulate a name update. When you click the
The form model is the source of truth for the control, so when you click the button, the value of the input is changed within the component class, overriding its current value. The form model is the source of truth for the control, so when you click the button, the value of the input is changed within the component class, overriding its current value.
<div class="lightbox"> <div class="lightbox">
<img src="generated/images/guide/reactive-forms/name-editor-2.png" alt="Name Editor Update"> <img src="generated/images/guide/reactive-forms/name-editor-2.gif" alt="Name Editor Update with a name label, the name Nancy in the input, text specifying that the value of the input is Nancy and an Update Name button">
</div> </div>
<div class="alert is-helpful"> <div class="alert is-helpful">
@ -217,7 +217,7 @@ To display the `ProfileEditor` component that contains the form, add it to a com
`ProfileEditor` allows you to manage the form control instances for the `firstName` and `lastName` controls within the form group instance. `ProfileEditor` allows you to manage the form control instances for the `firstName` and `lastName` controls within the form group instance.
<div class="lightbox"> <div class="lightbox">
<img src="generated/images/guide/reactive-forms/profile-editor-1.png" alt="Profile Editor"> <img src="generated/images/guide/reactive-forms/profile-editor-1.gif" alt="Profile Editor with labels and inputs for first and last name as well as a submit button">
</div> </div>
{@a nested-groups} {@a nested-groups}
@ -254,7 +254,7 @@ Add the `address` form group containing the `street`, `city`, `state`, and `zip`
The `ProfileEditor` form is displayed as one group, but the model is broken down further to represent the logical grouping areas. The `ProfileEditor` form is displayed as one group, but the model is broken down further to represent the logical grouping areas.
<div class="lightbox"> <div class="lightbox">
<img src="generated/images/guide/reactive-forms/profile-editor-2.png" alt="Profile Editor Update"> <img src="generated/images/guide/reactive-forms/profile-editor-2.png" alt="Profile editor update adding address inputs, instructive text for filling out the form to enable the submit button, and a disabled submit button">
</div> </div>
<div class="alert is-helpful"> <div class="alert is-helpful">
@ -388,7 +388,7 @@ Display the current status of `profileForm` using interpolation.
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="display-status" header="src/app/profile-editor/profile-editor.component.html (display status)"></code-example> <code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="display-status" header="src/app/profile-editor/profile-editor.component.html (display status)"></code-example>
<div class="lightbox"> <div class="lightbox">
<img src="generated/images/guide/reactive-forms/profile-editor-3.png" alt="Profile Editor Validation"> <img src="generated/images/guide/reactive-forms/profile-editor-3.png" alt="Profile Editor with validation status of invalid">
</div> </div>
The **Submit** button is disabled because `profileForm` is invalid due to the required `firstName` form control. After you fill out the `firstName` input, the form becomes valid and the **Submit** button is enabled. The **Submit** button is disabled because `profileForm` is invalid due to the required `firstName` form control. After you fill out the `firstName` input, the form becomes valid and the **Submit** button is enabled.
@ -466,7 +466,7 @@ Add the template HTML below after the `<div>` closing the `formGroupName` elemen
The `*ngFor` directive iterates over each form control instance provided by the aliases form array instance. Because form array elements are unnamed, you assign the index to the `i` variable and pass it to each control to bind it to the `formControlName` input. The `*ngFor` directive iterates over each form control instance provided by the aliases form array instance. Because form array elements are unnamed, you assign the index to the `i` variable and pass it to each control to bind it to the `formControlName` input.
<div class="lightbox"> <div class="lightbox">
<img src="generated/images/guide/reactive-forms/profile-editor-4.png" alt="Profile Editor Aliases"> <img src="generated/images/guide/reactive-forms/profile-editor-4.png" alt="Profile Editor with aliases section, which includes an alias label, input, and button for adding another alias text input">
</div> </div>
Each time a new alias instance is added, the new form array instance is provided its control based on the index. This allows you to track each individual control when calculating the status and value of the root control. Each time a new alias instance is added, the new form array instance is provided its control based on the index. This allows you to track each individual control when calculating the status and value of the root control.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 18 KiB