In an Angular template-driven form, how do attributes like required, minlength, pattern and email turn into validation, and why don't native browser bubbles appear?
answer
- attribute selectors on validator directives
- they need ngModel on the same element
- min and max only on number inputs
- empty values pass pattern and minlength
- novalidate added to every form
basics
~20 sFormsModule ships validator directives whose selectors match attributes such as required, minlength, pattern and email on an element that also has ngModel; each adds a validator to that control. FormsModule also adds novalidate to forms, so Angular, not the browser, reports errors.
solid answer
~40 sDirectives like `RequiredValidator`, `MinLengthValidator`, `PatternValidator` and `EmailValidator` have selectors such as `[required][ngModel]`, so the attribute alone does nothing; paired with `ngModel` it attaches the matching validator to that field's `FormControl`, and errors appear in `field.errors`. Bound forms such as `[required]="needsPhone"` switch the validator on and off. A few edge rules matter: `min` and `max` only match `input[type=number]`, a checkbox with `required` must be checked, and `pattern`, `minlength` and `email` skip empty values, so pair them with `required`. FormsModule's `NgNoValidate` directive adds `novalidate` to every `<form>`, so the browser never blocks submission or shows bubbles; add `ngNativeValidate` to opt back in. Status classes like `ng-invalid` and `ng-touched` drive styling.
go deeper
Know the common attributes (required, minlength, pattern, email) and how to show a message from the field's errors.
Explain that the attributes are directive selectors paired with ngModel, and why novalidate means Angular, not the browser, reports errors.
Catch the edge rules in review: min and max only on number inputs, empty values passing optional validators, and anchored pattern strings.
Decide once whether native bubbles or Angular messages own the error UI, and make the form components enforce that choice.
## Attributes are directive selectors In a template-driven form you do not pass validator functions to controls; you write **attributes**, and `FormsModule` supplies **validator directives** whose selectors match them. Each directive provides itself as a validator for the control on the same element, so `NgModel` picks it up when it builds its `FormControl`. | Attribute | Directive | Selector condition | Error key | |---|---|---|---| | `required` | `RequiredValidator` | not a checkbox, with `ngModel` | `required` | | `required` on a checkbox | `CheckboxRequiredValidator` | `input[type=checkbox]` with `ngModel` | `required` (must be checked) | | `minlength` / `maxlength` | `MinLengthValidator` / `MaxLengthValidator` | with `ngModel` | `minlength` / `maxlength` | | `pattern` | `PatternValidator` | with `ngModel` | `pattern` | | `email` | `EmailValidator` | with `ngModel` | `email` | | `min` / `max` | `MinValidator` / `MaxValidator` | only `input[type=number]` with `ngModel` | `min` / `max` | The same directives also match `formControl` and `formControlName`, which is why these attributes work in reactive templates too. ## Rules that surprise people - **The attribute alone does nothing.** Without `ngModel` (or a reactive control directive) on the element, no validator directive matches. - **`min` and `max` are number-only.** On `type="date"` or `type="text"` the attribute is ignored by Angular's validation. - **Empty values pass** `pattern`, `minlength` and `email`: an optional field that is empty is valid. Add `required` when empty should fail. - **Pattern strings are anchored.** A string pattern gets `^` and `$` added if missing, so `pattern="[0-9]{4}"` means exactly four digits. - **Binding toggles the rule.** `[required]="needsPhone"` adds or removes the validator and the rendered attribute as the expression changes; `[required]="false"` disables it. ## Why no native bubbles appear `FormsModule` includes **`NgNoValidate`**, whose selector matches every `<form>` without `ngNoForm` or `ngNativeValidate`, and it simply adds the **`novalidate`** attribute. So: 1. The browser does not block submission for failed constraints, and `ngSubmit` still fires. 2. No native error bubbles are shown; your template renders messages from `field.errors`. 3. Adding **`ngNativeValidate`** to a form removes `novalidate` and lets the browser validate again, on top of Angular's own checks. The validator directives still render attributes such as `required` onto the element, so assistive technology and CSS pseudo-classes still see the constraint. ## Showing the result Each field exported as `#email="ngModel"` exposes `errors`, `invalid`, `touched` and `dirty`, and `NgControlStatus` adds classes for styling: - `ng-valid` / `ng-invalid` and `ng-pending` for validity, - `ng-touched` / `ng-untouched` and `ng-dirty` / `ng-pristine` for interaction, - `ng-submitted` on the form after a submit. ```html <input name="email" type="email" ngModel required email #email="ngModel" /> @if (email.invalid && email.touched) { @if (email.errors?.['required']) { <small>Email is required.</small> } @if (email.errors?.['email']) { <small>Enter a valid email address.</small> } } ``` ## Common review findings - A `min="1"` on a quantity input of `type="text"`: no validation happens. - A `pattern` without `required` on a mandatory field: an empty submit passes. - Messages shown on `invalid` alone, so a fresh form opens covered in errors; gate them on `touched` or on `f.submitted`. - A form with `ngNativeValidate` **and** custom messages, so users see two different error styles for one problem. ## Beyond the built-in attributes A rule the attributes cannot express, such as a blocked email domain, is written as a **custom validator directive**: a directive with an attribute selector that provides itself for `NG_VALIDATORS` and implements `validate()`. Writing the validator function itself, and async or cross-field validators, is the subject of the validator topic; the template-driven part is only that the rule arrives as an attribute on an `ngModel` element.
- Why does min="18" on an <input type="text" ngModel> never produce a min error?Angular's `MinValidator` selector only matches `input[type=number]` elements with a form control directive, so on a text input no validator is attached. Use `type="number"`, or write a custom validator directive for text input that should be parsed as a number.
- How do you let the browser show its own validation bubbles in an Angular template-driven form?Add `ngNativeValidate` to the `<form>`. `NgNoValidate` then does not match, so no `novalidate` attribute is added and the browser validates constraints on submit as usual, in addition to Angular's validators.
saying these in an interview costs you the question
- Angular reads the native validity state for template-driven errors
- A required attribute validates even without ngModel on the element
- min and max work on any input type
- pattern and email fail on an empty optional field
- Template-driven forms show browser bubbles by default