docs(guide): Update Dart forms chapter to alpha.52

closes #471
This commit is contained in:
Kathy Walrath 2015-12-10 12:43:13 -08:00
parent 92f1690b73
commit 8e8963270b
8 changed files with 91 additions and 91 deletions

View File

@ -4,16 +4,16 @@
<!-- #docregion edit-div --> <!-- #docregion edit-div -->
<div [hidden]="submitted"> <div [hidden]="submitted">
<h1>Hero Form</h1> <h1>Hero Form</h1>
<!-- #docregion ng-submit --> <!-- #docregion ngSubmit -->
<form (ng-submit)="onSubmit()" #hf="form"> <form (ngSubmit)="onSubmit()" #heroForm="ngForm">
<!-- #enddocregion ng-submit --> <!-- #enddocregion ngSubmit -->
<!-- #enddocregion edit-div --> <!-- #enddocregion edit-div -->
<div class="form-group"> <div class="form-group">
<label for="name">Name</label> <label for="name">Name</label>
<!-- #docregion name-with-error-msg --> <!-- #docregion name-with-error-msg -->
<input type="text" class="form-control" required <input type="text" class="form-control" required
[(ng-model)]="model.name" [(ngModel)]="model.name"
ng-control="name" #name="form" > ngControl="name" #name="ngForm" >
<div [hidden]="name.valid" class="alert alert-danger"> <div [hidden]="name.valid" class="alert alert-danger">
Name is required Name is required
</div> </div>
@ -23,22 +23,22 @@
<div class="form-group"> <div class="form-group">
<label for="alterEgo">Alter Ego</label> <label for="alterEgo">Alter Ego</label>
<input type="text" class="form-control" <input type="text" class="form-control"
[(ng-model)]="model.alterEgo" [(ngModel)]="model.alterEgo"
ng-control="alterEgo" > ngControl="alterEgo" >
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="power">Hero Power</label> <label for="power">Hero Power</label>
<select class="form-control" required <select class="form-control" required
[(ng-model)]="model.power" [(ngModel)]="model.power"
ng-control="power" > ngControl="power" >
<option *ng-for="#p of powers" [value]="p">{{p}}</option> <option *ngFor="#p of powers" [value]="p">{{p}}</option>
</select> </select>
</div> </div>
<!-- #docregion submit-button --> <!-- #docregion submit-button -->
<button type="submit" class="btn btn-default" <button type="submit" class="btn btn-default"
[disabled]="!hf.form.valid">Submit</button> [disabled]="!heroForm.form.valid">Submit</button>
<!-- #enddocregion submit-button --> <!-- #enddocregion submit-button -->
<!-- #docregion edit-div --> <!-- #docregion edit-div -->
</form> </form>

View File

@ -4,26 +4,26 @@
<form> <form>
<div class="form-group"> <div class="form-group">
<label for="name">Name</label> <label for="name">Name</label>
<!-- #docregion ng-control-1 --> <!-- #docregion ngControl-1 -->
<input type="text" class="form-control" required <input type="text" class="form-control" required
[(ng-model)]="model.name" [(ngModel)]="model.name"
ng-control="name" > ngControl="name" >
<!-- #enddocregion ng-control-1 --> <!-- #enddocregion ngControl-1 -->
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="alterEgo">Alter Ego</label> <label for="alterEgo">Alter Ego</label>
<input type="text" class="form-control" <input type="text" class="form-control"
[(ng-model)]="model.alterEgo" [(ngModel)]="model.alterEgo"
ng-control="alterEgo" > ngControl="alterEgo" >
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="power">Hero Power</label> <label for="power">Hero Power</label>
<select class="form-control" required <select class="form-control" required
[(ng-model)]="model.power" [(ngModel)]="model.power"
ng-control="power" > ngControl="power" >
<option *ng-for="#p of powers" [value]="p">{{p}}</option> <option *ngFor="#p of powers" [value]="p">{{p}}</option>
</select> </select>
</div> </div>

View File

