Angular button is always enabled on form - javascript

I have this very basic form, that I am trying to enable/disable a button on if the form is/isn't filled out. When using this form, the button is always enabled even when I add/remove data in the input fields.
<form #activationForm="ngForm">
<input type="email" ([ngModel])="email" placeholder="{{'activation.email' | translate}}" required>
<input type="text" ([ngModel])="code" placeholder="{{'activation.code' | translate}}" required>
<button [disabled]="activationForm.form.invalid" class="button button--success" translate="activation.activate" (click)="activate()"></button>
</form>
#Component({
selector: 'app-activation',
templateUrl: './activation.component.html',
styleUrls: ['./activation.component.scss']
})
export class ActivationComponent {
public email: string = '';
public code: string = '';
}
Why is the button always enabled?

I would suggest using Reactive Forms. There's a cleaner way to do what you want.
Here's an example:
App Module:
import { ReactiveFormsModule } from '#angular/forms';
imports: [
...
ReactiveFormsModule
]
Component TypeScript:
this.activationForm = new FormGroup({
email: new FormControl('', [Validators.required, Validators.email]),
code: new FormControl('', [Validators.required])
});
Component Template:
<form [formGroup]="activationForm" (ngSubmit)="onSubmit($event)">
<input formControlName="email" />
<input formControlName="code" />
<button [disabled]="activationForm.invalid">Submit</button>
</form>
If you're using Angular Material, you can then leverage mat-hint or mat-error to assist with validation messages/hints. You can also make your own, of course.
The bonus with Reactive Forms is that everything is accessible. For example, you can do 'dynamic stuff' with your form by accessing their FormControl with this.activationForm.get('email'). The same goes for the form itself (ex: this.activationForm.setValue(...)). It's pretty powerful and in my opinion, much more preferable than NgForm.

to set two way data binding with ngModel it like this [(ngModel)]="email" also you have to provide name attribute
<form #activationForm="ngForm">
<input type="email" name="email" [(ngModel)]="email" placeholder="{{'activation.email' }}" required>
<input type="text" name="code" [(ngModel)]="code" placeholder="{{'activation.code' }}" required>
<button [disabled]="activationForm.form.invalid" class="button button--success" translate="activation.form.invalid" (click)="activate()">create</button>
</form>
demo 🚀

Related

Unable to get input using ngModel

I'm trying to get the input from some textboxes, but for some reason in my first textbox i'm getting the input without any problem. But on the second one i'm not getting any value from it even if i type something inside the textbox.
My html elements (the name input tag works):
<div class="form-group">
<label class="control-label">School name</label>
<input placeholder="Name" type="email" class="form-control" name="name" [(ngModel)]="name"
required>
</div>
<div class="form-group">
<label for="exampleFormControlInput1">School Address *</label>
<input placeholder="Address" type="email" class="form-control" name="sh_address" [(ngModel)]="sh_address"
required>
</div>
My code (this is called when the form is submitted):
public name: string = "";
public sh_address: string = "";
validateNewSchoolData() {
console.log(this.sh_address)
}
I tried your code in a new Angular Version 12 project. Your code works. Do you have imported the FormsModule?
imports: [
FormsModule,
...
],
Can you define the angular version you use or create a running code snippet to test?
I checked the two values by a button onClick event and console.log:
buttonClick() {
console.log(this.name, this.sh_address);
}
https://stackblitz.com/edit/angular-ivy-v51ahs
Greetings!

How can I associate a button with a form using Angular?

I'm working with Angular 6.x, and I'd like to associate a submit button located outside the form in the DOm with it. That is, I want to accomplish something structurally equivalent to this:
<button type='submit' form='myform'>
click me!
</button>
<form id='myform' action='#' onsubmit='console.log("wheee")'>
<input type='submit' value='me too'/>
</form>
I.e. I want to handle the submit event from the form element.
Is there a way to do this without going through nativeElement or without moving/duplicating the submit handler on the click event of the button?
You can achieve by reference #form of form and passing the same reference to button.
<button type='button' (click)="form.onsubmit()">
click me!
</button>
<form id='myform' #form action='#' onsubmit='console.log("wheee")'>
<input type='submit' value='me too'/>
</form>
Working copy is here - https://stackblitz.com/edit/angular-pnneks
Tyr it like this.
<button type='submit' (click)="myForm.submit()">
click me!
</button>
<form id='myform' action='#' #myForm onsubmit='console.log("wheee")'>
<input type='submit' value='me too'/>
</form>
Well, you could use a Reactive Form and then on the click of this button. call a function that would extract the value of the form and do something on it.
This way, you can also do things like disabling the button if the form is invalid.
So here's what your form will look like:
import { Component } from '#angular/core';
import { FormGroup, FormBuilder, FormControl, Validators } from '#angular/forms';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
userForm: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.userForm = this.fb.group({
firstName: [, Validators.required],
lastName: [, Validators.required]
});
}
submitForm() {
console.log('Form Submitted with value: ', this.userForm.value);
}
}
And here's the template:
<h3>User Form</h3>
<hr>
<form [formGroup]="userForm">
<div>
<label for="firstName">First Name: </label>
<input type="text" id="firstName" formControlName="firstName">
</div>
<br>
<div>
<label for="lastName">Last Name: </label>
<input type="text" id="lastName" formControlName="lastName">
</div>
</form>
<br>
<button (click)="submitForm()" [disabled]="userForm.invalid">Submit</button>
Here's a Sample StackBlitz for your ref.

