Syncfusion date-picker doesn't work properly with angular reactive forms - datepicker

In my Angular 7.0 project, I use two Syncfusion date-pickers to show 'created by' date and the 'modified by' date.
The controls work fine with the exact same data when I use tempalte driven approach with "[(ngModel)]".
The same controls on the same page doesn't work when I use it inside a reactive form. It just give me an error on the console (value.match is not a function) and show me just two textboxes instead the usual date controls. As you can see in the HTML file, I converted the school object to JSON and see the value for the date figures same time. I did not see any incompatibility there too.
.HTML File
<form [formGroup]="registerForm" (ngSubmit)="onSubmit()" class="pt-2">
<div class="row">
<div class="col-10">
<div class="form-group">
<div class="row">
<div class="col-4">
<span>Created date</span>
</div>
<div class="col-6">
<!-- This is doesn't work -->
<ejs-datepicker #createdDate class="syncfusion-dtp" placeholder='Enter date' format="dd-MM-yyyyy" aria-label="Created date"
[readonly]="true" [disabled]="true"
formControlName="createdOn" name="Created On"></ejs-datepicker>
</div>
</div>
</div>
</div>
</div>
<button type="submit" class="float-right ml-1 btn btn-info " [disabled]="!registerForm.valid">Submit</button>
</form>
<!-- This works -->
<ejs-datepicker class="syncfusion-dtp" placeholder='Enter date' format="dd-MM-yyyyy" aria-label="Modified date"
[readonly]="true" [disabled]="true"
[(ngModel)]="school.modifiedOn" name="Modified On"></ejs-datepicker>
.ts file
export class ListItemViewEditComponent implements OnInit, OnChanges {
#Output() notifyParentOnUpdate: EventEmitter<any> = new EventEmitter<any>();
#Input() schoolObject: School;
school = new School();
schoolNumber: number;
#ViewChild('createdDate') createdDate: DatePicker;
#ViewChild('modifiedDate') modifiedDate: DatePicker;
constructor(private fb: FormBuilder, private route: ActivatedRoute, private schoolsService: SchoolsService) { }
registerForm = this.fb.group({
id: [''],
schoolNumber: ['', Validators.required],
schoolName: [''],
description: [''],
createdOn: [''],
createdBy: [''],
modifiedOn: [''],
modifiedBy: ['']
});
ngOnChanges() {
this.school = this.schoolObject;
this.registerForm.setValue({
id: [this.school.id],
schoolNumber: [this.school.schoolNumber],
schoolName: [this.school.schoolName],
description: [this.school.description],
createdOn: [this.school.createdOn],
createdBy: [this.school.createdBy],
modifiedOn: [this.school.modifiedOn],
modifiedBy: [this.school.modifiedBy]
});
}
}
Please let me know if I am missing anything.

