Angular 4 Form FormArray Add a Button to add or delete a form input row - forms

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

Related

Angular 5: Obtaining form input value whether changed or not

How can I pass to onNext() the form's input value of the control named "Category1"? When the form is changed the value is passed but when the user doesn't change the value isn't. I need the value always passed. How do I do this?
<div class="app-form container-fluid">
<div class="row">
<div class="col-6">
<div class="button-left float-left">
<button (click)="onPrevious('tab-empinfo')">Previous</button>
</div>
</div>
<div class="col-6">
<div class="button-right float-right">
<button
(click)="onNext('tab-selection', {
form: 'category',
data: {category: formCategory.value['Category1']}})">
Next
</button>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<h4>Category</h4>
</div>
</div>
<div class="row">
<div class="col-12">
<form #formCategory="ngForm">
<div class="form-group" *ngFor="let input of categorySelection; let i = index">
<choice *ngIf="input.type == 'choice'" name="Category{{ i + 1 }}" ngModel
[multiple]="input.multiple" [question]="input.question" [answers]="input.answers"></choice>
</div>
</form>
</div>
</div>
</div>
<pre>
{{ formCategory.value | json }}
</pre>
Please see screen shot:
Try using an input tag instead of choice. Set the [(ngmodel)] to a property of your container, e.g. category1 and use the same name for each input. It does not need to match the property name, I arbitrarily set it to be category1. Set category1 property to the default value. The component's category1 property value will initialize one of the radio button to be set. Use the category1 property in your on click method parameter.
See https://stackblitz.com/edit/angular-yq5w2y?file=app/app.component.ts for a working example.
<div class="app-form container-fluid">
<div class="row">
<div class="col-6">
<div class="button-left float-left">
<button (click)="onPrevious('tab-empinfo')">Previous</button>
</div>
</div>
<div class="col-6">
<div class="button-right float-right">
<button
(click)="onNext('tab-selection', {
form: 'category',
data: {category: category1}})">
Next
</button>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<h4>Category</h4>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="form-group" *ngFor="let input of categorySelection">
<input type="radio" *ngIf="input.type == 'radio'" [(ngModel)]="category1" name="category1" value="{{input.answer}}" > {{input.answer}}
</div>
</div>
</div>
</div>
----
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular 5';
categorySelection = [{type: 'radio', answer: "who"}, {type: 'radio', answer: "what"}];
category1 = "who";
result;
onPrevious(data: string) {
this.result = data;
}
onNext(x, data: any): any {
this.result = data;
}
}

Angular2 disable submit button if form is invalid

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?

Angular2 ngForm not working

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,
]

templateUrl giving undefined when referring to class properties

While following the tutorial for forms at https://angular.io/docs/ts/latest/guide/forms.html
I keep getting undefined errors whenever referring to any of the properties that are defined in the .ts file as long as I use templateUrl and a seperate .html
When I take the same html and place in the .ts file inside `` in template everything works fine.
So my question is: Does the templateURl file need to define scope somehow? The tutorial doesn't seem to cover anything about it and I assumed that it would get access to the component variables that called it.
import { Component } from '#angular/core';
import { NgForm } from '#angular/forms';
import { Guitar } from './guitar';
#Component({
selector: 'guitar-form',
// template: `
// <div class="container">
// <h1>Guitar Form</h1>
// <form>
// <div class="form-group">
// <label for="brand">Brand</label>
// <select class="form-control" required
// [(ngModel)]="testmodel.brand" name="brand">
// <option *ngFor="let b of brands" [value]="b">{{b}}</option>
// </select>
// TODO: remove this: {{testmodel.brand}}
// </div>
// <div class="form-group">
// <label for="model">Model</label>
// <input type="text" class="form-control" required
// [(ngModel)]="testmodel.model" name="model">
// TODO: remove this: {{testmodel.model}}
// </div>
// <div class="form-group">
// <label for="color">Color</label>
// <input type="text" class="form-control"
// [(ngModel)]="testmodel.color" name="color">
// TODO: remove this: {{testmodel.color}}
// </div>
// <button type="submit" class="btn btn-default">Submit</button>
// </form>
// </div>
// `
templateUrl: 'app/guitar-form.component.html'
})
export class GuitarFormComponent {
brands = ['Fender', 'Gibson', 'Guild', 'Jackson', 'Epiphone', 'Charvel'];
testmodel = new Guitar(69, this.brands[0], 'Stratocaster', 'Black');
submitted = false;
onSubmit() { this.submitted = true; }
// TODO: remove this when we're done
get diagnostic() { return JSON.stringify(this.model); }
}
Funny, the undefined disappeared when I commented it out so I could demonstrate but ironically the diagnostic is no longer working when placed in the html and this was the one aspect that was working earlier. I have verified connectivity on an individual basis...
<div class="container">
<h1>Guitar Form</h1>
<form>
{{ diagnostic }}
<div class="form-group">
<label for="brand">Brand</label>
<select class="form-control" required
[(ngModel)]="testmodel.brand" name="brand">
<option *ngFor="let b of brands" [value]="b">{{b}}</option>
</select>
{{ testmodel.brand }}
</div>
<div class="form-group">
<label for="model">Model</label>
<input type="text" class="form-control" required
[(ngModel)]="testmodel.model" name="model">
TODO: remove this: {{testmodel.model}} -->
</div>
<div class="form-group">
<label for="color">Color</label>
<input type="text" class="form-control"
[(ngModel)]="testmodel.color" name="color">
{{ testmodel.color }}
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
I think you need to add
moduleId: module.id,
to the #Component({...}) decorator.
See also Angular2 - What is the meanings of module.id in component?

Angular 2: 'ngFormModel' since it isn't a known native property

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()]);