Populating Angular Reactive Forms correcctly - javascript

In an angular 7 app, I have created a list of managers. When a user clicks on an item, a view with a populated form should be displayed where the user can then update the manager details. While my view is shown, the manager details doesn't show in the form itself and I can see two errors in the console
ERROR Error: formGroup expects a FormGroup instance. Please pass one in.
ERROR TypeError: Cannot read property 'firstname' of null
at ManagerViewComponent.push../src/app/manager/manager-view/manager-view.component.ts.ManagerViewComponent.initForm
This is what my code looks like at the moment.
.ts
import { FormGroup, FormControl } from "#angular/forms";
import { ManagerService } from "./../../services/manager.service";
import { ActivatedRoute, Router } from "#angular/router";
import { Component, OnInit } from "#angular/core";
#Component({
selector: "app-manager-view",
templateUrl: "./manager-view.component.html",
styleUrls: ["./manager-view.component.css"]
})
export class ManagerViewComponent implements OnInit {
editManagerForm: FormGroup;
manager = null;
constructor(
private route: ActivatedRoute,
private managerService: ManagerService
) {}
ngOnInit() {
this.managerDetails();
this.initForm();
}
private initForm() {
let firstname = this.manager.firstname;
let lastname = this.manager.lastname;
let username = this.manager.username;
let password = this.manager.password;
let terminal = this.manager.terminal.name;
this.editManagerForm = new FormGroup({
firstname: new FormControl(firstname),
lastname: new FormControl(lastname),
username: new FormControl(username),
password: new FormControl(password),
terminal: new FormControl(terminal)
});
}
managerDetails() {
const managerId = this.route.snapshot.paramMap.get("id");
this.managerService.getManager(managerId).then(data => {
this.manager = data;
});
}
.html
<form id="editManager" [formGroup]="editManagerForm">
<div class="form-row">
<div class="col-6 form-group">
<label for="">Firstname</label>
<input
formControlName="firstname"
type="text"
class="form-control"
/>
</div>
<div class="col-6 form-group">
<label for="">Lastname</label>
<input
formControlName="lastname"
type="text"
class="form-control"
/>
</div>
</div>
<div class="form-row">
<div class="col-6 form-group">
<label for="terminal">Bus Terminal</label>
<select formControlName="terminal" class="form-control">
<option selected>{{ manager.terminal.name }}</option>
<option *ngFor="let terminal of terminals" [ngValue]="terminal">
{{ terminal.name }}
</option>
</select>
</div>
</div>
<!-- <p>{{ editManagerForm.value | json }}</p> -->
</form>
In app.module.ts I have both FormsModule and ReactiveFormsModule imported correcctly like this
import { FormsModule, ReactiveFormsModule } from "#angular/forms";
Apparently I am doing something wrong since this is my first attempt at populating reactive forms. Please help me fix this

You will need to make the following changes when you initialise your editManagerForm. You should use a FormBuilder to generate your Form Group and Form Controls.
Do remember to import formBuilder and formGroup on your component.
import { FormBuilder, FormGroup } from '#angular/forms';
.
.
constructor(
private formBuilder: FormBuilder,
) { }
editManagerForm: FormGroup = this.formBuilder.group({
firstName: [null],
lastName: [null],
username: [null],
password: [null],
terminal: [null],
}
I assume you will populate the editManagerForm on the managerDetails() method. You can achieve this by using patchValue:
managerDetails() {
const managerId = this.route.snapshot.paramMap.get("id");
this.managerService.getManager(managerId).then(data => {
this.editManagerForm.patchValue(data);
});
}
Note: I am assuming that the data you fetched from getManager() is an object in the following format
data = {
firstName: '',
lastName: '',
username: '',
password: '',
terminal: ''
}

Related

Dynamically adding new text box after button press is not working - Angular

I am creating an Edit Employee form and there is a button Add new skill which need to create new text box to add the skill.
But when I click on Add new skill, it is not adding any new text boxes and displaying these details inside the console.
My typescript code :
import { Component, OnInit } from '#angular/core';
import { FormArray, FormBuilder, NgForm, Validators } from '#angular/forms';
import { EmployeeDepartment } from '../employee-department-class';
#Component({
selector: 'app-edit-emp-form-builder',
templateUrl: './edit-emp-form-builder.component.html',
styleUrls: ['./edit-emp-form-builder.component.css']
})
export class EditEmpFormBuilderComponent implements OnInit {
/**
* Using some default values and may be different from other component
*/
department = [
new EmployeeDepartment(1, 'Payroll'),
new EmployeeDepartment(2, 'Internal'),
new EmployeeDepartment(3, 'HR')
]
skills: any = []
employeeDetails = this.fb.group({
employeeName: ['', [Validators.required, Validators.minLength(4), Validators.maxLength(20)]],
department: [null, [Validators.required]],
skills: this.fb.array([
this.fb.control('')
])
});
constructor(private fb: FormBuilder) {
}
get skill() {
return this.employeeDetails.get('skills') as FormArray;
}
addSkill() {
this.skills.push(this.fb.control(''));
}
onSubmit(f: NgForm) {
console.log(f);
}
ngOnInit(): void {
}
}
My HTML code for the skill
<div formArrayName="skills">
<h3>Skill</h3>
<button (click)="addSkill()">Add new skill</button>
<div *ngFor="let s of skill.controls; let i = index">
<label>Skill:
<input type="text" [formControlName]="i">
</label>
</div>
</div>
Remove the variable skills: any = [] from your code.
Rename get skill() to get skills()

