Why does the Angular data binding "late"?

I have some issues with data and class-binding in Angular, and neither disabling the submit button works.

checkNewChampName(value) {
    if (this.actualChampionships.indexOf(value) !== -1) {
      this.validNewChampName = false;
    } else {
      this.validNewChampName = true;
    }
  }
<form #userForm="ngForm">
  <div class="form-group">
    <label>Name</label>
    <input #name="ngModel" type="text" [class.is-invalid]="!validNewChampName" class="form-control" name="name" [(ngModel)]="newChampName" (keydown)="checkNewChampName(name.value)">
  </div>
<button [disabled]="userForm.form.invalid" class="btn btn-primary">Submit</button>
</form>

So I would like an input, which calls a validation method for every keydown. This validation should set a class variable to true or false, depending on is the value element of an array or not. And I want to bind this boolean variable to the 'is-invalid' class of the input. I have 3 problems:

  1. At the beginning my input is red, and if I write something and delete to blank again, it won't be red again (it shouldn't be at the start neither), so why is it red at start?
  2. The validation method almost works, but it lates one character. So if 'asdf' is in the array, my input remains blue, but it changes to red to the next keydown.
  3. Why the button is not disabled when my input is red?

Upvotes: 1

Views: 979

Answers (2)

Martin Parenteau
Martin Parenteau

Reputation: 73731

The problem is that the keydown event is called before the ngModel has been updated. In order to respond to an input change, handle ngModelChange:

<input ... [(ngModel)]="newChampName" (ngModelChange)="checkNewChampName($event)">

See this stackblitz for a demo.


The button is not disabled because there is no actual form validation on the input element (e.g. with a required or pattern attribute). The validation that you are performing is just in your component logic but it does not actually invalidate the form. You could define a custom validator for that field, as explained in this article. An alternative is to switch to the Reactive forms model, in which custom validation is easier to implement. In your case, the simple solution is to disable the button when your current validation fails:

<button [disabled]="!validNewChampName" class="btn btn-primary">

Upvotes: 2

David Anthony Acosta
David Anthony Acosta

Reputation: 4908

  1. It's red because the variable is set to false to start off with and you've told it to display an error whenever it is false. And your logic to set it to true won't kick in until you've typed something. If you want to avoid that, a few options you have:

**Either alter your logic so that it only displays an error if the input has been touched (for example,

[class.is-invalid]="name.dirty && !validNewChampName"

**or you can just set the variable to true by default:

export class Whatever {
validChampName = true;
}

for #2, you need to grab the model's value so try:

try (keydown)="checkNewChampName(newChampName)"

Also, consider binding to (ngModelChange)="checkNewChampName($event)" instead. That way, your logic will not just run on keydown, but rather if the value is ever changed.

Lastly, it is not disabled because it should be

userForm.invalid 

(without the form after it)

Upvotes: 3

Related Questions