@ -2,28 +2,28 @@
<div class="container"> <div class="container">
<h1>Hero Form</h1> <h1>Hero Form</h1>
<form> <form>
<!-- #docregion ng-model-2 --> <!-- #docregion ngModel-2 -->
{{diagnostic}} {{diagnostic}}
<div class="form-group"> <div class="form-group">
<label for="name">Name</label> <label for="name">Name</label>
<input type="text" class="form-control" required <input type="text" class="form-control" required
[(ng-model)]="model.name" > [(ngModel)]="model.name" >
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="alterEgo">Alter Ego</label> <label for="alterEgo">Alter Ego</label>
<input type="text" class="form-control" <input type="text" class="form-control"
[(ng-model)]="model.alterEgo"> [(ngModel)]="model.alterEgo">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="power">Hero Power</label> <label for="power">Hero Power</label>
<select class="form-control" required <select class="form-control" required
[(ng-model)]="model.power"> [(ngModel)]="model.power">
<option *ng-for="#p of powers" [value]="p">{{p}}</option> <option *ngFor="#p of powers" [value]="p">{{p}}</option>
</select> </select>
</div> </div>
<!-- #enddocregion ng-model-2 --> <!-- #enddocregion ngModel-2 -->
<button type="submit" class="btn btn-default">Submit</button> <button type="submit" class="btn btn-default">Submit</button>
</form> </form>

View File

@ -4,11 +4,11 @@
<form> <form>
<div class="form-group"> <div class="form-group">
<label for="name">Name</label> <label for="name">Name</label>
<!-- #docregion ng-model-1 --> <!-- #docregion ngModel-1 -->
<input type="text" class="form-control" required <input type="text" class="form-control" required
[(ng-model)]="model.name" > [(ngModel)]="model.name" >
TODO: remove this: {{model.name}} TODO: remove this: {{model.name}}
<!-- #enddocregion ng-model-1 --> <!-- #enddocregion ngModel-1 -->
</div> </div>
<div class="form-group"> <div class="form-group">
@ -20,7 +20,7 @@
<div class="form-group"> <div class="form-group">
<label for="power">Hero Power</label> <label for="power">Hero Power</label>
<select class="form-control" required> <select class="form-control" required>
<option *ng-for="#p of powers" [value]="p">{{p}}</option> <option *ngFor="#p of powers" [value]="p">{{p}}</option>
</select> </select>
</div> </div>
<!-- #enddocregion powers --> <!-- #enddocregion powers -->

View File

@ -4,25 +4,25 @@
<form> <form>
<div class="form-group"> <div class="form-group">
<label for="name">Name</label> <label for="name">Name</label>
<!-- #docregion ng-model-3 --> <!-- #docregion ngModel-3 -->
<input type="text" class="form-control" required <input type="text" class="form-control" required
[ng-model]="model.name" [ngModel]="model.name"
(ng-model-change)="model.name = $event" > (ngModel-change)="model.name = $event" >
TODO: remove this: {{model.name}} TODO: remove this: {{model.name}}
<!-- #enddocregion ng-model-3 --> <!-- #enddocregion ngModel-3 -->
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="alterEgo">Alter Ego</label> <label for="alterEgo">Alter Ego</label>
<input type="text" class="form-control" <input type="text" class="form-control"
[(ng-model)]="model.alterEgo"> [(ngModel)]="model.alterEgo">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="power">Hero Power</label> <label for="power">Hero Power</label>
<select class="form-control" required <select class="form-control" required
[(ng-model)]="model.power"> [(ngModel)]="model.power">
<option *ng-for="#p of powers" [value]="p">{{p}}</option> <option *ngFor="#p of powers" [value]="p">{{p}}</option>
</select> </select>
</div> </div>

View File

@ -4,27 +4,27 @@
<form> <form>
<div class="form-group"> <div class="form-group">
<label for="name">Name</label> <label for="name">Name</label>
<!-- #docregion ng-control-2 --> <!-- #docregion ngControl-2 -->
<input type="text" class="form-control" required <input type="text" class="form-control" required
[(ng-model)]="model.name" [(ngModel)]="model.name"
ng-control="name" #spy > ngControl="name" #spy >
TODO: remove this: {{spy.className}} TODO: remove this: {{spy.className}}
<!-- #enddocregion ng-control-2 --> <!-- #enddocregion ngControl-2 -->
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="alterEgo">Alter Ego</label> <label for="alterEgo">Alter Ego</label>
<input type="text" class="form-control" <input type="text" class="form-control"
[(ng-model)]="model.alterEgo" [(ngModel)]="model.alterEgo"
ng-control="alterEgo" > ngControl="alterEgo" >
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="power">Hero Power</label> <label for="power">Hero Power</label>
<select class="form-control" required <select class="form-control" required
[(ng-model)]="model.power" [(ngModel)]="model.power"
ng-control="power" > ngControl="power" >
<option *ng-for="#p of powers" [value]="p">{{p}}</option> <option *ngFor="#p of powers" [value]="p">{{p}}</option>
</select> </select>
</div> </div>