In my component add new Item is not working in angular

All files are attached as below,
component.html - This is component file
component.ts - This is component ts file
admin-service - This is service file
admin.ts - This is model file
add-que.html - This is component html file.
<div>
<form [formGroup]="adminForm" (ngSubmit)="newQuestion()">
<div class="form-group">
<label for="exampleInputEmail1">Question</label>
<input formControlName="description" type="description" placeholder="Enter question"
class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp">
<small class="text-danger"
*ngIf="!adminForm.get('description').valid && adminForm.get('description').touched">
Please Enter a Question</small>
</div>
<div class="form-group m-auto">
<div class="col-6">
(a)<input formControlname="alternatives" type="text">
(b)<input formControlname="alternatives" type="text">
</div>
<div class="col-6">
(c)<input formControlname="alternatives" type="text">
(d)<input formControlname="alternatives" type="text">
</div>
</div>
<div class="m-auto">
<input type="submit" value="Add" class="btn btn-primary"/>
</div>
</form>
</div>
**add-que.ts** - This is component ts file.
import { Component, OnInit } from '#angular/core';
import { FormControl, FormGroup, Validators } from '#angular/forms';
import { Router } from '#angular/router';
import { AdminService } from '../service/admin.service';
#Component({
selector: 'app-add-question',
templateUrl: './add-question.component.html',
styleUrls: ['./add-question.component.css']
})
export class AddQuestionComponent implements OnInit {
adminForm = new FormGroup({
description: new FormControl("", [Validators.required]),
alternatives: new FormControl("", [Validators.required])
});
constructor(private adminService: AdminService, private router: Router) { }
ngOnInit(): void {
}
newQuestion(){
if(this.adminForm.valid){
this.adminService.addQue(this.adminForm.value).subscribe(res => {
this.adminForm.reset();
this.router.navigate(["/admin"]);
})
}
}
}
**admin-service.ts** - This is service ts file.
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Observable } from 'rxjs';
import { Admin } from "../model/admin";
#Injectable({
providedIn: 'root'
})
export class AdminService {
private ROOT_URL = "http://localhost:3300/questions";
private httpOptions = {
headers: new HttpHeaders().set("Content-Type", "application/json")
};
constructor(private http: HttpClient) { }
getQuestion(): Observable<Admin[]> {
return this.http.get<Admin[]>(this.ROOT_URL);
}
getQue(id: string){
return this.http.get<Admin>(`${this.ROOT_URL}/${id}`);
}
addQue(admin){
return this.http.post<any>(this.ROOT_URL, admin, this.httpOptions);
}
}
**admin.ts** - This is model ts file.
export interface Admin {
description: String,
alternatives: [
{
text: {
type: String,
required: true
},
isCorrect: {
type: Boolean,
required: true,
default: false
}
}
]
}
I did created add-question component with form as above. I did tried to add new question through form in angular but not get anything. i did attached my files as above.
you sould'nt use this.router.navigate(["/admin"]); to try "refresh a component". That's no work (there are no changes). In general you has a component with a ngOnInit. Simply get out the code under a ngOnInit in a function and call this function in subscribe, e.g.
ngOnInit()
{
this.init()
}
init()
{
..here you initialize your variables
}
submit(){
...
.subscribe(res=>{
....
this.init()
})
}