There are a few things you can check,
In the app.module.ts make sure you have imported ReactiveFormsModule.
In the component class, registerForm has been declared and set the type to FormGroup (Not essential, but let's follow good practices)
Move the registerForm declaration inside ngOnInit (Very first line)
I have created a StackBlitz example for you - https://stackblitz.com/edit/angular7-synfunction-datepicker
In the example I have set the [disabled]=false and [readonly]=false. So, I can see the actual date picker popup.
If you are struggling to set the value of the datepicker. Make a function and set the value inside the function and call it from somewhere. (i.e. NgOnInit, NgOnChange)
Hope this helps.
Cheers.

I guess the likely cause of this issue must be related to string parsing. The error “value.match is not a function” must be because, the type of value is not a string, since match is a method that belongs to String prototype. So, ensure if there is a type mismatching error in your application.
For your reference, I have prepared a sample with Syncfusion Datepicker using Angular Reactive forms.
https://stackblitz.com/edit/angular-eix8bh-z25gns?file=reactiveform.component.ts
If this does not help, you can contact the Syncfusion support center in the below link.
https://www.syncfusion.com/support/directtrac/incidents

Related

Angular 2 FormGroup Validation Types

Component:
ngOnInit() {
this.record = new FormGroup({
movement: new FormControl(''),
weight: new FormControl('', [Validators.required, Validators.minLength(1), Validators.maxLength(3)]),
date: new FormControl('', [Validators.required]),
comments: new FormControl('', [Validators.required, Validators.minLength(3), Validators.maxLength(25)]),
});
}
View:
<div class="column col-6">
<div class="form-group">
<label class="form-label">Weight</label>
<input formControlName="weight" class="form-input" type="text" placeholder="Weight" />
</div>
</div>
<div class="column col-6">
<div class="form-group">
<label class="form-label">Date</label>
<input formControlName="date" class="form-input" type="text" placeholder="Date" />
</div>
</div>
I've got the above validation working just fine, but I need to change the validators on weight to only accept numerical values and the validators on date to accept a specific format (##/##/####).
I've been searching forever and haven't found any built-in methods of doing this. Does anyone have any suggestions? Thanks!
There are only a small number of built-in form validation rules in Angular.
required
minLength
maxLength
pattern (which you could use for email)
EDIT:
More built in:
min
max
email
nullValidator
requiredTrue
Validators API (thank you, #developer033)
If you want to do anything beyond that, you will have to code your own. They are not difficult.
You can find an example on how to build and use a custom validator at the link below.
Custom validation
If you happen to have a Pluralsight subscription, Deborah Kurata has a great course on Angular Reactive Forms, and there is a clip or two on custom form validation in the Validation module.

Edit form field labels in angular 2

I have a built a model-driven (reactive) form, as shown here, in Angular 2.
My html looks like this:
<form [formGroup]="userForm" (ngSubmit)="onSubmit(userForm.value, userForm.valid)">
<label for="firstName">First Name:</label>
<input type="text" formControlName="firstname" id="firstName" required>
<label for="lastname">Last Name:</label>
<input type="text" formControlName="lastname" id="lastName" required>
<br>
<label for="email">Email:</label>
<input type="email" formControlName="email" id="email">
<br>
</form>
In my .ts file:
import { FormGroup, FormControl, FormBuilder, Validators } from '#angular/forms';
...
ngOnInit() {
this.paymentForm = this.formBuilder.group({
firstname: ['', Validators.required],
lastname: ['', Validators.required],
email: ['',],
})
this.userForm.valueChanges.subscribe(value => {
console.log(value);
});
}
I've added the required attribute in my template as well, as suggested by angular docs
Quoting:
required remains, not for validation purposes (we'll cover that in the code), but rather for css styling and accessibility.
What I want is to cycle through each form field and add a * to the associated label if the field is required.
So, First Name reads First Name *; and so on.
How would I go about doing that. Thanks.
#Directive({
selector: '[required]'
})
export class LabelRequiredDirective {
constructor(private elRef:ElementRef){}
ngAfterContentInit() {
this.elRef.nativeElement.labels.forEach(l => l.textContent += ' *');
}
}
Because the selector matches every element that has the required attribute, it is applied to all elements where the label should be updated.
Sadly nativeElement.labels is only supported in Chrome. For other browsers another strategy is necessary to get the label associated with an input element (See also Find html label associated with a given input)

How to force Angular 2 to re-check validator?

I have a simple login form written in Angular 2 reactive (data-driven) template. It's working perfectly but when I refresh the page and browser fills e-mail+password (autocomplete), my form's valid property seems false.
But when I press any key or click anywhere in my page while form is invalid, angular updates some states (I guess) and my form become valid.
How can I trigger that state? How can I say angular "Hey, check my form again."? I can't trigger my own validation script because some validation messages are alerts. If I do this, when user open this page, he/she will see these alerts.
I remember, I use trigger('input') trick to update ng-model.
updateValueAndValidity() is what you are looking for. The document is here: AbstractControl. It can force recalculate the value and validation status of the control.
Here's a demo:
form.component.ts
export class FormComponent implements OnInit {
myform: FormGroup;
// explicit validation of each field
emailValid: AbstractControl;
passwordValid: AbstractControl;
constructor(private fb: FormBuilder) {
this.myform = fb.group({
'email': ['', Validators.required],
'password': ['', Validators.required],
});
this.emailValid = this.myform.controls['email'];
this.passwordValid = this.myform.controls['password'];
}
ngOnInit() {
this.myform.get('email').valueChanges.subscribe(()=>forceValidAgain());
this.myform.get('password').valueChanges.subscribe(()=>forceValidAgain());
}
forceValidAgain(){
this.emailValid.updateValueAndValidity();
this.passwordValid.updateValueAndValidity();
}
}
form.component.html
<form [formGroup]="myform" (ngSubmit)="onSubmit(myform.value)">
<div class="form-group">
<label for="email">Email</label>
<input type="email"
class="form-control"
id="email"
name="email"
[formControl]="myform.controls['email']"
[ngClass]="{'is-invalid': !emailValid.valid && emailValid.touched }">
<div
class="invalid-feedback"
*ngIf="emailValid.hasError('required')">
This field is required
</div>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password"
class="form-control"
id="password"
name="password"
[formControl]="myform.controls['password']"
[ngClass]="{'is-invalid': !passwordValid.valid && passwordValid.touched}">
<div
class="invalid-feedback"
*ngIf="passwordValid.hasError('required')">
This field is required
</div>
</div>
</form>
I would suggest creating a method like onValueChanged (that is mentioned in Angular2 docs), bind it to your form, and execute it while the component is initialized. So the binding to the form changes should be done in this way:
this.form.valueChanges.subscribe((data) => {
this.onValueChanged(data)
});
And execution for example just the line after like this:
this.onValueChanged();
This execution should solve your problem via validation during component initialization.
I think that the method implementation from the docs (below) is pretty clear.
onValueChanged(data?: any) {
if (!this.heroForm) { return; }
const form = this.heroForm;
for (const field in this.formErrors) {
// clear previous error message (if any)
this.formErrors[field] = '';
const control = form.get(field);
if (control && control.dirty && !control.valid) {
const messages = this.validationMessages[field];
for (const key in control.errors) {
this.formErrors[field] += messages[key] + ' ';
}
}
}
}
Docs I am referring to: https://angular.io/docs/ts/latest/cookbook/form-validation.html

Angular2 Forms - ngControl

I'm trying to use ngControl to apply error classes based on user's input.
Somehow, I can't make it to work. I see that appropriate classes are set (line ng-invalid), but when trying to use name.valid (where name is my ngControl) it doesn't work.
html:
<div ngClass="{alert: name.invalid}">
<label for="name">Name</label>
<input ngControl="name" #name id="name" [(ngModel)]="user.name"/>
</div>
</div>
js
export class App {
userForm: any;
user: any;
constructor(
private _formBuilder: FormBuilder) {
this.user = {name: 'Ben'};
this.userForm = this._formBuilder.group({
'name': ['', Validators.required]
});
}
}
I saw on angular.io examples that they do use it like this (just for other cases, like show/hide divs)?
Here's the simple plunker: http://plnkr.co/edit/BKx4yplIOu44tk7Mfolc?p=preview
When input field is empty, I would expect that upper div gets alert class, but that doesn't happen.
In fact there are three things to change in your template:
ngClass should be [ngClass]. Otherwise the value is considered as a string and not as an expression.
#name should be #name="ngForm". Otherwise you reference the DOM element and not the control.
there is no invalid property on controls in Angular2 but only a valid one.
Here is the refactored code:
<div [ngClass]="{alert: !name.valid}">
<label for="name">Name</label>
<input ngControl="name" #name="ngForm"
required id="name" [(ngModel)]="user.name"/>
</div>
Here is the plunkr: http://plnkr.co/edit/OJfb9VDqlrRH4oHXQJyg?p=preview.
Note that you can't leverage of FormBuilder with ngControl since the latter allows you to define inline form. With FormBuilder you must use ngFormControl instead.
Here is a sample:
<div [ngClass]="{alert: !userForm.controls.name.valid}">
<label for="name">Name</label>
<input [ngFormControl]="userForm.controls.name"
id="name" [(ngModel)]="user.name"/>
</div>
See this article for more details:
http://restlet.com/blog/2016/02/11/implementing-angular2-forms-beyond-basics-part-1/

Angular 2 form validation, hasError is not a function

i try to make a validation for my input fields.
this is a piece of code that I used:
DepartmentComponent
import {
FORM_DIRECTIVES,
FormBuilder,
ControlGroup,
Validators ,
AbstractControl
} from 'angular2/common';
#Component({
selector: 'my-departments',
providers: [HTTP_PROVIDERS, DepartmentService],
directives: [FORM_DIRECTIVES, Alert],
styleUrls: ['app/department.component.css'],
templateUrl: 'app/department.component.html',
pipes:[SearchPipe]
})
export class DepartmentComponent implements OnInit {
myForm: ControlGroup;
departmentName: AbstractControl;
departmentLocation: AbstractControl;
constructor(private _router: Router, private _departmentService: DepartmentService, fb: FormBuilder) {
this.myForm = fb.group({
'departmentName': ['', Validators.required],
'departmentLocation': ['', Validators.required]
});
this.departmentName= this.myForm.controls['departmentName'];
this.departmentLocation= this.myForm.controls['departmentLocation'];
}
DepartmentComponent template
<form [ngFormModel]="myForm"
(ngSubmit)="addDepartment(newItem)" [hidden]="!showAddView" align="center">
<div>
<label for="editAbrv">Department name:</label><br>
<input type="text" [(ngModel)]="newItem.departmentName" [ngFormControl]="myForm.controls['departmentName']" >
<div *ngIf="departmentName.hasError('required')" class="ui error message"><b style="color:red;">Name is required</b></div>
</div>
<div>
<label for="editAbrv">Department Location:</label><br>
<input type="text" [(ngModel)]="newItem.departmentLocation" [ngFormControl]="myForm.controls['departmentLocation']" >
<div *ngIf="departmentLocation.hasError('required')" class="ui error message"><b style="color:red;">Location is required</b></div>
</div>
<div>
<button type="submit" class="ui button">Submit</button>
<button><a href="javascript:void(0);" (click)="showHide($event)" >
Cancel
</a></button>
</div>
</form>
The problem is that I got an error: .hasError is not a function. hasError function is in my html file (which you can see) I really don't see where I'm wrong. I did everything like is described in tutorial, but can't figure out why is this happen. Thanks for advice!
you should use *ngIf="myForm.controls['departmentLocation'].hasError('required')"
or any better luck with
this.departmentName= this.myForm.controls.find('departmentName'); ?
In place of hasError you should use errors
i.e it should be
myForm.controls['departmentLocation'].errors['required']
i.e with *ngIf
*ngIf="myForm.controls['departmentLocation'].errors['required']"
This is similar to another answer I've provided here: Form Builder with hasError() for validation throws an error of ERROR TypeError: Cannot read property 'hasError' of undefined.
The gist is that TypeScript getter's can be used to solve this in a clean way.
In your component class:
get departmentLocation() {
return this.myForm.get( 'departmentLocation' );
}
In your component template:
<input type="text" formControlName="departmentLocation">
<p class="ui error message" *ngIf="departmentLocation.hasError('required')">Department location is required</p>
Also important to note that using ngModel with Reactive Forms in Angular 6+ is depracated, so it's been removed from the examples above.
i was getting compilation error for using
loginForm.get('email').hasError('required')
This fixed it
loginForm.get('email')?.hasError('required')
use it like this
<mat-error *ngIf="!loginForm.get('password')?.hasError('required')
&& loginForm.get('password')?.hasError('whitespace')">