I am trying to create a form with inputs that only update onBlur ([ngFormOptions]="{updateOn: 'blur'}"
). But a side effect of this is that the form is no longer submitted once the user hits 'enter', since the model is only updated onBlur. (same as this question, but it has no answer)
As a result of this the form is marked invalid, since there is a value in the input field, but the model is not yet updated with the value.
Versions:
- Angular 6.0.7
- @angular/forms 6.0.7
HTML example
<form (ngSubmit)="login()" #loginForm="ngForm" [ngFormOptions]="{updateOn: 'blur'}" [validate-on-submit]="loginForm">
<input type="text" ([NgModel])="user.username"/>
<input type="password" ([NgModel])="user.password"/>
</form>
When entered a valid text in both fields and hitting 'enter', the form validates and marks password (the input that had the focus) as invalid, since the NgModel has not yet been updated, and thus the value is invalid.
What do I want
So what I am looking for is a way to validate on both onBlur
aswell as onSubmit
. I know that since Angular 5 we have the option [ngFormOptions]="{updateOn: 'blur'}"
, but is there a way to give 2 parameters to this object?
([ngFormOptions]="{updateOn: ['blur', 'submit']}"
doesn't seem to work)
Almost forgot, I do not want the model updated onChange
(creates annoying messages that are not very helpfull, since the user is still typing..)
Create a third, invisible input in your form, and give it a template variable. Before submitting, simply focus this input, which will trigger the
onBlur
update :Note that this is one of the many workarounds you can use, not an actual solution
The solution - A form component
After following the advice of @trichetriche (creating a separate component that holds the logic of the form) and letting template driven forms go in favor of reactive forms I found the following solution:
Do note the formgroup that is injected, we can set it on the new form in this component so that the form controls are correctly transcluded (otherwise Angular does not recognize the form controls). This is the problem that stopped me from using template driven forms
When the form is submitted we now check if it is valid. If it isn't then we recursively loop trough all the FormControls (
updateControls()
) and update the status to 'touched' (since we are going to set errors on them we can also mark them 'touched'). In order to make Angular send out anstateChange
event to it's listeners we need to useupdateValueAndValidity()
. And since we are already looping trough every formControl we can set theonlySelf
flag totrue
.The template that belongs to the form:
By making sure Angular emits and event when the form is invalid we've created a hook that can be used for custom messages (remember
ng-messages
from Angular 1.x?). More information on that can be found [here][2].If you want to use this in a template this would look as follows:
Note the
[formGroup]
tag, Angular uses the same tag to define a reactive form on a form. Removing the tag from the input results in an angular error telling you that it needs the tag in order to assign theformControlName
. We can make sure a programmer doesn't run into this problem by using the same tag for injecting the form into the directive. Angular will be happy and you can get the form inside the directive, win-win situation!