Angular 2 form validation touched and dirty gives back error, can't find out why

So I have implemented a custom form validation for changing password. It works good, but I'm getting an error message, when the new password is typed, but the confirmPassword is not touched yet, because the two differ. I want to use validation.dirty and touched, to prevent this behaviour, but implementing it as it follows, Im getting the following error:
There is no directive with "exportAs" set to "ngModel" ("mGroup]="password" type="password" class="form-control" id="confirmPassword" name="confirmPassword" [ERROR ->]#confirmPassword="ngModel">
<form (ngSubmit)="changePassword()" [formGroup]="password">
<div class="form-group">
<label for="oldPassword">Old Password</label>
<input type="password" class="form-control" id="oldPassword" name="oldPassword">
</div>
<div class="form-group">
<label for="password">New Password</label>
<input formControlName="password" [formGroup]="password" type="password" class="form-control" id="password" name="password">
</div>
<div class="form-group">
<label for="confirmPassword">Confirm Password</label>
<input formControlName="confirmPassword" [formGroup]="password" type="password" class="form-control" id="confirmPassword" name="confirmPassword" #confirmPassword="ngModel">
<div class="alert alert-danger" *ngIf="password.controls.confirmPassword.errors?.MatchPassword && (confirmPassword.touched || confirmPassword.dirty)">Password not match</div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
I tought that #confirmPassword="ngModel" will solve this, on the confirmPassword input, but that's what causing the error.
What am I doing wrong here?
Here is a working StackBlitz using "ReactiveForms" approach.
The first thing you are not doing right is mixing different form approaches.
If you want to use model driven forms (using FormsModule), then you need to get rid of the ReactiveFormsModule directives: formControlName, formGroup, etc.
Also, you missed the [(ngModel)]="myValue" in your code, which is required for #ref="ngModel" to work.
if you want to stick to ReactiveFormsModule, you need to get rid of ngModel code. Also, since you are using formGroup on the parent form, you DON'T need to specify it on the children:
<form [formGroup]="parent>
<input formControlName="child">
</form>
would bind this form to:
parent = new FormGroup({
child: new FormControl(null),
});
I also took the liberty to suggest a validation approach, where the "future" password field has MinLength and other validators, while the whole password FormGroup is responsible for making sure that your passwords match.
Use a Custom Validator instead
example:
ngOnInit() {
this.myForm = new FormGroup({
password1: new FormControl(),
password2: new FormControl('',this.comparePassword.bind(this)),
})
comparePassword(control: FormControl): { [key: string]: boolean } {
if (control.parent){//
const password1 = control.parent.value['password1'];
const password2 = control.value;
if(password1 === password2){
return {passwordMismatch:true}
}
}
return null;
}
HTML
<form [formGroup]="myForm">
<div class="form-group">
<label for="password1">password1</label>
<input type="password"
class="form-control" formControlName="password1">
</div>
<div class="form-group">
<label for="password2">password2</label>
<input type="password" class="form-control"
formControlName="password2">
</div>
<div *ngIf="myForm.get('password2').errors && myForm?.get('password2')?.errors?.passwordMismatch">
Password Mismatch
</div>
</form>
Live Demo

How to normally bind forms in angular

I have a template form, which I wrote from guides and they don't really work though.
I have several of models:
export class User {
constructor(public userId?: UserId,
public firstName?: String,
public lastName?: String,
public address?: Address) {
}
}
export class Address {
constructor(
public street?: String,
public city?: String,
public zipCode?: String) {
}
}
I have component:
Component({
templateUrl: 'user.html'
})
export class MyComponent implements OnInit, OnDestroy{
user: User;
userForm: NgForm;
ngOnInit(): void {
}
And page itself:
<form novalidate #userForm="ngForm">
<div class="form-group">
<input required minlength="4" type="text"
id="firstName"
[(ngModel)]="user.firstName" name="firstName">
<small *ngIf="!firstName.valid">Not valid!</small>
</div>
<div class="form-group">
<input required ng-minlength="4" type="text"
id="lastName"
[(ngModel)]="user.lastName" name="lastName">
</div>
<div ngModelGroup="user.address">
<div class="form-group">
<input required ng-minlength="4"
type="text"
id="address-house"
[(ngModel)]="user.address.address1" name="address.address1">
</div>
<div class="form-group">
<div class="form-group">
<input required ng-minlength="4"
type="text"
id="zipCode"
[(ngModel)]="user.address.zipCode" name="address.zipCode">
</div>
<div>
<input required ng-minlength="4"
type="text"
lass="form-control input-lg"
id="city"
[(ngModel)]="user.address.city" name="address.city">
</div>
</div>
</div>
<button type="button" (click)="checkAndProceed()">Continue</button>
</div>
</form>
The only thing I want to do is to add validation - that's all. None of the guides helped. Can we do in-html validation or ts validation? It would be nice to call validation when clicking 'Continue' button and making it valid if it is so.
In this case of validation I additionally get console error:
Cannot read property 'valid' of undefined
There are lots of attributes on input elements. We have name, Id, and template reference variable. Your code is missing the template reference variable. It is the template reference variable that holds onto the reference to the element and has the valid, dirty, and other flags associated with it.
For example, change your code to include a template reference variable like this:
<div class="form-group">
<input required minlength="4" type="text"
id="firstName"
[(ngModel)]="user.firstName" name="firstName"
#firstNameVar="ngModel">
<small *ngIf="!firstNameVar.valid">Not valid!</small>
</div>
Notice the #firstNameVar. That is the template reference variable. It can be named the same thing as your Id and name attributes. I just named it something different so it could be readily distinguished between the other two attributes.
Notice also that the *ngIf is then changed to use firstNameVar.valid
For more information on template reference variables, see this: https://angular.io/guide/template-syntax#ref-vars

Angular 2 Form Validation For Numbers minimum and maximum

I have a form with input boxes. The input boxes are in both type text and numbers. And I have to validate them and I followed this tutorial and tried to validate them.
According to that if I have to validate a string then I can use the control group as follows.
constructor(fb: FormBuilder){
this.complexForm = fb.group({
'firstName' : [null, Validators.required],
'lastName': [null, Validators.compose([Validators.required, Validators.minLength(5), Validators.maxLength(10)])]
})
And the HTML code for this as follows...
<form [formGroup]="complexForm" (ngSubmit)="submitForm(complexForm.value)">
<div class="form-group">
<label>First Name:</label>
<input class="form-control" type="text" placeholder="John" [formControl]="complexForm.controls['firstName']">
</div>
<div class="form-group">
<label>Last Name</label>
<input class="form-control" type="text" placeholder="Doe" [formControl]="complexForm.controls['lastName']">
</div>
<div class="form-group">
<button type="submit" class="btn btn-default">Submit</button>
</div>
</form>
But I have to validate a number type input box also as the following example.
<form [formGroup]="complexForm" (ngSubmit)="submitForm(complexForm.value)">
<div class="form-group">
<label>Age:</label>
<input class="form-control" type="number" [formControl]="complexForm.controls['age']">
</div>
<div class="form-group">
<button type="submit" class="btn btn-default">Submit</button>
</div>
</form>
But Problem is there is no option for Validators to select what is minimum value and maximum value for the input.
are there someone has a solution for this issue?
Thanks.
The current version of angular 4 now has min and max validators
So it is as simple as writing
this.complexForm = fb.group({
age:[null,[Validators.required, Validators.min(5),Validators.max(90)]],
})
Remember it is on the #angular/forms repo
$: npm uninstall #angular/forms --save
$: npm install #angular/forms --save
There's no built-in Validator for min/max currently you can checkout the source for Validator to see whats available.
What you can do is create a custom validator function following the tutorials in the official docs
Example:
export function maxValue(max: Number): ValidatorFn {
return (control: AbstractControl): {[key: string]: any} => {
const input = control.value,
isValid = input > max;
if(isValid)
return { 'maxValue': {max} }
else
return null;
};
}
With this you can update your code to
constructor(fb: FormBuilder){
this.complexForm = fb.group({
'firstName' : [null, Validators.required],
'lastName': [null, Validators.compose([Validators.required, Validators.minLength(5), Validators.maxLength(10)])],
'age': [null, maxValue(60)]
})
If I get your issue right, try to include your validators' requirements also in HTML so your code will look as follows:
<form [formGroup]="complexForm" (ngSubmit)="submitForm(complexForm.value)">
<div class="form-group">
<label>Age:</label>
<input class="form-control" type="number" required minlength="5" maxlength="10" [formControl]="complexForm.controls['age']">
</div>
<div class="form-group">
<button type="submit" class="btn btn-default">Submit</button>
</div>
</form>
not sure if this is what you are looking for but HTML validators are always an option
<input type="number">
This won't allow users to put in anything but numbers and you don't have to parse it as it will be set as a number not a string.

Categories