[As a Newbie tried to put this into plnkr, but couldn't; problems getting #angular/forms added to json.]
Purpose: to iron out things I need to know to do all my work in FormBuilder
HTML:
<input type="text"
formControlName="bucket"
value="A"
[(ngModel)]="AllData.bucket" />
// added to button to test: [disabled]="this.myTestForm.invalid || this.myTestForm.pristine"
<div><button
type="submit">submit</button></div>
</form>
<div *ngIf="result.length > 0 ">{{result}}</div>
<div *ngIf="myTestForm.errors === true ">ERRORS FOUND</div>
Running the app: shows the formbuilder in the ts below initializes the input field correctly and if I add the [disabled] expression commented above it disables button correctly.
Here's the ts:
import {Component, OnInit} from '#angular/core';
import {Validators, FormBuilder, FormGroup} from '#angular/forms';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
myTestForm: FormGroup;
result: string;
AllData = { //// wired to ngModel
bucket: '12'
}
constructor(private fb: FormBuilder) {
this.result = '';
}
ngOnInit() {
this.myTestForm = this.fb.group({
bucket: ['starting value', [Validators.required, Validators.minLength(5)]]
<-- ngModel bucket above overrides this init value as expected -->
});
}
onSubmit(value: any) { // ways to show results
this.result = this.AllData.bucket;
// OR //
this.result = this.myTestForm.controls['bucket'].value;
// OR //
this.result = this.myTestForm.get('bucket').value;
}
}
The app inits with '12' in the input field as expected. No matter what I put into the textbox before pressing the submit button, devTools always shows the myTestForm 'error' property as undefined.
I'm expected errors to have some sort of string(s) based on the type of error(s) that is occurring.
Further, I scoured the web for ways to capture invalid fields as soon as the error occurs (of course for !pristine fields), but I couldn't get anything to work.
Any help will be much appreciated.
Thanks in advance,
Chuck
I have created a small demo to provide a suggestion on your approach
Do not use [(ngModel)] when your are using reactive forms approach, as ngModel will take precedence over the formControl and set its value to the control irrespective of formcontrol's value, that you have initialized.
<form [formGroup]="myTestForm" >
<input type="text"
formControlName="bucket"
value="A" />
<div><button
[disabled]="myTestForm.invalid || myTestForm.pristine"
type="submit" >submit</button></div>
</form>
To check form errors, use hasError() on controls
<div *ngIf="myTestForm.get('bucket').hasError('required')">Input is required</div>
<div *ngIf="myTestForm.get('bucket').hasError('minlength')">Min length should be 5</div>
Related
thank You for reading. I'm a Angular beginner and have an first problem I can't solve. I read many, many posts without success, sorry.
My seach form is working fine if I get the search phrase in one component and process it in another component. The *ngFor loop gives back the right result-array in dom and console: searchCustomer(phrase).
The exactly same search function works not, if I include a header component with another search form - although I get the right results via console(!). I included the header via app-header in component.html.
Why contains the array "results" the right items but the dom isn't showing it? This is my code:
search.component.html:
<form #f = "ngForm" action="verification" method="get">
<input [(ngModel)] = "phrase" type="text" name="phrase">
<button type="button" (click)="searchCustomer()">Search</button>
</form>
search.component.ts:
searchCustomer() {
this.dataService.setPhrase(this.phrase); // store phrase
this.router.navigate(['/results']); // navigate
}
result.component.html:
<app-header></app-header>
<div class="col-md-6" *ngFor="let vs of results">
...
</div>
result.component.ts:
import { ChangeDetectorRef, Component, OnInit } from '#angular/core';
// add ApiService
import { ApiService } from '../~services/api.service'; // ApiService
// add customers Model
import { Customer } from '../~interfaces/customers';
// DataService
import { DataService } from "../~services/data.service";
#Component({
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.css'],
})
export class ResultComponent implements OnInit {
// add array:
results: Customer[];
// add variable
phrase: string;
constructor(
private apiService: ApiService,
private dataService: DataService,
private cdr: ChangeDetectorRef
) {
}
ngOnInit() {
}
searchCustomer(phrase) {
// search
this.apiService.searchCustomer(phrase).subscribe((result:Customer[]) => {
this.results = result;
console.log(this.results);
this.cdr.detectChanges();
}
)
}
}
header.component.html:
<form #f = "ngForm">
<input type="text" [(ngModel)] = "phrase" type="text" value="{{phrase}}">
<button type="button" (click)="searchCustomer()">Search</button>
</form>
header.component.html:
searchCustomer(){
this.dataService.setPhrase(this.phrase);
this.resultComponent.searchCustomer(this.phrase);
}
If I start the search from Header, I can see the correct array "results" in Console, but not in dom. There is no refreshing.
Thank you for eye-opening,
Matthias
This was the solution in result.component.ts
responsePage$: Observable<ResponsePage>;
ngOnInit() {
this.apiService.searchCustomer(phrase).subscribe((result:Customer[]) => {
this.results = result;
this.searchCustomer(phrase);
});}
searchCustomer(phrase) {
this.responsePage$=this.apiService.customers(phrase);
}
How can i restrict first digit as 0 in the input textbox which accepts numbers.
for example:
Number cannot be like this 012345
Number can be like 123000
I have used pattern /^0|[^0-9.]/ but its not working in angular reactive forms.
My Input textbox control looks like below:
<input type="text" class="form-control" id="inputNumber" formControlName="inputNumber" maxlength="5" minlength ="1" pattern="/^0|[^0-9.]/" [(ngModel)]="inputNumber" required>
Any thoughts are highly appreciated.
Thanks for help.
Please use below pattern
[1-9][0-9]*
Sample code
<!DOCTYPE html>
<html>
<body>
Only numbers not starting with zero allowed.<br>
<input type="text" pattern="^[1-9][0-9]*$" required oninput="if(!this.value.match('^[1-9][0-9]*$'))this.value='';"></input>
</body>
</html>
Use Reactive form & a custom validator with reactive form and check the value on change. This will give more control in handling the form. The below code shows two different error when the input starts with 0 or if it is not a number, It will also disable the form submit button any invalid input.
To populate the data in the input you can use setValue like done in populateValue function
import {
Component,
VERSION,
OnInit
} from "#angular/core";
import {
FormGroup,
FormBuilder,
FormControl,
AbstractControl,
ValidationErrors,
ValidatorFn
} from "#angular/forms";
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
myForm: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.myForm = this.fb.group({
myInput: ["", [this.customValidator]] // custom validator
});
this.populateValue();
}
populateValue() {
// use this to populate input with api response
this.myForm.controls.myInput.setValue("wwedwedwed");
}
customValidator(control: AbstractControl): ValidationErrors {
let error = {
name: "",
message: ""
};
if (control.value !== "") {
// this validation will check if the value does not start with 0 or !isNaN
if (isNaN(control.value)) {
error.name = "notNumber";
error.message = "Cannot be a string";
return error;
}
if (control.value.startsWith(0)) {
{
error.name = "noZeroStart";
error.message = "Cannot start with 0";
return error;
}
}
return null;
}
return error;
}
}
<form [formGroup]="myForm">
<div>
<input formControlName='myInput'>
<div *ngIf="myForm.controls.myInput.errors">
{{myForm.controls.myInput.errors.message}}
</div>
</div>
<button type='submit' [disabled]="myForm.invalid">
Submit</button>
</form>
Stackblitz Demo
I have tried other solutions such as fixing form references but that didn't help me. Here is my form for a login
<form [formGroup]="longinForm" (ngSubmit)="onSubmit()">
<h1>Login Form</h1>
<div class="form-group" >
<label for="name" >Username</label>
<input type="text" name ="user" [(ngModel)]="loginuserdata.user"
#user="ngModel"
class="form-control" required >
<div *ngIf="submitted">
<div [hidden]="user.valid || user.pristine" class="alert alert-danger">
Username is required
</div>
</div>
</div>
<div class="form-group" >
<label for="pass" >Password</label>
<input type="text" name="pass" [(ngModel)]="loginuserdata.pass" #pass="ngModel"
class="form-control" required >
<div *ngIf="submitted">
<div [hidden]="pass.valid || pass.pristine" class="alert alert-danger">
Password is required
</div>
</div>
</div>
<button type="submit" class="btn btn-success">Submit</button>
</form>
also here is my login component
import { Component, OnInit, ViewChild } from '#angular/core';
import { NgForm, FormGroup } from '#angular/forms';
import { Router } from '#angular/router';
import { LoginService } from './login.service';
import { ILoginData } from './login-data';
#Component({
selector: 'app-loginform',
templateUrl: './loginform.component.html',
styleUrls: ['./loginform.component.css']
})
export class LoginformComponent implements OnInit {
loginForm:FormGroup;
loginuserdata : any[] = [];
error:string;
submitted=false;
constructor(private route: Router,private service:LoginService) { }
get f(){return this.loginForm.controls;}
ngOnInit() {
this.onSubmit();
}
onSubmit(){
if(this.loginForm.invalid){return;}//form invalid stop here
//form is valid do something
this.submitted=true;
if(this.f.user.value == "Admin" && this.f.pass.value == "Apassword"){
this.service.getAdmin()
.subscribe(data => this.loginuserdata = data)
this.route.navigateByUrl('admin');
err => this.error = err;
console.log(this.error);
}
}
}
If you guys need to see any other pieces of my code let me know please help me out I tried something similar to an answer solved to this but it didn't work for me.
You need to build the form controls, you have it defined but you need something like the following. You need to initialise the form group.
import { NgForm, FormGroup, FormBuilder } from '#angular/forms';
//Be sure to import formBuilder and inject through constructor. This will allow you
//to manage how you will build the form and add validation.
//Its a helper class provided by Angular to help us explicitly declare forms.
constructor( private formBuilder: FormBuilder ){}
public ngOnInit(): void {
this.loginForm = this.formBuilder.group({
username: ['', Validators.required],
password: ['', Validators.required]
});
This is where you can define the controls and the validators for the form group. (This example would just dictate that there needs to be a value for each in order to be valid. This should be built in the ngOnInit.
Then in your template you can remove the [(ngModel)] and make it look like the following. The value for the input will then be held within the control when you need to access it.
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
<input type="text" name ="user" [formControlName]="'username'"
class="form-control" required>
<form>
Above would serve as an example for the login username / email.
Also as mentioned in the comments you have a typo in the form in the HTML.
This should then be valid when you are trying to access the valid and invalid properties of the from group. Comment if you have any trouble.
Here is the Angular documentation for reactive forms.
How about you do this.
ngAfterViewInit() {
this.onSubmit()
}
I think it was not yet defined at ngOninit.
Check Life Cycle.
So I am having a very weird problem with Angular reactive form. I asked my instructor, he couldn't figure it out so I have only one place I am wishing to get some help from. It's here.
So I am using a Angular form and the signup.component.html code snippet is:
<form [formGroup]="form" (submit)="onSaveUser()" *ngIf="!isLoading">
<mat-accordion class="accordion-headers-align">
<mat-expansion-panel [expanded]="1">
<mat-expansion-panel-header>
<mat-panel-title>Personal data</mat-panel-title>
<mat-panel-description>Type your personal details</mat-panel-description>
</mat-expansion-panel-header>
<mat-form-field>
<input matInput type="text" formControlName="name" placeholder="Name">
<mat-error *ngIf="form.get('name').invalid">Please enter your name</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput type="date" formControlName="dob" placeholder="DD/MM/YYYY">
<mat-error *ngIf="form.get('dob').invalid">Please enter your valid date of birth in form of DD/MM/YYYY</mat-error>
</mat-form-field>
and continue like that, ignore the accordion part pls.
Then my signup.component.ts file is:
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormControl, Validators } from '#angular/forms';
import { ActivatedRoute, ParamMap } from '#angular/router';
#Component({
selector: 'app-signup',
templateUrl: './signup.component.html',
styleUrls: ['./signup.component.css']
})
export class SignupComponent implements OnInit {
isLoading = false;
form: FormGroup;
imagePreview: string;
constructor(public userService: UsersService, public route: ActivatedRoute) { }
ngOnInit() {
this.form = new FormGroup({
name: new FormControl(null, {validators: [
Validators.required,
Validators.minLength(3),
// tslint:disable-next-line:quotemark
Validators.pattern(some regex not of concern)
]}),
dob: new FormControl(null, {validators: [
Validators.required,
// tslint:disable-next-line:max-line-length quotemark
Validators.pattern(some regex not of concern)
]}),
and continued like this, nothing special. Just trying to map the form fields. So the form renders like this: The error I am getting in console is:
ERROR TypeError: "this.form is undefined, can't access property "get" of it".
ERROR Error: "formGroup expects a FormGroup instance. Please pass one in.
ERROR TypeError: "_co.form is undefined, can't access property "get" of it".
and I don't understand what is going wrong, I checked the documentation and everything, no help. I am guessing it's a possible bug so wanted to make sure.
I resolve with *ngIf="form", in this way form tag will be rendered only when form is ready.
<form [formGroup]="form" (submit)="onSaveUser()" *ngIf="form">
I have a custom Validator to check for spaces in input field, but I cant understand why input field is being undefined to the constructor.
CustomValidationComponent:
import {Component} from '#angular/core';
import {FormControl, FormGroup, FormBuilder} from '#angular/forms';
import {UsernameValidators} from './usernameValidators';
#Component({
selector: 'custom-validation',
template: `
<div [formGroup]="usernameGroup">
<input type="text" placeholder="Name" formControlName="username">
<div *ngIf="username.errors.cannotContainSpace"> Without spaces! </div>
</div>
`
})
export class CustomValidationFormComponent {
usernameGroup: FormGroup;
constructor(fb: FormBuilder){
this.usernameGroup = fb.group({
username: ['', UsernameValidators.cannotContainSpace],
});
}
UsernameValidator:
import {FormControl} from '#angular/forms';
export class UsernameValidators {
static cannotContainSpace(control: FormControl){
if (control.value.indexOf(' ') >= 0)
return { cannotContainSpace: true };
return null;
}
}
PLUNKER
Your username formControl is not a class variable to access directly. You can access it through the FormGroup which is userNameGroup.
<div *ngIf="usernameGroup.controls['username'].errors?.cannotContainSpace">
Or as Manuel Zametter mentioned:
<div *ngIf="usernameGroup.controls.username.errors?.cannotContainSpace">
The ?. is safe navigation operator which does a check on errors if it is undefined or null.
Plunker