Angular8 form input fields disabled

I am using Angular8 and have a form used to update a product. My problem however is this forms input fields and submit button is disabled. Can anyone advise what I am doing wrong? I would expect to be able to edit the input fields text and submit the form.
html:
<div class="bodyClass">
<h1>{{title | titlecase}}</h1>
<div class="card">
<div class="card-body">
<form *ngIf="angForm && productData" [formGroup]="angForm" novalidate>
<div class="form-group">
<label class="col-md-4">Product Name</label>
<input type="text" class="form-control" formControlName="name" #name/>
</div>
<div *ngIf="angForm.controls['name'].invalid && (angForm.controls['name'].dirty || angForm.controls['name'].touched)"
class="alert alert-danger">
<div *ngIf="angForm.controls['name'].errors.required">
Product Name is required.
</div>
</div>
<div class="form-group">
<button (click)="updateProduct(name.value)" type="submit" class="btn btn-primary"
[disabled]="angForm.invalid">
Update Product
</button>
</div>
</form>
</div>
</div>
</div>
component:
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormBuilder, Validators } from '#angular/forms';
import { Observable } from 'rxjs';
import { PermissionsService } from '../../services/permissions.service';
import { Permissions } from '../../model/permissions';
import { ProductsService } from '../../services/products.service';
import { DataService } from '../../services/data.service';
import { Product } from '../../model/product';
#Component({
selector: 'app-productsupdate',
templateUrl: './productsupdate.component.html',
styleUrls: ['./productsupdate.component.css']
})
export class ProductsupdateComponent implements OnInit {
angForm: FormGroup;
private permissionsObservable: Observable<Permissions>;
private showData: boolean = true;
private title: string;
private productData: Product;
constructor(private _permissionsService: PermissionsService, private _productService: ProductsService,
private dataService: DataService, private fb: FormBuilder) {
this.createForm();
}
ngOnInit() {
this.title = 'Update Product';
if (this.dataService.serviceData) {
console.log(this.dataService.serviceData);
this.productData = this.dataService.serviceData;
}
}
ngDoCheck() {
if (this.productData) {
this.angForm.get('name').setValue(this.productData.name);
}
}
createForm() {
this.angForm = this.fb.group({
name: ['', Validators.required ],
decription: ['', Validators.required ],
status: ['', Validators.required ]
});
}
updateProduct() {
console.log('submit');
}
}
Screenprint:
You can see that the button is disabled and the input text is non-editable.
You are using ngDoCheck, so everytime angular runs it, for example when you are trying to type, and productData is populated,
this.angForm.get('name').setValue(this.productData.name);
is run, and thus always setting the value making it seem that you cannot edit the field.
Since this can also be a race condition, as forms are asynchronous, I suggest building the form after getting value to productData (if getting value). So remove the createForm() function from constructor and add it later on:
if (this.dataService.serviceData) {
console.log(this.dataService.serviceData);
this.productData = this.dataService.serviceData;
}
this.createForm();
Modify the createForm function a bit:
createForm() {
this.angForm = this.fb.group({
name: [this.productData.name || '', Validators.required ],
description: [this.productData.description || ''],
status: [this.productData.status || '']
});
}

