I am learning promises and I am trying to get a simple example to work but I get the error. The promises role is just to check whether a certain name has been entered and produce a boolean error on call back
Cannot read property 'shouldBeUnique' of null
Here is my component
import {Component, Inject} from '#angular/core';
import {FormGroup, Validators, FormBuilder} from '#angular/forms';
import {UsernameValidators} from './usernameValidators'
#Component({
selector: 'signup-form',
templateUrl: 'app/signup-form.component.html'
})
export class SignUpFormComponent {
form: FormGroup;
constructor(#Inject(FormBuilder) fb: FormBuilder) {
this.form = fb.group({
username: ['', Validators.compose([
Validators.required,
UsernameValidators.cannotContainSpace
]), UsernameValidators.shouldBeUnique],
password: ['', Validators.required]
})
}
get username(): any {return this.form.get('username');}
get password(): any {return this.form.get('password');}
}
Here is my component.html
<form [formGroup]="form" (ngSubmit)="signup()">
<div class="form-group">
<label for="username">Username</label>
<input
id="username"
type="text"
class="form-control"
formControlName="username" placeholder="Username"
>
<div *ngIf="username.touched && username.errors">
<div *ngIf="username.errors.shouldBeUnique" class="alert alert-danger">This username is already taken</div>
</div>
</form>
Here is my validator class where the promise is being made
import {FormControl} from '#angular/forms';
export class UsernameValidators {
static shouldBeUnique(control: FormControl) {
return new Promise((resolve, reject) => {
setTimeout(function(){
if (control.value == "mosh")
resolve({ shouldBeUnique: true});
else
resolve(null);
}, 1000)
});
}
}
Thanks
Try using the safe navigation operator (?.) to guard against null and undefined values in property paths.
<div *ngIf="username.touched && username.errors">
<div *ngIf="username.errors?.shouldBeUnique" class="alert alert-danger">This username is already taken</div>
</div>
This should resolve the error you are currently running into. Read more in the Angular 2 docs here:
https://angular.io/guide/template-syntax#safe-navigation-operator
Related
i have a component.ts that looks like this :
import { Component, OnInit } from '#angular/core';
import {FormBuilder, FormGroup, Validators} from "#angular/forms";
import {Observable, Subscription} from "rxjs";
import {ArticleService} from "../article.service";
#Component({
selector: 'app-article-new',
templateUrl: './article-new.component.html',
styleUrls: ['./article-new.component.css']
})
export class ArticleNewComponent implements OnInit {
response$?: Subscription;
constructor(private formBuilder: FormBuilder, private articleService: ArticleService) { }
articleForm: FormGroup = this.formBuilder.group({
title: ['', Validators.required],
content: ['', [Validators.required, Validators.minLength(69)]]
})
ngOnInit(): void {
}
async submit(){
console.log('article / sub', this.articleForm.value);
this.response$ = await this.articleService.createArticle(this.articleForm.value).subscribe(
res => console.log(res)
);
}
get title() {
return this.articleForm.get('title');
}
get content() {
return this.articleForm.get('content');
}
}
And the html of the component :
<h3>Create New Article</h3>
<form [formGroup]="articleForm" (ngSubmit)="submit()">
<div class="form-group">
<label for="title">Title :</label>
<input type="text" formControlName="title" class="form-control" id="title" required>
<div *ngIf="title?.invalid" class="alert alert-danger">
Title is required
</div>
</div>
<div class="form-group">
<label for="content">Content :</label>
<textarea class="form-control" formControlName="content" id="content" rows="3" required></textarea>
<div *ngIf="content?.errors && (content?.errors['required'] !== null)" class="alert alert-danger">
Content is required
</div>
<div *ngIf="content?.errors && (content?.errors['minLength'] !== null)" class="alert alert-danger">
Minimum length
</div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
As you can see, i have 3 divs with *ngIf. The first one with "Title" gave me a similar error that i solved by adding "?" after the variable name.
But with the content variable, adding the question mark didn't change anything. So i have that error for those 2 divs :
TS2533: Object is possibly 'null' or 'undefined'.
Yes, this is strict checking and having "long" paths like (content?.errors['required'] !== null will cause such things. It should clear up by using
content?.errors?.required
but I do prefer using hasError instead, so that is what I would suggest you to use, better readability in my opinion ;) So that would mean simply
content?.hasError('required')
and that can be applied to every formcontrol in your form, any validators. Please not that there is a gotcha with minLenght and maxLength. The L needs to be lowercase, so:
content?.hasError('minlength')
Try to use this :
articleForm= new FormGroup({
title: new FormControl('', Validators.required),
content: new FormControl('', [Validators.required, Validators.minLength(69)])
});
See this documentation :
https://www.concretepage.com/angular-2/angular-2-formgroup-example
Move the form creation logic inside ngOnInit hook
ngOnInit(): void {
this.buildForm();
}
buildForm():void{
this.articleForm = this.formBuilder.group({
title: ['', Validators.required],
content: ['', [Validators.required, Validators.minLength(69)]]
})
}
and add check in html part for articleForm
<form *ngIf="articleForm" [formGroup]="articleForm" (ngSubmit)="submit()">
Here is my component.ts:
import { Component, OnInit } from '#angular/core';
import { Validators, FormControl, FormGroup, FormBuilder } from '#angular/forms';
#Component({
selector: 'app-microplate',
templateUrl: './microplate.component.html',
styleUrls: ['./microplate.component.css']
})
export class MicroplateComponent implements OnInit {
form: FormGroup;
ngOnInit(): void {
this.form = new FormGroup({
columns: new FormControl('some value', [
Validators.required
])
});
}
get columns() { return this.form.get('columns'); }
}
Here is my component.html:
<form [formGroup]="form">
<label for="columns"><b>Columns: </b></label>
<input id="columns" type="text" [formControlName]="columns">
</form>
I get the following error message at form: FormGroup;:
Property 'form' has no initializer and is not definitely assigned in the constructor.
I have used the information from Angular website, but I cannot solve the issue. Any help would be appreciated.
Moshen the reason to use
get columns() { return this.form.get('columns'); }
Is for do something like
<form [formGroup]="form">
<label for="columns"><b>Columns: </b></label>
<!--see that you use formControlName="columns", not between []-->
<!--you can also use
<input id="columns" type="text" [formControlName]="'columns'">
see that in this case is an string, NOT the variable "columns" -->
<input id="columns" type="text" formControlName="columns">
<!--here use "columns" that is your FormControl to ask if you has errors and is touched-->
<div *ngIf="columns.errors && columns.touched">Required</div>
</form>
I have a problem when I get to the edit route. I get this message -
ERROR TypeError: Cannot read property 'name' of undefined
When I comment input name error comeback for the next input. Looking my code.
import { Component, OnInit } from '#angular/core';
import { SensorService } from '../servicesapi/sensor.service';
import { ActivatedRoute, Router } from '#angular/router';
import { ISensor } from '../sensor/sensor'
#Component({
selector: 'app-edit-sensor',
templateUrl: './edit-sensor.component.html',
styleUrls: ['./edit-sensor.component.scss']
})
export class EditSensorComponent implements OnInit {
id: number;
sens: ISensor;
constructor(private route: ActivatedRoute, private router: Router,
private
sensor: SensorService) { }
ngOnInit() {
// this.sens = new ISensor();
this.id = this.route.snapshot.params['id'];
this.sensor.getSingleSensor(this.id)
.subscribe(data => {
console.log("data" , data)
this.sens = data;
}, error => console.log(error));
}
updateEmployee() {
this.sensor.updateSensor(this.id, this.sens)
.subscribe(data => console.log(data), error => console.log(error));
// this.employee = new Employee();
// this.gotoList();
this.router.navigate(['/']);
}
onSubmit() {
this.updateEmployee();
}
}
Also my html showing error:
<h3>Update Employee</h3>
<div [hidden]="submitted" style="width: 400px;">
<form (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" id="name" required
[(ngModel)]="sens.name" name="name"> //here is problem
</div>
<div class="form-group">
<label for="path">Path</label>
<input type="text" class="form-control" id="path" required
[(ngModel)]="sens.path" name="path">
</div>
<button type="submit" class="btn btn-success">Submit</button>
</form>
</div>
I can read the data. I can even startle them, but this error returns me.
The reason you're getting this error is because the EditSensorComponent's HTML file is rendered by the DOM before the ngOnInit(). Now, since you've only defined sens and not initialized it, you're getting an undefined error
Try this:
1. Initialize the sens object in the EditSensorComponent while you're defining it.
2. Looking at the code, I'm assuming the sens object has two properties (name and path)
sens: ISensor = {
name: '',
path: ''
}
This is my employee.component.html
<div class="row">
<div class="input-field col s12">
<input type="text" name="name" #name="ngModel" [(ngModel)]="employeeService?.selectedEmployee.name" placeholder="Enter full name"
required>
<label>Name :
<label class="red-text">*</label>
</label>
</div>
</div>
Below one is the employee.component.ts
import { Component, OnInit } from '#angular/core';
import { NgForm } from '#angular/forms';
import { EmployeeService } from '../shared/employee.service';
import { Employee } from '../shared/employee.model';
#Component({
selector: 'app-employee',
templateUrl: './employee.component.html',
styleUrls: ['./employee.component.css'],
providers: [EmployeeService]
})
export class EmployeeComponent implements OnInit {
constructor(public employeeService: EmployeeService) { }
ngOnInit() {
this.resetForm();
}
resetForm(form?: NgForm) {
if (form) {
form.reset();
this.employeeService.selectedEmployee = {
name: '',
position: '',
office: '',
salary: null
};
}
}
}
Chrome throw "ERROR TypeError: Cannot read property 'name' of undefined" error while loading my angular initial load.
Cannot read property 'name' of undefined”
Means that some reference to name is wrong. There is only one in your code : employeeService?.selectedEmployee.name, therefore employeeService?.selectedEmployee is undefined.
Fix
Make sure selectedEmployee is not undefined, or use safe navigation employeeService?.selectedEmployee?.name
add Safe Navigation Operator ( ?. ) becouse of employeeService?.selectedEmployee undefined
Stackblitz demo
component.html
<input type="text" name="name" #name="ngModel" [(ngModel)]="employeeService?.selectedEmployee.name" placeholder="Enter full name" required>
component.ts
import { Component,ViewChild } from '#angular/core';
import { EmployeeService } from './employee.service';
import {NgForm} from '#angular/forms';
#ViewChild('myForm') myForm:any;
constructor(public employeeService: EmployeeService) { }
ngOnInit() {
this.resetForm(this.myForm);
}
resetForm(form?: NgForm) {
if (form) {
form.reset();
this.employeeService.selectedEmployee = {
name: '',
position: '',
office: '',
salary: null
};
}
}
Try this.
<input type="text" name="name" #name="ngModel" [(ngModel)]="employeeService?.selectedEmployee?.name" placeholder="Enter full name" required>
Add another ? after the selectedEmplyee. It will solve the problem.
The reason for the error is when the application starts the selectedEmplyee in employeeService is undefined. So make sure that variable is not undefined.
you need to instantiate the employeeService.selectedEmployee object so it won't be undefined instantiate the object in the constructor or ngOnInit() and you can not use Safe Navigation Operator ( ?. ) in ngModel
eg:
ngOnInit(){
this.employeeService.selectedEmployee=new Employee ();
}
#Krishna Rathore
Screenshot of StackBlitz
I have the following code:
HTML:
<div [class]="new_workflow_row_class" id="new_workflow_row">
<div class="col-sm-6">
<label class="checkmark-container" i18n>New Workflow
<input type="checkbox" id="new-workflow" name="new-workflow" [(ngModel)]="new_checkbox" (click)="uncheckBox($event, 'edit')">
<span class="checkmark" id="new-workflow-checkmark" [class]="checkmark_class"><span id="new-workflow-checkmark-content"></span>{{checkmark_content}}</span>
</label>
<input type="text" *ngIf="new_checkbox" id="new_workflow_name" name="new_workflow_name" (keyup)="clearWorkflowError(true)" [(ngModel)]="new_workflow" placeholder="Enter Workflow Name">
<p [hidden]="!show_workflow_error && !workflowForm.errors" class="workflow-error" i18n>The workflow name already exists. Please use a different name.</p>
</div>
</div>
Component.ts:
duplicateWorkflowValidator(control: FormControl) {
console.log("this validator was called!");
clearTimeout(this.workflowTimeout);
return new Promise((resolve, reject) => {
if (this.new_workflow != '') {
this.workflowTimeout = setTimeout(() => {
this
.requestService
.findWorkflow(control.value)
.subscribe((results: any) => {
let data: any = results.data;
if (data.duplicate) {
resolve({ duplicateWorkflow: { value: control.value}})
}
else if (results.status == "OK") {
resolve(null);
}
})
;
}, 500);
}
else {
resolve(null);
}
})
}
Inside constructor for component.ts:
this.workflowForm = new FormGroup({
name: new FormControl(this.new_workflow, [
Validators.required,
], this.duplicateWorkflowValidator.bind(this))
});
I am trying to bind this asynchronous validator to the reactive form but it's not working. I want to use the duplicateWorkflowValidator inside workflowForm and have it trigger an error message when it finds a duplicate workflow.
How do I a) bind the validator to the reactive form properly, b) access the validator errors? Thanks in advance, hopefully this makes sense.
You are mixing template forms with reactive forms. Chose one approach. In the below example I am using reactive forms.
Try this simplified version. For demonstration purposes below the validator will fail when I type test, but succeed when I type anything else. You will need to change that to your service call.
https://angular-sjqjwh.stackblitz.io
Template:
<form [formGroup]="myForm">
<div>
<div>
<input type="text" formControlName="name">
<div *ngIf="myForm.controls.name.hasError('duplicateWorkflow')">
Workflow already exists!
</div>
{{ myForm.controls.name.hasError('duplicateWorkflow') | json }}
</div>
</div>
</form>
Component
import { Component } from '#angular/core';
import { FormControl, FormGroup, Validators, Form, FormBuilder } from '#angular/forms';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
workflowTimeout: number = 0;
myForm: FormGroup;
constructor(private fb: FormBuilder) {
this.myForm = this.fb.group({
name: new FormControl('',
[Validators.required],
this.duplicateWorkflowValidator.bind(this))
});
}
duplicateWorkflowValidator(control: FormControl) {
console.log("this validator was called!");
return new Promise((resolve, reject) => {
if (control.value === 'test') {
this.workflowTimeout = setTimeout(() => {
resolve({ duplicateWorkflow: { value: control.value } })
}, 500);
}
else {
resolve(null);
}
});
}
}