View File

@ -3,7 +3,7 @@ name: hero_form
description: Form example description: Form example
version: 0.0.1 version: 0.0.1
dependencies: dependencies:
angular2: 2.0.0-alpha.47 angular2: 2.0.0-alpha.52
browser: ^0.10.0 browser: ^0.10.0
transformers: transformers:
- angular2: - angular2:

View File

@ -20,11 +20,11 @@ include ../../../../_includes/_util-fns
- How to build an Angular form with a component and template - How to build an Angular form with a component and template
- The `ng-model` two-way data binding syntax for reading and writing values to input controls - The `ngModel` two-way data binding syntax for reading and writing values to input controls
- The `ng-control` directive to track the change state and validity of form controls - The `ngControl` directive to track the change state and validity of form controls
- The special CSS classes that `ng-control` adds to form controls and how to use them to provide strong visual feedback - The special CSS classes that `ngControl` adds to form controls and how to use them to provide strong visual feedback
- How to display validation errors to users and enable/disable form controls - How to display validation errors to users and enable/disable form controls
@ -79,11 +79,11 @@ figure.image-display
1. Create the `Hero` model class. 1. Create the `Hero` model class.
1. Create the component that controls the form. 1. Create the component that controls the form.
1. Create a template with the initial form layout. 1. Create a template with the initial form layout.
1. Add the **ng-model** directive to each form input control. 1. Add the **ngModel** directive to each form input control.
1. Add the **ng-control** directive to each form input control. 1. Add the **ngControl** directive to each form input control.
1. Add custom CSS to provide visual feedback. 1. Add custom CSS to provide visual feedback.
1. Show and hide validation error messages. 1. Show and hide validation error messages.
1. Handle form submission with **ng-submit**. 1. Handle form submission with **ngSubmit**.
1. Change the form's display after submission. 1. Change the form's display after submission.
:marked :marked
@ -236,7 +236,7 @@ figure.image-display
.l-main-section .l-main-section
:marked :marked
## Add powers with ***ng-for** ## Add powers with ***ngFor**
Our hero must choose one super power from a fixed list of Agency-approved powers. Our hero must choose one super power from a fixed list of Agency-approved powers.
We maintain that list internally (in `HeroFormComponent`). We maintain that list internally (in `HeroFormComponent`).
@ -255,7 +255,7 @@ figure.image-display
.l-main-section .l-main-section
:marked :marked
## Two-way data binding with ***ng-model** ## Two-way data binding with ***ngModel**
Running the app right now would be disappointing. Running the app right now would be disappointing.
figure.image-display figure.image-display
@ -275,7 +275,7 @@ figure.image-display
Find the `<input>` tag for Name and update it like this: Find the `<input>` tag for Name and update it like this:
+makeExample('forms/dart/lib/hero_form_component_ngmodel_ngfor.html', 'ng-model-1', 'lib/hero_form_component.html (excerpt)')(format=".") +makeExample('forms/dart/lib/hero_form_component_ngmodel_ngfor.html', 'ngModel-1', 'lib/hero_form_component.html (excerpt)')(format=".")
.l-sub-section .l-sub-section
:marked :marked
@ -284,32 +284,32 @@ figure.image-display
We left ourselves a note to throw it way when we're done. We left ourselves a note to throw it way when we're done.
:marked :marked
Focus on the binding syntax: `[(ng-model)]="..."`. Focus on the binding syntax: `[(ngModel)]="..."`.
If we ran the app right now and started typing in the Name input box, If we ran the app right now and started typing in the Name input box,
adding and deleting characters, we'd see them appearing and disappearing adding and deleting characters, we'd see them appearing and disappearing
from the interpolated text. from the interpolated text.
At some point it might look like this. At some point it might look like this.
figure.image-display figure.image-display
img(src="/resources/images/devguide/forms/ng-model-in-action.png" width="400px" alt="ng-model in action") img(src="/resources/images/devguide/forms/ng-model-in-action.png" width="400px" alt="ngModel in action")
:marked :marked
The diagnostic is evidence that values really are flowing from the input box to the model and The diagnostic is evidence that values really are flowing from the input box to the model and
back again. **That's two-way data binding!** back again. **That's two-way data binding!**
Let's add similar `[(ng-model)]` bindings to Alter Ego and Hero Power. Let's add similar `[(ngModel)]` bindings to Alter Ego and Hero Power.
We'll ditch the input box binding message We'll ditch the input box binding message
and add a new binding at the top to the component's `diagnostic` property. and add a new binding at the top to the component's `diagnostic` property.
Then we can confirm that two-way data binding works *for the entire Hero model*. Then we can confirm that two-way data binding works *for the entire Hero model*.
After revision, the core of the form should have three `[(ng-model)]` bindings that After revision, the core of the form should have three `[(ngModel)]` bindings that
look much like this: look much like this:
+makeExample('forms/dart/lib/hero_form_component_ngmodel2.html', 'ng-model-2', 'lib/hero_form_component.html (excerpt)')(format=".") +makeExample('forms/dart/lib/hero_form_component_ngmodel2.html', 'ngModel-2', 'lib/hero_form_component.html (excerpt)')(format=".")
:marked :marked
If we ran the app right now and changed every Hero model property, the form might look like this: If we ran the app right now and changed every Hero model property, the form might look like this:
figure.image-display figure.image-display
img(src="images/ng-model-in-action-2.png" width="500px" alt="ng-model in super action") img(src="images/ng-model-in-action-2.png" width="500px" alt="ngModel in super action")
:marked :marked
The diagnostic near the top of the form The diagnostic near the top of the form
confirms that our changes to the values are reflected in the model. confirms that our changes to the values are reflected in the model.
@ -318,9 +318,9 @@ figure.image-display
.l-sub-section .l-sub-section
h3 Inside [(ng-model)] h3 Inside [(ngModel)]
:marked :marked
*This section is an optional deep dive into [(ng-model)]. Not interested? Skip ahead!* *This section is an optional deep dive into [(ngModel)]. Not interested? Skip ahead!*
The punctuation in the binding syntax, <span style="font-family:courier"><b>[()]</b></span>, is a good clue to what's going on. The punctuation in the binding syntax, <span style="font-family:courier"><b>[()]</b></span>, is a good clue to what's going on.
@ -337,22 +337,22 @@ figure.image-display
In fact, we can break the `NgModel` binding into its two separate modes In fact, we can break the `NgModel` binding into its two separate modes
as in this rewrite of the Name `<input>` binding: as in this rewrite of the Name `<input>` binding:
+makeExample('forms/dart/lib/hero_form_component_ngmodelchange.html', 'ng-model-3')(format=".") +makeExample('forms/dart/lib/hero_form_component_ngmodelchange.html', 'ngModel-3')(format=".")
:marked :marked
<br>The property binding should feel familiar. The event binding might seem strange. <br>The property binding should feel familiar. The event binding might seem strange.
The name `ng-model-change` specifies an event property of the `NgModel` directive. The name `ngModel-change` specifies an event property of the `NgModel` directive.
When Angular sees a binding target in the form <span style="font-family:courier">[(abc)]</span>, When Angular sees a binding target in the form <span style="font-family:courier">[(abc)]</span>,
it expects the `abc` directive to have an `abc` input property and an `abc-change` output property. it expects the `abc` directive to have an `abc` input property and an `abc-change` output property.
The other oddity is the template expression, `model.name = $event`. The other oddity is the template expression, `model.name = $event`.
We're used to seeing an `$event` object coming from a DOM event. We're used to seeing an `$event` object coming from a DOM event.
The `ng-model-change` property doesn't produce a DOM event; it's an Angular `EventEmitter` The `ngModel-change` property doesn't produce a DOM event; it's an Angular `EventEmitter`
property that returns the input box value when it fires—which is precisely what property that returns the input box value when it fires—which is precisely what
we should assign to the model's `name` property. we should assign to the model's `name` property.
Nice to know but is it practical? `[(ng-model)]` is usually what we want, but Nice to know but is it practical? `[(ngModel)]` is usually what we want, but
we might split the binding when the event handling has to do something special we might split the binding when the event handling has to do something special
such as debounce or throttle the keystrokes. such as debounce or throttle the keystrokes.
@ -364,7 +364,7 @@ figure.image-display
.l-main-section .l-main-section
:marked :marked
## Track change-state and validity with **ng-control** ## Track change-state and validity with **ngControl**
A form isn't just about data binding. We'd also like to know the state of the controls on our form. A form isn't just about data binding. We'd also like to know the state of the controls on our form.
The `NgControl` directive keeps track of control state for us. The `NgControl` directive keeps track of control state for us.
@ -384,15 +384,15 @@ figure.image-display
control and make messages appear or disappear. control and make messages appear or disappear.
We'll explore those effects soon. Right now We'll explore those effects soon. Right now
let's **add `ng-control`to all three form controls**, let's **add `ngControl`to all three form controls**,
starting with the Name input box. starting with the Name input box.
+makeExample('forms/dart/lib/hero_form_component_ngcontrol.html', 'ng-control-1', 'lib/hero_form_component.html (excerpt)')(format=".") +makeExample('forms/dart/lib/hero_form_component_ngcontrol.html', 'ngControl-1', 'lib/hero_form_component.html (excerpt)')(format=".")
:marked :marked
Be sure to assign a unique name to each `ng-control` directive. Be sure to assign a unique name to each `ngControl` directive.
.l-sub-section .l-sub-section
:marked :marked
Angular registers controls under their `ng-control` names Angular registers controls under their `ngControl` names
with the `NgForm` directive. with the `NgForm` directive.
We didn't add the `NgForm` directive explicitly but it's here; We didn't add the `NgForm` directive explicitly but it's here;
we'll talk about it [later in this chapter](#ng-form). we'll talk about it [later in this chapter](#ng-form).
@ -429,7 +429,7 @@ figure.image-display
named **spy** named **spy**
to the Name `<input>` tag and use the spy to display those classes. to the Name `<input>` tag and use the spy to display those classes.
+makeExample('forms/dart/lib/hero_form_component_spy.html', 'ng-control-2')(format=".") +makeExample('forms/dart/lib/hero_form_component_spy.html', 'ngControl-2')(format=".")
:marked :marked
Now run the app, and look at the Name input box. Now run the app, and look at the Name input box.
@ -442,8 +442,8 @@ figure.image-display
The classes are displayed as follows: The classes are displayed as follows:
1. `form-control ng-untouched ng-pristine ng-valid` (initial state) 1. `form-control ng-untouched ng-valid ng-pristine` (initial state)
1. `form-control ng-pristine ng-valid ng-touched` (after clicking) 1. `form-control ng-valid ng-pristine ng-touched` (after clicking)
1. `form-control ng-valid ng-touched ng-dirty` (after changing) 1. `form-control ng-valid ng-touched ng-dirty` (after changing)
1. `form-control ng-touched ng-dirty ng-invalid` (after erasing) 1. `form-control ng-touched ng-dirty ng-invalid` (after erasing)
@ -498,11 +498,11 @@ figure.image-display
+makeExample('forms/dart/lib/hero_form_component.html', 'name-with-error-msg', 'lib/hero_form_component.html (excerpt)', stylePattern)(format=".") +makeExample('forms/dart/lib/hero_form_component.html', 'name-with-error-msg', 'lib/hero_form_component.html (excerpt)', stylePattern)(format=".")
:marked :marked
We initialized the template local variable with the string "form" We initialized the template local variable with the string "ngForm"
(`#name="form"`). (`#name="ngForm"`).
Angular recognizes that syntax and sets the `name` variable Angular recognizes that syntax and sets the `name` variable
to the `Control` object identified by the `ng-control` directive that, to the `Control` object identified by the `ngControl` directive that,
not coincidentally, we called "name". not coincidentally, we called "name".
We bind the `Control` object's `valid` property to the element's `hidden` property. We bind the `Control` object's `valid` property to the element's `hidden` property.
@ -513,14 +513,14 @@ figure.image-display
h3 The NgForm directive h3 The NgForm directive
:marked :marked
Recall from the previous section that `ng-control` registered this input box with the Recall from the previous section that `ngControl` registered this input box with the
`NgForm` directive as "name". `NgForm` directive as "name".
We didn't add the `NgForm`<!-- TODO: link to (../api/core/NgForm-class.html) --> directive explicitly. We didn't add the `NgForm`<!-- TODO: link to (../api/core/NgForm-class.html) --> directive explicitly.
Angular added it surreptitiously, wrapping it around the `<form>` element. Angular added it surreptitiously, wrapping it around the `<form>` element.
The `NgForm` directive supplements the `<form>` element with additional features. The `NgForm` directive supplements the `<form>` element with additional features.
It collects controls (elements identified by an `ng-control` directive) It collects controls (elements identified by an `ngControl` directive)
and monitors their properties including their validity. and monitors their properties including their validity.
It has its own `valid` property, which is true only if every contained It has its own `valid` property, which is true only if every contained
control is valid. control is valid.
@ -539,7 +539,7 @@ figure.image-display
.l-main-section .l-main-section
:marked :marked
## Submit the form with **ng-submit** ## Submit the form with **ngSubmit**
The user should be able to submit this form after filling it in. The user should be able to submit this form after filling it in.
The Submit button at the bottom of the form The Submit button at the bottom of the form
does nothing on its own, but it will does nothing on its own, but it will
@ -548,18 +548,18 @@ figure.image-display
A "form submit" is meaningless at the moment. To make it meaningful, A "form submit" is meaningless at the moment. To make it meaningful,
we'll update the `<form>` tag with another Angular directive, `NgSubmit`, we'll update the `<form>` tag with another Angular directive, `NgSubmit`,
and bind it to the `HeroFormComponent.onSubmit()` method: and bind it to the `HeroFormComponent.onSubmit()` method:
+makeExample('forms/dart/lib/hero_form_component.html', 'ng-submit')(format=".") +makeExample('forms/dart/lib/hero_form_component.html', 'ngSubmit')(format=".")
:marked :marked
We slipped in something extra there at the end! We defined a We slipped in something extra there at the end! We defined a
template local variable, **`#hf`**, and initialized it with the value "form". template local variable, **`#heroForm`**, and initialized it with the value "ngForm".
The variable `hf` is now a handle to the `NgForm` as we [discussed earlier](#ng-form) The variable `heroForm` is now a handle to the `NgForm` as we [discussed earlier](#ng-form)
with respect to `ng-control`, although this time we have a reference to the form with respect to `ngControl`, although this time we have a reference to the form
rather than a control. rather than a control.
We'll bind the form's overall validity via We'll bind the form's overall validity via
the `hf` variable to the button's `disabled` property the `heroForm` variable to the button's `disabled` property
using an event binding. Here's the code: using an event binding. Here's the code:
+makeExample('forms/dart/lib/hero_form_component.html', 'submit-button')(format=".") +makeExample('forms/dart/lib/hero_form_component.html', 'submit-button')(format=".")
:marked :marked
@ -631,10 +631,10 @@ figure.image-display
- An Angular HTML form template. - An Angular HTML form template.
- A form component class with a `Component` decorator. - A form component class with a `Component` decorator.
- The `ng-submit` directive for handling the form submission. - The `ngSubmit` directive for handling the form submission.
- Template local variables such as `#hf`, `#name`, `#alter-ego`, and `#power`. - Template local variables such as `#heroForm`, `#name`, `#p`, and `#spy`.
- The `ng-model` directive for two-way data binding. - The `ngModel` directive for two-way data binding.
- The `ng-control` directive for validation and form element change tracking. - The `ngControl` directive for validation and form element change tracking.
- The local variables `valid` property on input controls to check if a control is valid and show/hide error messages. - The local variables `valid` property on input controls to check if a control is valid and show/hide error messages.
- Property binding to disable the submit button when the form is invalid. - Property binding to disable the submit button when the form is invalid.
- Custom CSS classes that provide visual feedback to users about required invalid controls. - Custom CSS classes that provide visual feedback to users about required invalid controls.