How to create nested formgroup and take value

I have created a form but I am not able to create nested form as I need to add certain input fields dynamically which I am getting but now what is required is like when I click add a link it should add form nested.
Form image
Add button near input field should add only social media link and GitHub input field others will remain same.
HTML
<div class="container">
<p> </p>
<div>
<form [formGroup]="searchForm" novalidate (ngSubmit)="submit(searchForm.value)">
<div formArrayName="properties">
<div *ngFor="let prop of searchForm.get('properties').controls; let i = index">
<div class="row" [formGroupName]="i">
<select formControlName="desg" class="form-control">
<option value="CEO">CEO</option>
<option value="CTO">CTO</option>
<option value="CMO">CMO</option>
<option value="Project Manager">Project Manager</option>
</select>
<input formControlName="name" type="text" class="form-control" placeholder="Name">
<input formControlName="linkedin" type="text" class="form-control" placeholder="LinkedIn Url">
<input formControlName="github" type="text" class="form-control" placeholder="Github Url">
<button *ngIf="searchForm.controls['properties'].length > 1 " (click)="onDelProperty(i)">Delete</button>
</div>
</div>
</div>
<p>
</p>
<a (click)="onAddProperty()">Add</a>
<button class="btn btn-bold btn-primary" type="submit">submit</button>
</form>
</div>
</div>
component.ts
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormBuilder, FormArray, FormControl } from '#angular/forms';
import { IcoService } from '../../services/ico.service';
import { debug } from 'util';
#Component({
selector: 'app-team',
templateUrl: './team.component.html',
styleUrls: ['./team.component.css']
})
export class TeamComponent implements OnInit {
searchForm: FormGroup;
searchForm1: FormGroup;
constructor(private fb: FormBuilder,private icoService: IcoService,) { }
ngOnInit() {
this.searchForm = this.fb.group({
properties: this.fb.array([this.createItem()])
});
}
createItem(): FormGroup {
return this.fb.group({
name: '',
desg: '',
social_media: '',
github:''
});
}
submit(formData: any) {
this.icoService.teamDetail(formData).subscribe((result) => {
console.log()
}, (err) => { console.log('err',err) })
}
onAddProperty() {
for(var i=1; i<=1; i++) {
(this.searchForm.get('properties') as FormArray).push(this.createItem());
}
}
onDelProperty(index:any) {
for(var i=1; i<=1; i++) {
(this.searchForm.get('properties') as FormArray).removeAt(index);
}
}
}
You Can try for following, This may help for you:
import { ToasterContainerComponent, ToasterService, ToasterConfig} from 'angular2-toaster';
// ======================== Internal Dependency Module Loading Start ================================
import {TabViewModule} from 'primeng/tabview';
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup, Validators,FormArray, FormControl} from '#angular/forms';
import { Router } from '#angular/router';
import { GlobalAjaxMethodHandler } from '../util/GlobalAjaxMethodHandler';
import { GlobalValidationService } from '../util/GlobalValidationService';
import { AjaxMethodName } from '../util/GlobalAjaxPathHandler';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
loginForm: FormGroup;
form = new FormGroup({
cities: new FormArray([
// new FormControl('drp')
this.initAddress()
]),
});
constructor(public _fb: FormBuilder, private _GlobalAjaxMethodHandler: GlobalAjaxMethodHandler, public router: Router,public _toasterService:ToasterService) { }
ngOnInit() {
this.formIntialization();
}
initAddress() {
// initialize our address
return this._fb.group({
third : ['', Validators.required],
second: [''],
first: [''],
four: ['']
});
}
//================================= Initialize form Value ============================================
formIntialization() {
this.loginForm = this._fb.group ({
Email : ['', [Validators.required, GlobalValidationService.emailValidator]],
Password : ['', [Validators.required, GlobalValidationService.passwordValidator]]
});
}
get cities(): FormArray { return this.form.get('cities') as FormArray; }
addCity() {
this.cities.push(this._fb.group({
third : ['', Validators.required],
second: [''],
first: [''],
four: ['']
}));
}
onSubmit() {
console.log(this.cities.value); // ['SF', 'NY']
console.log(this.form.value); // { cities: ['SF', 'NY'] }
}
setPreset() {
if(this.cities.length > 1){
this.cities.removeAt(this.cities.length-1);
}
}
}
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<div formArrayName="cities">
<!-- <div *ngFor="let city of cities.controls; index as i">
<input [formControlName]="i" placeholder="City">
</div> -->
<div *ngFor="let city of cities.controls; index as i">
<div [formGroupName]="i">
<select formControlName="first">
<option [selected]="true">--Select--</option>
<option>A1</option>
<option>B1</option>
<option>C1</option>
</select>
<select formControlName="second">
<option [selected]="true">--Select--</option>
<option>A2</option>
<option>B2</option>
<option>C2</option>
</select>
IN : <input type="checkbox" formControlName="third" value="+">
Out :<input type="checkbox" formControlName="four" value="+">
</div>
</div>
</div>
<input type="button" name="" value="Enable integration" (click)="onSubmit()">
</form>
I'm not sure what your for loop is for in your onAddProperty and onDelProperty methods.
And if I understand correctly, your name and desg controls should be added to the fb.group defined in ngOnInit. Those will then be defined only one time. Only add to the createItem method those controls that you want repeated each time the user clicks the Add.
I have similar code that adds an address block. My code looks something like this:
#Component({
selector: 'my-signup',
templateUrl: './app/customers/customer.component.html'
})
export class CustomerComponent implements OnInit {
customerForm: FormGroup;
get addresses(): FormArray{
return <FormArray>this.customerForm.get('addresses');
}
constructor(private fb: FormBuilder) { }
ngOnInit(): void {
this.customerForm = this.fb.group({
firstName: ['', [Validators.required, Validators.minLength(3)]],
lastName: ['', [Validators.required, Validators.maxLength(50)]],
addresses: this.fb.array([this.buildAddress()])
});
}
addAddress(): void {
this.addresses.push(this.buildAddress());
}
buildAddress(): FormGroup {
return this.fb.group({
addressType: 'home',
street1: '',
street2: '',
city: '',
state: '',
zip: ''
});
}
}
In the above example, the first name and last name appear only once, but the address fields are repeated each time the user clicks an add button.
For a full working example, check out this github: https://github.com/DeborahK/Angular2-ReactiveForms (specifically the Demo-Final folder)
UPDATE to include YOUR code:
Make the following change ... notice that everything you do not want repeated is in the ngOnInit. Everything you do want repeated is in the createItem.
ngOnInit() {
this.searchForm = this.fb.group({
name: '',
desg: '',
properties: this.fb.array([this.createItem()])
});
}
createItem(): FormGroup {
return this.fb.group({
social_media: '',
github:''
});
}
You also, of course, need to remove the fields you don't want repeated from the ngFor in your html. Just move them up above the ngFor.

