Reputation: 585
I'm trying to use angular material 2 datepicker with angular forms and I'm getting this error:
vendor.bundle.js:92803 ERROR TypeError: Cannot read property 'invalid' of undefined
at MdInput.defaultErrorStateMatcher [as errorStateMatcher] (vendor.bundle.js:121344)
at MdInput.webpackJsonp.../../../material/@angular/material.es5.js.MdInput._updateErrorState (vendor.bundle.js:131778)
at MdInput.webpackJsonp.../../../material/@angular/material.es5.js.MdInput.ngDoCheck (vendor.bundle.js:131739)
at checkAndUpdateDirectiveInline (vendor.bundle.js:102634)
at checkAndUpdateNodeInline (vendor.bundle.js:104132)
at checkAndUpdateNode (vendor.bundle.js:104071)
at prodCheckAndUpdateNode (vendor.bundle.js:104765)
at Object.View_FormComponent_0._co [as updateDirectives] (ng:///AppModule/FormComponent.ngfactory.js:2337)
at Object.updateDirectives (vendor.bundle.js:104522)
at checkAndUpdateView (vendor.bundle.js:104038)
Below is the code form my comonent :
import {Component, OnInit, Output, Input, EventEmitter} from "@angular/core";
import {FormControl, Validators, FormGroup} from "@angular/forms";
import {ReferentialService} from "../shared/referential.service";
@Component({
selector: 'app-form',
templateUrl: './form.component.html',
styleUrls: ['./form.component.css']
})
export class FormComponent implements OnInit {
private form: FormGroup;
@Input() model: any;
isValidationMode: boolean;
@Output('cancel') cancel$: EventEmitter<any>;
@Output('submit') submit$: EventEmitter<any>;
// select lists
languages: any;
types: any;
subtypes: any;
entities: any;
managements: any;
countries: any;
services: any;
constructor(private _referentialService: ReferentialService) {
this.submit$ = new EventEmitter();
this.cancel$ = new EventEmitter();
this.model = {};
this.form = this._buildForm();
}
ngOnInit() {
this.initSelectLists();
}
/**
* Function to handle component update
*
* @param record
*/
ngOnChanges(record) {
if (record.model && record.model.currentValue) {
this.model = record.model.currentValue;
this.isValidationMode = !!this.model;
this.form.patchValue(this.model);
}
}
/**
* Function to init select list
*/
initSelectLists() {
this._referentialService.getCountries().subscribe((countries: any[]) => this.countries = countries);
this._referentialService.getLanguages().subscribe((languages: any[]) => this.languages = languages);
this._referentialService.getTypes().subscribe((types: any[]) => this.types = types);
this._referentialService.getSubTypes().subscribe((subtypes: any[]) => this.subtypes = subtypes);
this._referentialService.getEntities().subscribe((entities: any[]) => this.entities = entities);
this._referentialService.getServices().subscribe((services: any[]) => this.services = services);
this._referentialService.getManagements().subscribe((managements: any[]) => this.managements = managements);
}
/**
* Function to emit event to cancel process
*/
cancel() {
this.cancel$.emit();
}
/**
* Function to emit event to submit form and person
*/
submit(doc: any) {
this.submit$.emit(doc);
}
/**
* Function to build our form
*
* @returns {FormGroup}
*
* @private
*/
private _buildForm(): FormGroup {
return new FormGroup({
file: new FormControl('', Validators.compose([])),
documentid: new FormControl('', Validators.compose([Validators.required])),
title: new FormControl('', Validators.compose([Validators.required])),
name: new FormControl('', Validators.compose([Validators.required])),
format: new FormControl('', Validators.compose([Validators.required])),
summary: new FormControl('', Validators.compose([Validators.required])),
mnesysid: new FormControl('', Validators.compose([Validators.required])),
entity: new FormControl('', Validators.compose([Validators.required])),
management: new FormControl('', Validators.compose([])),
service: new FormControl('', Validators.compose([])),
creationDate: new FormControl(null, Validators.compose([])),
sendDate: new FormControl({
value: this.model.creationDate
}, Validators.compose([Validators.required])),
type: new FormControl('', Validators.compose([Validators.required])),
subtype: new FormControl('', Validators.compose([])),
archivist: new FormControl({
value: this.model.archivist,
disabled: true
}, Validators.compose([Validators.required])),
firstname: new FormControl({value: this.model.name, disabled: true}, Validators.compose([Validators.required])),
lastname: new FormControl({
value: this.model.firstname,
disabled: true
}, Validators.compose([Validators.required])),
beneficiary: new FormControl('', Validators.compose([])),
depositid: new FormControl('', Validators.compose([])),
filepath: new FormControl('', Validators.compose([])),
isprivate: new FormControl('', Validators.compose([])),
language: new FormControl('', Validators.compose([Validators.required])),
country: new FormControl('', Validators.compose([])),
publicyear: new FormControl(null, Validators.compose([])),
version: new FormControl('', Validators.compose([])),
status: new FormControl('', Validators.compose([])),
comment: new FormControl('', Validators.compose([Validators.required]))
});
}
}
And this is my html code
<md-card>
<md-card-title>
<span *ngIf="isValidationMode" style="color: #00965E !important;">Validation du document</span>
<span *ngIf="!isValidationMode" style="color: #00965E !important;">Soumission de document</span>
</md-card-title>
<md-card-content>
<form novalidate [formGroup]="form">
<h5 style="margin-bottom: -5px">Personne déposant le fichier</h5>
<table>
<tr>
<td>
<p [class.errors]="form.controls.archivist.touched && form.controls.archivist.invalid">
<md-input-container>
<input mdInput placeholder="Identifiant" formControlName="archivist">
<span *ngIf="form.controls.archivist.touched && form.controls.archivist.errors?.required">
<br>L'identifiant de l'archivist est obligatoire</span>
</md-input-container>
</p>
</td>
<td>
<p [class.errors]="form.controls.lastname.touched && form.controls.lastname.invalid">
<md-input-container>
<input mdInput placeholder="Nom" formControlName="lastname">
<span *ngIf="form.controls.lastname.touched && form.controls.lastname.errors?.required">
<br>le nom est obligatoire</span>
</md-input-container>
</p>
</td>
<td>
<p [class.errors]="form.controls.firstname.touched && form.controls.firstname.invalid">
<md-input-container>
<input mdInput placeholder="Prenom" formControlName="firstname">
<span *ngIf="form.controls.firstname.touched && form.controls.firstname.errors?.required">
<br>le prénom est obligatoire</span>
</md-input-container>
</p>
</td>
<td>
<md-input-container>
<input mdInput formControlName="beneficiary" placeholder="Pour le compte de">
</md-input-container>
</td>
</tr>
</table>
<h5 class="sub-title">Identification du document</h5>
<table>
</table>
<table>
<tr>
<td>
<input type="file" (change)="fileChange($event)" formControlName="file" placeholder="Upload file"
accept=".pdf,.doc,.docx">
</td>
<td>
<p [class.errors]="form.controls.title.touched && form.controls.title.invalid">
<md-input-container>
<input mdInput placeholder="Titre" formControlName="title">
<span *ngIf="form.controls.title.touched && form.controls.title.errors?.required">
<br>le titre est obligatoire</span>
</md-input-container>
</p>
</td>
<td>
<p [class.errors]="form.controls.format.touched && form.controls.format.invalid">
<md-input-container>
<input mdInput placeholder="Format" formControlName="format">
<span *ngIf="form.controls.format.touched && form.controls.format.errors?.required">
<br>le format est obligatoire</span>
</md-input-container>
</p>
</td>
<td>
<p [class.errors]="form.controls.documentid.touched && form.controls.documentid.invalid">
<md-input-container>
<input mdInput placeholder="Identifiant" formControlName="documentid">
<span *ngIf="form.controls.documentid.touched && form.controls.documentid.errors?.required">
<br>l'identifiant est obligatoire</span>
</md-input-container>
</p>
</td>
<td>
<p [class.errors]="form.controls.mnesysid.touched && form.controls.mnesysid.invalid">
<md-input-container>
<input mdInput placeholder="Cote" formControlName="mnesysid">
<span *ngIf="form.controls.mnesysid.touched && form.controls.mnesysid.errors?.required">
<br>la cote est obligatoire</span>
</md-input-container>
</p>
</td>
</tr>
</table>
<h5 class="sub-title">Description du document</h5>
<table>
<tr>
<td>
<p [class.errors]="form.controls.name.touched && form.controls.name.invalid">
<md-input-container>
<input mdInput placeholder="Nom" formControlName="name">
<span *ngIf="form.controls.name.touched && form.controls.name.errors?.required">
<br>Le nom est obligatoire</span>
</md-input-container>
</p>
</td>
<td>
<md-input-container><input mdInput placeholder="Version" formControlName="version"></md-input-container>
</td>
<td>
<p [class.errors]="form.controls.language.touched && form.controls.language.invalid">
<md-select placeholder="Langue" formControlName="language">
<md-option>None</md-option>
<md-option *ngFor="let language of languages" [value]="language.languageCode">{{ language.frenchLabel
}}
</md-option>
</md-select>
<span *ngIf="form.controls.language.touched && form.controls.language.errors?.required">
<br><br>La langue est obligatoire</span>
</p>
</td>
<td>
<md-select placeholder="Pays" formControlName="country">
<md-option>None</md-option>
<md-option *ngFor="let country of countries" [value]="country.countryCode">{{ country.frenchLabel }}
</md-option>
</md-select>
</td>
</tr>
</table>
<table>
<tr>
<td>
<p [class.errors]="form.controls.summary.touched && form.controls.summary.invalid">
<md-input-container>
<textarea mdInput placeholder="Résumé" formControlName="summary"
class="text-area"></textarea>
<span *ngIf="form.controls.summary.touched && form.controls.summary.errors?.required">
<br>Le résumé est obligatoire</span>
</md-input-container>
</p>
</td>
</tr>
</table>
<table>
<tr>
<td>
<p [class.errors]="form.controls.type.touched && form.controls.type.invalid">
<md-select placeholder="Type" formControlName="type">
<md-option>None</md-option>
<md-option *ngFor="let type of types" [value]="type.typeCode">{{ type.frenchLabel }}</md-option>
</md-select>
<span *ngIf="form.controls.type.touched && form.controls.type.errors?.required">
<br><br>Le type est obligatoire</span>
</p>
</td>
<td>
<md-select placeholder="Sous type" formControlName="subtype">
<md-option>None</md-option>
<md-option *ngFor="let subtype of subtypes" [value]="subtype.subtypeCode">{{ subtype.frenchLabel }}
</md-option>
</md-select>
</td>
<td>
<p [class.errors]="form.controls.entity.touched && form.controls.entity.invalid">
<md-select placeholder="Entité" formControlName="entity">
<md-option>None</md-option>
<md-option *ngFor="let entity of entities" [value]="entity.entityCode">{{ entity.frenchLabel }}
</md-option>
</md-select>
<span *ngIf="form.controls.entity.touched && form.controls.entity.errors?.required">
<br><br>L'entité est obligatoire</span>
</p>
</td>
<td>
<md-select placeholder="Direction" formControlName="management">
<md-option>None</md-option>
<md-option *ngFor="let management of managements" [value]="management.managementCode">{{
management.frenchLabel }}
</md-option>
</md-select>
</td>
<td>
<md-select placeholder="Métier/Service" formControlName="service">
<md-option>None</md-option>
<md-option *ngFor="let service of services" [value]="service.serviceCode">{{ service.frenchLabel }}
</md-option>
</md-select>
</td>
</tr>
</table>
<h5 style="margin-top: 5px;margin-bottom: -5px">Création du document</h5>
<table>
<tr>
<td style="padding-right: 40px;">
<md-radio-group formControlName="isprivate" required>
<md-radio-button value="false" color="primary">Public</md-radio-button>
<md-radio-button value="true" color="primary" [checked]="isprivate">Privé</md-radio-button>
</md-radio-group>
</td>
<td>
<md-form-field>
<input mdInput [mdDatepicker]="creationDatePicker" placeholder="Date de création"
formControlName="creationDate">
<md-datepicker-toggle mdSuffix [for]="creationDatePicker"></md-datepicker-toggle>
<md-datepicker #creationDatePicker></md-datepicker>
</md-form-field>
</td>
<!--<td>-->
<!--<md-form-field>-->
<!--<input mdInput [mdDatepicker]="sendDatePicker" placeholder="Date d'envoi" formControlName="sendDate">-->
<!--<md-datepicker-toggle mdSuffix [for]="sendDatePicker"></md-datepicker-toggle>-->
<!--<md-datepicker #sendDatePicker></md-datepicker>-->
<!--</md-form-field>-->
<!--</td>-->
<!--<td>-->
<!--<md-form-field>-->
<!--<input mdInput [mdDatepicker]="publicationDatePicker" placeholder="Date de passage en public"-->
<!--formControlName="publicyear">-->
<!--<md-datepicker-toggle mdSuffix [for]="publicationDatePicker"></md-datepicker-toggle>-->
<!--<md-datepicker #publicationDatePicker></md-datepicker>-->
<!--</md-form-field>-->
<!--</td>-->
</tr>
</table>
<table>
<tr>
<td>
<p *ngIf="isValidationMode" [class.errors]="form.controls.comment.touched && form.controls.comment.invalid">
<md-input-container>
<textarea mdInput placeholder="Commentaire" formControlName="comment"
class="text-area"></textarea>
<span *ngIf="form.controls.summary.touched && form.controls.comment.errors?.required">
<br>Le commentaire est obligatoire</span>
</md-input-container>
</p>
</td>
</tr>
</table>
</form>
</md-card-content>
<md-card-actions align="end">
<div *ngIf="isValidationMode">
<button md-button align [hidden]="!isValidationMode">Refuser</button>
</div>
<div>
<button md-button type="button" (click)="cancel()">Annuler</button>
</div>
<div>
<button md-button color="primary" type="submit" (click)="submit(form.value)" >
<span *ngIf="!isValidationMode">Soumettre</span>
<span *ngIf="isValidationMode">Valider</span>
</button>
</div>
</md-card-actions>
</md-card>
The date that i'm trying to bind from my model has this format "2017-06-20T12:00:00.000Z"
Could you please help me ? is there a need to put on place a dateAdapter ?
Upvotes: 0
Views: 1503
Reputation: 558
Your error:
TypeError: Cannot read property 'invalid' of undefined
is actually caused by this exception:
Datepicker: value not recognized as a date object by DateAdapter.
This means that the date format "2017-06-20T12:00:00.000Z"
you're using in your model is not a valid Date
instance. See this line in the source code.
So you should use a Date
instance like so:
return new FormGroup({
creationDate: new FormControl(new Date(2017, 6, 20)),
})
or in your model:
model.creationDate = new Date(2017, 6, 20);
Also, as mentioned in the documentations:
Please note: MdNativeDateModule is based off of the functionality available in JavaScript's native Date object, and is thus not suitable for many locales. One of the biggest shortcomings of the native Date object is the inability to set the parse format. We highly recommend using a custom DateAdapter that works with the formatting/parsing library of your choice.
You should not rely on the NativeDateAdapter
since it's using the native JavaScript Date
object, which is very limited when it comes to parsing complexe date formats. For that, you could for instance use the awesome Moment.js library.
Upvotes: 6