How can I make submit button disabled, when my form is invalid in Angular2?
Now I use code:
<input type="submit" [disabled]="!registrationForm.valid" value="Zarejestruj">
and this is not working and make form always disabled, also when this is valid.
Any suggestion?
UPDATE:
I found error, in form I have file input and this causes form button failed. Here is my whole code (without validation tips)
<div class="form">
<form [formGroup]="registrationForm" (ngSubmit)="RegisterUser(registrationForm.value)" novalidate>
<div class="form-row">
<input type="text" name="login" placeholder="Nazwa użytkownika" [formControl]="registrationForm.controls['login']">
</div>
<div class="form-row">
<input type="password" name="password" placeholder="Hasło" [formControl]="registrationForm.controls['password']" validateEqual="repeatPassword" reverse="true">
</div>
<div class="form-row">
<input type="password" name="repeatPassword" placeholder="Powtórz hasło" [formControl]="registrationForm.controls['repeatPassword']" validateEqual="password">
</div>
<div class="form-row">
<input type="file" name="avatar" file-model="avatar" [formControl]="registrationForm.controls['avatar']" >
</div>
<div class="form-row submit">
<input type="submit" [disabled]="!registrationForm.valid" value="Zarejestruj">
</div>
</form>
</div>
And registration.component.ts:
/ Imports
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormBuilder, Validators } from '#angular/forms';
#Component({
templateUrl: './app/registration/registration.component.html'
})
// Component class implementing OnInit
export class RegistrationComponent {
registrationForm : FormGroup;
constructor(fb: FormBuilder){
this.registrationForm = fb.group({
'login' : [null, Validators.compose([Validators.required, Validators.minLength(4), Validators.maxLength(12)])],
'password' : [null, Validators.compose([Validators.required, Validators.minLength(6), Validators.maxLength(20)])],
'repeatPassword': [null, Validators.required],
'avatar': [null, Validators.required],
})
}
RegisterUser(value: any){
console.log(value);
}
}
Now the question is: why this does not working?
Related
As Angular documentation says we can use formControlName in our forms:
<h2>Hero Detail</h2>
<h3><i>FormControl in a FormGroup</i></h3>
<form [formGroup]="heroForm" novalidate>
<div class="form-group">
<label class="center-block">Name:
<input class="form-control" formControlName="name">
</label>
</div>
</form>
As they say...
Without a parent FormGroup, [formControl]="name" worked earlier because that directive can stand alone, that is, it works without being in a FormGroup. With a parent FormGroup, the name input needs the syntax formControlName=name in order to be associated with the correct FormControl in the class. This syntax tells Angular to look for the parent FormGroup, in this case heroForm, and then inside that group to look for a FormControl called name.
Anyway some months ago I asked this to figure out what is the difference between formControlName and [formControl].
Now my question is: what about use formControlName with nested FormGroups?
For example, if I have the following form structure:
this.myForm = fb.group({
'fullname': ['', Validators.required],
'gender': [],
'address': fb.group({
'street': [''],
'houseNumber': [''],
'postalCode': ['']
})
});
What is the right way to bind "street" (or "houseNumber" or "postalCode") to related HTML elements using formControlName?
you can use Form group which is basically a collection of controls ( controls mean the fields given in your html form) define in your typescript syntax and binded to your HTML elements using the formControlName directive ,for example
this.myForm = fb.group({
'fullname': ['', Validators.required],
'gender': [],
'address': fb.group({
'street': [''],
'houseNumber': [''],
'postalCode': ['']
})
});
Template:
<form [formGroup]="myForm" >
<div class="form-group">
<label for="fullname">Username</label>
<input type="text" id="username" formControlName="fullname" class="form-control">
</div>
<div class="radio" *ngFor="let gender of genders">
<label>
<input type="radio" formControlName="gender" [value]="gender">{{ gender }} </label>
</div>
<div formGroupName="address">
<div class="form-group">
<label for="street">Username</label>
<input type="text" id="username" value="street" formControlName="street" class="form-control">
</div>
<div class="form-group">
<label for="houseNumber">Username</label>
<input type="text" id="username" value="street" formControlName="houseNumber" class="form-control">
</div>
<div class="form-group">
<label for="postalCode">Username</label>
<input type="text" id="username" value="street" formControlName="postalCode" class="form-control">
</div>
</div>
</form>
A formGroup can consist of a nested formGroup and hierarchy can continue on ,but in accessing the value the its fairly simple .
It is true. Look at formGroupName
this.myForm = fb.group({
'fullname': ['', Validators.required],
'gender': [],
'address': fb.group({
'street': [''],
'houseNumber': [''],
'postalCode': ['']
})
});
<form [formGroup]="myForm" >
<div class="form-group">
<label for="fullname">Username</label>
<input type="text" id="username" formControlName="fullname" class="form-control">
</div>
<div class="radio" *ngFor="let gender of genders">
<label>
<input type="radio" formControlName="gender" [value]="gender">{{ gender }} </label>
</div>
<div formGroupName="address">
<div class="form-group">
<label for="street">Username</label>
<input type="text" id="username" value="street" formControlName="street" class="form-control">
</div>
<div class="form-group">
<label for="houseNumber">Username</label>
<input type="text" id="username" value="street" formControlName="houseNumber" class="form-control">
</div>
<div class="form-group">
<label for="postalCode">Username</label>
<input type="text" id="username" value="street" formControlName="postalCode" class="form-control">
</div>
</div>
</form>
tl;dr:
You can break your form up into components that use your nested formgroups, and formcontrolname can be used as normal.
The approach I tend to use, since usually nested Formgroups are used to designate separate parts of a form, is to break it up into components and pass those components the nested formgroup as an input parameter.
So in your case, I would have an address component that takes a formgroup as a parameter:
<app-address [formGroup]="myForm.get('address')"></app-address
And inside of that component, I would just have an #Input() formGroup that would be used in the html:
<div [formGroup]="formGroup">
....
That way you can reference the control name explicitly as you normally would, since it would be part of this formgroup.
Also, keep in mind the form is passed as reference. your changes would be accounted for in the parent component's myForm element, and if you needed access to parts of the form not in you formgroup (validation, change detection, ect ect) you could always pass down the whole form and just define the formgroup to reference the inner group explicitly:
<div [formGroup]="formGroup.get('adress')">
(assuming you pass down the entire form object that is
Happy coding!
I am struggling with a problem in Angular 10.
I have a "parent" form group "forma" who has a few dependent groups: "company", and at the same time, "company" has two
"children" with another groups, msgAccounts and socialMedia.
When I fill the form and I submit it, in the backend everything is correct, I can see how these data is stored in the db
correctly, but when I receive this json I cannot display the data
inside "company.msgAccounts" and "company.socialMedia" in the controls
(inputs). This is what I get from the server:
{
name:'',
active: '',
........
company:{
msgAccounts:{line: "#linedemo", whatsapp: "0325554244"},
socialMedia: {fb: '', tw: '', is: ''}
}
..........
}
this.forma = this.fb.group( {
siteName : [ '', [Validators.required, Validators.minLength(5)]],
siteEmail : [ '', [Validators.required, Validators.minLength(5)]],
// defaultLocation: [ '', [Validators.required, Validators.minLength(10)]],
active : [ true, [Validators.required, Validators.minLength(5)]],
mainLanguage: ['' , [Validators.required, Validators.minLength(2)]],
company: this.fb.group({
name: [''],
address: [''],
phone: [''],
msgAccounts: this.fb.group({
line: [],
whatsapp: []
}),
socialMedia: this.fb.group({
fb: [],
is: [],
tw: []
})
})
});
And the html: (Sorry about the indentation, when it was not easy using this editor, and I just pasted a part of the code in order to do it as shorter as possible).
<mat-tab-group>
<mat-tab label="settings">
<form autocomplete="off" >
<ng-template ng-template matTabContent>
<mat-tab-group [formGroup]="forma">
<mat-tab label="global">
// All this fields have are fine
</mat-tab>
<mat-tab formGroupName="company" label="company">
<div class="card">
<div class="card-header" id="headingTwo">
<h5 class="mb-0">Details</h5>
</div>
<div id="company-details">
<div class="form-group row">
<div class="col-sm-3">
<input
type="text"
class="form-control"
id="name"
name="name"
placeholder="Company name"
formControlName=name>
</div>
</div>
<div class="form-group row" formGroupName="msgAccounts">
<div class="col-sm-6">
<input
type="text"
class="form-control"
id="line"
name="line"
placeholder="line"
formControlName=line>
</div>
<div class="col-sm-6">
<input
type="text"
class="form-control"
id="whatsapp"
name="whatsapp"
placeholder="whatsapp"
formControlName=whatsapp>
</div>
</div>
<div class="form-group row" formGroupName="socialMedia" >
<div class="col-sm-6">
<input
type="text"
class="form-control"
id="is"
name="is"
placeholder="Is"
formControlName=is>
</div>
</div>
</div>
</div>
</mat-tab>
</mat-tab-group>
<div class="form-group row">
<div class="col-sm-10">
<button type="submit"
(click)="saveConfig()">Save</button>
</div>
</div>
</ng-template>
</form>
</mat-tab>
</mat-tab-group>
I am building an app using Angular 4.0.2. How can I add a button to a form to add a new row of input and a delete button for a particular row to delete? I mean that I want a form something like this. I want my form to look something like this:
.
Here is my code:
add-invoice.component.html
<h3 class="page-header">Add Invoice</h3>
<form [formGroup]="invoiceForm">
<div formArrayName="itemRows">
<div *ngFor="let itemrow of itemRows.controls; let i=index" [formGroupName]="i">
<h4>Invoice Row #{{ i + 1 }}</h4>
<div class="form-group">
<label>Item Name</label>
<input formControlName="itemname" class="form-control">
</div>
<div class="form-group">
<label>Quantity</label>
<input formControlName="itemqty" class="form-control">
</div>
<div class="form-group">
<label>Unit Cost</label>
<input formControlName="itemrate" class="form-control">
</div>
<div class="form-group">
<label>Tax</label>
<input formControlName="itemtax" class="form-control">
</div>
<div class="form-group">
<label>Amount</label>
<input formControlName="amount" class="form-control">
</div>
<button *ngIf="i > 1" (click)="deleteRow(i)" class="btn btn-danger">Delete Button</button>
</div>
</div>
<button type="button" (click)="addNewRow()" class="btn btn-primary">Add new Row</button>
</form>
<p>{{invoiceForm.value | json}}</p>
Here is my code for add-invoice.component.ts
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormControl, FormArray, FormGroup } from '#angular/forms';
#Component({
selector: 'app-add-invoice',
templateUrl: './add-invoice.component.html',
styleUrls: ['./add-invoice.component.css']
})
export class AddInvoiceComponent implements OnInit {
invoiceForm: FormGroup;
constructor(
private _fb: FormBuilder
) {
this.createForm();
}
createForm(){
this.invoiceForm = this._fb.group({
itemRows: this._fb.array([])
});
this.invoiceForm.setControl('itemRows', this._fb.array([]));
}
get itemRows(): FormArray {
return this.invoiceForm.get('itemRows') as FormArray;
}
addNewRow(){
this.itemRows.push(this._fb.group(itemrow));
}
ngOnInit(){
}
}
Here's a shortened version of your code:
When you init your form, you can add one empty formgroup inside your formArray:
ngOnInit() {
this.invoiceForm = this._fb.group({
itemRows: this._fb.array([this.initItemRows()])
});
}
get formArr() {
return this.invoiceForm.get('itemRows') as FormArray;
}
Then the function:
initItemRows() {
return this._fb.group({
// list all your form controls here, which belongs to your form array
itemname: ['']
});
}
Here is the addNewRow and deleteRow functions:
addNewRow() {
this.formArr.push(this.initItemRows());
}
deleteRow(index: number) {
this.formArr.removeAt(index);
}
Your form should look like this:
<form [formGroup]="invoiceForm">
<div formArrayName="itemRows">
<div *ngFor="let itemrow of formArr.controls; let i=index" [formGroupName]="i">
<h4>Invoice Row #{{ i + 1 }}</h4>
<div>
<label>Item Name</label>
<input formControlName="itemname">
</div>
<button type="button" (click)="deleteRow(i)" *ngIf="formArr.controls.length > 1" >
Delete
</button>
</div>
</div>
<button type="button" (click)="addNewRow()">Add new Row</button>
</form>
Here's a
DEMO
Not sure why the setState function in the addUser() not doing it's job. The POST request works fine, but the inputs don't clear and that is an issue for the users. Perhaps a second pair of eyes, can spot the flaw.
import React, { Component, PropTypes } from 'react';
import { injectIntl, intlShape, FormattedMessage } from 'react-intl';
import { Link } from 'react-router';
// Import Style
import styles from './UserRegistrationForm.css';
export class UserRegistrationForm extends Component {
constructor(props) {
super(props);
this.state = { nickname: '', studentId: '', email: '', password: '' };
this.handleInputChange = this.handleInputChange.bind(this);
this.addUser = this.addUser.bind(this);
}
handleInputChange(event) {
this.setState({
[event.target.name]: event.target.value,
});
}
addUser = () => {
if (this.state.nickname && this.state.studentId && this.state.email && this.state.password) {
this.props.addUser(this.state.nickname, this.state.studentId, this.state.email, this.state.password);
this.setState({ nickname: '', studentId: '', email: '', password: '' });
}
};
render() {
return (
<div className={`${styles.formContainer} ${styles.center}`}>
<i className={`${styles.cap} fa fa-graduation-cap`} />
<h1 className={styles.title}><FormattedMessage id="siteTitle" /></h1>
<div className="row">
<form method="POST" className="col-lg-4 push-lg-4 col-md-6 push-md-3 col-xs-8 push-xs-2">
<div className="form-group row">
<label className="input-labels">Full Name</label>
<input type="text" className="form-control" name="nickname" placeholder="Full Name" onChange={this.handleInputChange} />
</div>
<div className="form-group row">
<label className="input-labels">Student ID</label>
<input type="text" className="form-control" name="studentId" placeholder="Student ID" onChange={this.handleInputChange} />
</div>
<div className="form-group row">
<label className="input-labels">Email</label>
<input type="email" className="form-control" name="email" placeholder="Email" onChange={this.handleInputChange} />
</div>
<div className="form-group row">
<label className="input-labels">Password</label>
<input type="password" className="form-control" name="password" placeholder="Password" onChange={this.handleInputChange} />
</div>
<div className={styles.center}>
<button
className={`${styles.btnOutlineSecondary} btn btn-outline-secondary ${styles.signInButton}`}
type="button" onClick={this.addUser}
>
Register and Start Studying!
</button><br /><br />
<Link to="/profile"><button className="btn btn-info" type="button">Temp Button to Profile Page</button></Link><br /><br />
<Link to="/">Already have an account? Sign in Here</Link>
</div>
</form>
</div>
</div>
);
}
}
UserRegistrationForm.propTypes = {
addUser: PropTypes.func.isRequired,
intl: intlShape.isRequired,
};
export default injectIntl(UserRegistrationForm);
There is no two-way data-binding in react, so you have to be careful when implementing controlled components https://facebook.github.io/react/docs/forms.html#controlled-components
for every one of your inputs, to make them controlled inputs, that will clear on submit, you must set their values to the corresponding state value, e.g.
<input value={this.state.nickname} type="text" className="form-control" name="nickname" placeholder="Full Name" onChange={this.handleInputChange} />
and
<input value={this.state.nickname} type="text" className="form-control" name="studentId" placeholder="Student ID" onChange={this.handleInputChange} />
... for every one of your inputs, adding the value attribute will make sure they stay in sync with, this.setState calls
I am trying to do a login form component but I cannot read the form data.
When I try to write username on the console, 'undefined' writes.
Everything seems usual but form data does not come to component.
Below is the html code:
<form (ngSubmit)="onSubmit(myForm)"
#myForm="ngForm"
class="form-signin">
<div class="form-group">
<h2 class="form-signin-heading">Please sign in</h2>
<input type="text"
id="inputUsername"
name="inputUsername"
class="form-control"
placeholder="User Name"
required>
<input type="password"
id="inputPassword"
name="inputPassword"
class="form-control"
placeholder="Password" >
</div>
<button class="btn btn-lg btn-primary btn-block"
type="submit">Sign in</button>
</form>
Component ts:
#Component({
selector: 'signin',
templateUrl: './signin.component.html',
encapsulation: ViewEncapsulation.None
})
export class SigninComponent implements OnInit{
constructor(){}
ngOnInit(){ }
onSubmit(form: NgForm){
console.log(form.value.inputUsername);
}
}
Thanks in advance.
You need to add the ngModel directive to each of the form fields. This registers the form fields to your form.
<input type="text"
id="inputUsername"
name="inputUsername"
class="form-control"
placeholder="User Name"
ngModel
required>
add FormsModule in appmodule.ts
import { FormsModule } from '#angular/forms';
imports: [
FormsModule,
]
My error is
EXCEPTION: Error: Uncaught (in promise): Template parse errors:
Can't bind to 'ngFormModel' since it isn't a known native property ("
<h3 class = "head">MY PROFILE</h3>
<form [ERROR ->][ngFormModel]="form" (ngSubmit)="onSubmit(form.value)">
<div class="row">
"): a#3:7
There is no directive with "exportAs" set to "ngForm" ("stname</label>
<input type="text" id="facebook" class="form-control" ngControl="firstname" [ERROR ->]#firstname="ngForm" >
</div>
"): a#9:85
There is no directive with "exportAs" set to "ngForm" ("/label>
<input type="text" id="facebook" class="form-control col-xs-3" ngControl="lastname" [ERROR ->]#lastname="ngForm" >
</div>
My template,
<h3 class="head">MY PROFILE</h3>
<form [ngFormModel]="form" (ngSubmit)="onSubmit(form.value)">
<div class="row">
<div class="form-group">
<label class="formHeading">firstname</label>
<input type="text" id="facebook" class="form-control" ngControl="firstname" #firstname="ngForm">
</div>
<div *ngIf="firstname.touched">
<div *ngIf="!firstname.valid" class="alert alert-danger">
<strong>First name is required</strong>
</div>
</div>
<div class="form-group">
<label class="formHeading">lastname</label>
<input type="text" id="facebook" class="form-control col-xs-3" ngControl="lastname" #lastname="ngForm">
</div>
<div *ngIf="lastname.touched">
<div *ngIf="!lastname.valid" class="alert alert-danger">
<strong>Last name is required</strong>
</div>
</div>
<div class="form-group">
<label class="formHeading">Profilename</label>
<input type="text" id="facebook" class="form-control col-xs-3" ngControl="profilename" #profilename="ngForm">
</div>
<div class="form-group">
<label class="formHeading">Phone</label>
<input type="text" id="facebook" class="form-control col-xs-3" ngControl="phone" #phone="ngForm">
</div>
<div *ngIf="phone.touched">
<div *ngIf="!phone.valid" class="alert alert-danger">
<strong>Phone number is required</strong>
</div>
</div>
<label class="formHeading">Image</label>
<input type="file" name="fileupload" ngControl="phone">
<div class="form-row btn">
<button type="submit" class="btn btn-primary " [disabled]="!form.valid">Save</button>
</div>
</div>
</form>
My Component
import {Component} from '#angular/core';
import { Http, Response, Headers} from '#angular/http';
import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject';
import {contentHeaders} from '../headers/headers';
import {FORM_DIRECTIVES} from '#angular/forms';
import {Router, ROUTER_DIRECTIVES} from '#angular/router';
import {Control, FormBuilder, ControlGroup, Validators} from '#angular/common';
#Component({
templateUrl: './components/profile/profile.html',
directives: [ROUTER_DIRECTIVES, FORM_DIRECTIVES],
})
export class Profile {
http: Http;
form: ControlGroup;
constructor(fbld: FormBuilder, http: Http, public router: Router) {
this.http = http;
this.form = fbld.group({
firstname: ['', Validators.required],
lastname: ['', Validators.required],
profilename: ['', Validators.required],
image: [''],
phone: [''],
});
}
onSubmit(form: any) {
console.log(form);
let body = JSON.stringify(form);
var headers = new Headers();
this.http.post('http://localhost/angular/index.php/profile/addprofile', body, {
headers: headers
})
.subscribe(
response => {
if (response.json().error_code == 0) {
alert('added successfully');
this.router.navigate(['/demo/professional']);
} else {
alert('fail');
}
});
}
}
The problem is that you're still importing from common and especially using the instructions of the old forms.
Import correctly the components for new forms:
import {FORM_DIRECTIVES, REACTIVE_FORM_DIRECTIVES} from '#angular/forms';
import {FormBuilder, FormGroup, Validators} from '#angular/forms';
And correct the component:
#Component({
...
directives: [FORM_DIRECTIVES, REACTIVE_FORM_DIRECTIVES]
})
export class AppComponent {
form: FormGroup;
constructor(fbld: FormBuilder) {
this.form = fbld.group({
...
});
}
...
}
Then correct the view: ngFormModel has been replaced by formGroup, and use formControl for your fields:
<form [formGroup]="form" (ngSubmit)="onSubmit(form.value)">
<div class="row">
<div class="form-group">
<label class="formHeading">firstname</label>
<input type="text" id="facebook" class="form-control" [formControl]="form.controls['firstname']" >
</div>
<div *ngIf ="form.controls['firstname'].touched">
<div *ngIf ="!form.controls['firstname'].valid" class = "alert alert-danger">
<strong>First name is required</strong>
</div>
</div>
...
<div class="form-row btn">
<button type="submit" class="btn btn-primary" [disabled]="!form.valid">Save</button>
</div>
</div>
</form>
Edit. From Angular 2.0.0-rc.5, is necessary to remove the directives from the component and import the form modules in AppModule:
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
#NgModule({
imports: [
...
FormsModule,
ReactiveFormsModule
],
...
bootstrap: [AppComponent]
})
export class AppModule { }
If you use a shared module, do not forget to export them:
#NgModule({
imports: [
...
FormsModule,
ReactiveFormsModule
],
exports: [
...
FormsModule,
ReactiveFormsModule
]
})
export class SharedModule { }
I had the same issue. What I did to solve it:
remove the tag, and add (click)-function to the button
checked my backend: there was an error in some special event... fixed it
Now it doesn't fire twice anymore.
So double check this! You never know...
Just import the following statement in ts,
import {FORM_DIRECTIVES, FormBuilder, Validators, REACTIVE_FORM_DIRECTIVES} from '#angular/forms';
directives: [CORE_DIRECTIVES, ROUTER_DIRECTIVES, FORM_DIRECTIVES, REACTIVE_FORM_DIRECTIVES],
Make the following changes in templates,
<h3 class = "head">MY PROFILE</h3>
<form [ngFormModel]="form" (ngSubmit)="onSubmit(form.value)">
<div class="row">
<div class="form-group">
<label class="formHeading">firstname</label>
<input type="text" id="facebook" class="form-control" [formControl]="form.controls['firstname']">
</div>
<div *ngIf ="firstname.touched">
<div *ngIf ="!firstname.valid" class = "alert alert-danger">
<strong>First name is required</strong>
</div>
</div>
<div class="form-group">
<label class="formHeading">lastname</label>
<input type="text" id="facebook" class="form-control col-xs-3" [formControl]="form.controls['lastname']">
</div>
<div *ngIf ="lastname.touched" >
<div *ngIf = "!lastname.valid" class = "alert alert-danger">
<strong>Last name is required</strong>
</div>
</div>
<div class="form-group">
<label class="formHeading">Profilename</label>
<input type="text" id="facebook" class="form-control col-xs-3" [formControl]="form.controls['profilename']" >
</div>
<div class="form-group">
<label class="formHeading">Phone</label>
<input type="text" id="facebook" class="form-control col-xs-3" [formControl]="form.controls['phone']">
</div>
<div *ngIf ="phone.touched" >
<div *ngIf = "!phone.valid" class = "alert alert-danger">
<strong>Phone number is required</strong>
</div>
</div>
<div class="form-row btn">
<button type="submit" class="btn btn-primary " [disabled]="!form.valid">Save</button>
</div>
Finally do this in your bootstrapping,
import {provideForms, disableDeprecatedForms} from '#angular/forms';
bootstrap(MyDemoApp, [
provideForms(),
disableDeprecatedForms()]);