TypeError: Cannot create property 'validator' on string 'abc#gmail.com' at setUpControl

I face issue in formGroup. First Based on URL I take some value and call to API for retrieve particular user-data for pre-field text.
register.html
<form [formGroup]="form" (ngSubmit)="onSubmit(form.value)" class="form-horizontal">
<div class="form-group row">
<label for="inputEmail3" class="col-sm-4 ">Username</label>
<div class="col-sm-8">
<input [formControl]="email" type="text" class="form-control" id="inputEmail3" placeholder="Email Address" [readonly]="isReadOnly">
</div>
</div>
</form>
register.component.ts
import { Component } from '#angular/core';
import { FormGroup, AbstractControl, FormBuilder, Validators } from '#angular/forms';
import { Router, ActivatedRoute } from '#angular/router';
import { EmailValidator, EqualPasswordsValidator } from '../../theme/validators';
#Component({
selector: 'register',
templateUrl: './register.html',
})
export class Register {
public form: FormGroup;
public email: AbstractControl;
public username: string;
constructor(private registerService: RegisterService, fb: FormBuilder, private router: Router, private route: ActivatedRoute) {
this.form = fb.group({
'email': ['', Validators.compose([Validators.required])]
.... etc..
});
this.email = this.form.controls['email'];
this.registerService.getUser({ userId: "asdasd2123da2das" }).subscribe(posts => {
if (posts) {
var userObj = posts.json();
console.log("userObj : ", userObj.data);
if (userObj.data && userObj.data[0].email) {
this.email = this.username = userObj.data[0].email; // ouput : abc#gmail.com
this.isReadOnly = true;
this.router.navigateByUrl('/register');
} else {
alert("You are Not Autorize to access this Page");
this.router.navigateByUrl('/login');
}
}
});
}
}
Error Details :
TypeError: Cannot create property 'validator' on string 'abc#gmail.com'
at setUpControl (http://localhost:3004/vendor.dll.js:9739:23)
at FormControlDirective.ngOnChanges (http://localhost:3004/vendor.dll.js:44196:89)
at Wrapper_FormControlDirective.ngDoCheck (/ReactiveFormsModule/FormControlDirective/wrapper.ngfactory.js:50:18)
<form [formGroup]="form" (ngSubmit)="onSubmit(form.value)" class="form-horizontal">
<div class="form-group row">
<label for="inputEmail3" class="col-sm-4 ">Username</label>
<div class="col-sm-8">
<input formControlName="email" type="text" class="form-control" id="inputEmail3" placeholder="Email Address" [readonly]="isReadOnly">
</div>
</div>
</form>
please try like this change [formControl] to formControlName.
And to set the output to the input field please do the following, point the line this.form.patchValue
import { Component } from '#angular/core';
import { FormGroup, AbstractControl, FormBuilder, Validators } from '#angular/forms';
import { Router, ActivatedRoute } from '#angular/router';
import { EmailValidator, EqualPasswordsValidator } from '../../theme/validators';
#Component({
selector: 'register',
templateUrl: './register.html',
})
export class Register {
public form: FormGroup;
public email: AbstractControl;
public username: string;
constructor(private registerService: RegisterService, fb: FormBuilder, private router: Router, private route: ActivatedRoute) {
this.form = fb.group({
'email': ['', Validators.compose([Validators.required])]
.... etc..
});
this.email = this.form.controls['email'];
this.registerService.getUser({ userId: "asdasd2123da2das" }).subscribe(posts => {
if (posts) {
var userObj = posts.json();
console.log("userObj : ", userObj.data);
if (userObj.data && userObj.data[0].email) {
this.email = this.username = userObj.data[0].email; // ouput : abc#gmail.com
this.form.patchValue({
email : this.email
});
this.isReadOnly = true;
this.router.navigateByUrl('/register');
} else {
alert("You are Not Autorize to access this Page");
this.router.navigateByUrl('/login');
}
}
});
I missed the square brackets in formGroup in markup as below
<form formGroup="form">
</form>
and it throws similar error as
ERROR TypeError: Cannot create property 'validator' on string 'form'
Once the square brackets added in formGroup (see below), the error disappears
<form [formGroup]="form">....</form>
For complete information about each kind of form, see Reactive Forms and Template-driven Forms from the https://angular.io/guide/forms-overview website. you are missing actual syntax for Grouping form controls and Registering the control in the template.both are valid in different case
<input type="text" [formControl]="name">
<input type="text" formControlName="firstName">

Categories

Resources