How to Extract just the Date from Angular Material Datepicker - html

I have a reactive form and inside it I want to use the angular material datepicker. I've extracted the value from the datepicker using (dateChange) but the value it self is of the type object and included much more than what I need. Sun Aug 11 2019 00:00:00 GMT-0400 (Eastern Daylight Time)
Is there away to get just the Month, Day, and the Year Aug 11 2019 without all the rest?
HTML
<mat-form-field>
<input matInput [matDatepicker]="dp" placeholder="Choose a date (dateChange)="updateDOB($event)" disabled>
<mat-datepicker-toggle matSuffix [for]="dp"></mat-datepicker-toggle>
<mat-datepicker touchUi #dp disabled="false"></mat-datepicker>
</mat-form-field>
TS (pretty simple at the moment)
updateDOB(dateObject) {
console.log(dateObject.value);
}
I've been reading a bunch of documentation but still can't figure it out. Any help is greatly appreciated.

-Update-
I was able to come up with a work around for the problem. I stringified the object value and then trimmed the string to just included the date.
updateDOB(dateObject) {
// convert object to string then trim it to yyyy-mm-dd
const stringified = JSON.stringify(dateObject.value);
const dob = stringified.substring(1, 11);
this.applicant.contact[0].dob = dob;
}
The result now comes out to 2019-08-11 which works for me.
If anyone has any other suggestions I'd still be happy to hear them.

Try this,
updateDoB(dateObject){
console.log("DATE in dd/mm/yyyy",dateObject.toLocaleDateString())
}

another way to do it:
dateObject.value.toLocaleString().substring(1, 10)

I had a very similar problem. I solved it using MomentDateAdapter and custom directive. Steps i took:
install #angular/material-moment-adapter and moment.js
add date format configuration :
export const DATE_FORMATS = {
parse: {
dateInput: ['MMM DD yyyy']
},
display: {
dateInput: 'MMM DD yyyy',
monthYearLabel: 'MMM yyyy',
dateA11yLabel: 'LL',
monthYearA11yLabel: 'MMMM yyyy',
},
};
First 'dateInput' matters how You should enter date (using keyboard for example) to be successfully parsed. Second 'dateInput' matters what format is used to dipslay date.
Define providers:
{ provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE, MAT_MOMENT_DATE_ADAPTER_OPTIONS]},
{ provide: MAT_DATE_FORMATS, useValue: DATE_FORMATS }
Create custom directive:
import {
Directive,
EventEmitter,
HostListener,
Input,
Output,
} from '#angular/core';
#Directive({
selector: '[setOutputDateFormat]',
})
export class DateFormatDirective {
#Input('setOutputDateFormat')
inputFormat = null;
#Output()
dateChangeWithFormat = new EventEmitter<string>();
constructor() {}
#HostListener('dateChange', ['$event'])
onDateChange(event: any) {
const dateFormatted = event.value.format(this.inputFormat);
this.dateChangeWithFormat.emit(dateFormatted);
}
}
Directive listens to 'dateChange' events, event.value attribute should contain date in moment object (as we switched to MomentDateAdapter at the beginning), so directive switches format to the desired ones and fires custom event with the desired format
Lastly use the directive inside template providing custom format in 'outputDateFormat' property:
<mat-form-field>
<input
matInput
[setOutputDateFormat]="outputDateFormat"
[matDatepicker]="dp"
placeholder="Choose a date"
(dateChangeWithFormat)="updateDOB($event)"
/>
<mat-datepicker-toggle matSuffix [for]="dp"></mat-datepicker-toggle>
<mat-datepicker touchUi #dp disabled="false"></mat-datepicker>
</mat-form-field>
Example here: https://stackblitz.com/edit/angular-ivy-beufcw?file=src/app/app.component.ts
Now it seems that moment.js project has a legacy status, so better solution would be switch to luxon date adapter (it shouldn't be hard because luxon is moment.js successor)

Related

Angular Interpolation Binding does not show date if format is not "yyyy-MM-dd"

My interpolation binding doesn't work if the given format is not 'yyyy-MM-dd'
.html:
<div class="form-group">
<label>DOB
<input type="date" class="form-control" id="birth_date" value={{stu.result.birth_date}}>
</label>
</div>
In this problem, the current format of the date is MM/dd/yyyy, so the interpolation binding doesn't work as expected and only shows plain MM/dd/yyyy (without the corresponding data as the value) on the input box. But if the format is yyyy-MM-dd, it works without problem (this worked because there are a few data have this format, and the others are on another format). Is there any solution I can use to fix this problem? Or, could anyone propose a new way to do POST request and the data sent is in yyyy-MM-dd format? Because every time I do a POST request, the data sent to the server is in MM/dd/yyyy format, not the wire format yyyy-MM-dd.
Here is the html used for sending the data:
<div class="form-group">
<label for="birth_date">DOB:</label>
<input type="date" class="form-control" id="birth_date" [(ngModel)]="birth_date" name="birth_date"
name="tanggal_lahir">
</div>
and this is the chunk ofTypeScript file used for sending the data:
this.http.post(this.urlApi, {
birth_date: this.birth_date
}, this.httpOptions).subscribe(.....);
Make date formats consistent across your application.
In your typescript code you can change date format using inbuilt angular DatePipe.
import { DatePipe } from '#angular/common';
constructor(public dateFormatPipe: DatePipe){
}
this.http.post(this.urlApi, {
birth_date: this.dateFormatPipe.transform(this.birth_date, 'yyyy-MM-dd')
}, this.httpOptions).subscribe(.....);
Now you also need to add DatePipe to providers in your module configuration.
import { DatePipe } from '#angular/common';
#NgModule({
declarations: [
],
entryComponents: [
],
imports: [
],
providers: [DatePipe],
bootstrap: [AppComponent]
})

How to format date in Angular Kendo Grid

I working on Angular Kendo Grid and I am getting server data in format
1900-01-01T00:00:00
But I want it to display in standard format, not sure how to do it. I have applied format='{0:MM/dd/yyyy h:mm a}' in grid column but no effect. What ever data format conversion to do, I need to do at client side of code i.e server date to javascript format!!
<kendo-grid-column field="mydata.openDate" width="220" title="Open Date" filter="date"
format='{0:MM/dd/yyyy h:mm a}'>
</kendo-grid-column>
Try this:
<kendo-grid-column field="dateField" width="220" title="Open Date">
<ng-template kendoGridCellTemplate let-dataItem>
{{dataItem.dateField | date: 'MM/dd/yyyy'}}
</ng-template>
</kendo-grid-column>
You can also use short or other formats provided by angular Date Pipe
if you have a big project and you have to use the same format multiple times then a directive is the way to go.
This is super important for the usability and in case you decided to change the format you use (Maintenance)
import { Directive, OnInit } from '#angular/core';
import { ColumnComponent } from '#progress/kendo-angular-grid';
#Directive({
selector: '[kendo-grid-column-date-format]'
})
export class KendoGridColumnDateFormatDirective implements OnInit {
constructor(private element: ColumnComponent) {
}
ngOnInit() {
this.element.format = "{0:dd.MM.yyyy}";
}
}
and you can use it like this
<kendo-grid-column field="yourField"
title="your title"
kendo-grid-column-date-format>
</kendo-grid-column>
Super important do not forget to register the directive
The Grid data needs to contain actual JavaScript Date objects as opposed to some string representations. Then built-in formatting, sorting, filtering and editing will treat the dates as such and will work as expected:
Docs
Map the data so that it contains actual dates.
EXAMPLES:
String
Date

Angular 5 FormGroup reset doesn't reset validators

I have a form on my page and when I call FormGroup.reset() it sets the forms class to ng-pristine ng-untouched but FormControl.hasError(...) still returns truthy. What am I doing wrong here?
Template
<form [formGroup]="myForm" (ngSubmit)="submitForm(myForm)">
<mat-form-field>
<input matInput formControlName="email" />
<mat-error *ngIf="email.hasError('required')">
Email is a required feild
</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput type="password" formControlName="password" />
<mat-error *ngIf="password.hasError('required')">
Password is a required feild
</mat-error>
</mat-form-field>
<button type="submit">Login</button>
</form>
Component
export class MyComponent {
private myForm: FormGroup;
private email: FormControl = new FormContorl('', Validators.required);
private password: FormControl = new FormControl('', Validators.required);
constructor(
private formBuilder: FormBuilder
) {
this.myForm = formBuilder.group({
email: this.email,
password: this.password
});
}
private submitForm(formData: any): void {
this.myForm.reset();
}
}
Plunker
https://embed.plnkr.co/Hlivn4/
It (FormGroup) behaves correctly. Your form requires username and password, thus when you reset the form it should be invalid (i.e. form with no username/password is not valid).
If I understand correctly, your issue here is why the red errors are not there at the first time you load the page (where the form is ALSO invalid) but pop up when you click the button. This issue is particularly prominent when you're using Material.
AFAIK, <mat-error> check the validity of FormGroupDirective, not FormGroup, and resetting FormGroup does not reset FormGroupDirective. It's a bit inconvenient, but to clear <mat-error> you would need to reset FormGroupDirective as well.
To do that, in your template, define a variable as such:
<form [formGroup]="myForm" #formDirective="ngForm"
(ngSubmit)="submitForm(myForm, formDirective)">
And in your component class, call formDirective.resetForm():
private submitForm(formData: any, formDirective: FormGroupDirective): void {
formDirective.resetForm();
this.myForm.reset();
}
GitHub issue: https://github.com/angular/material2/issues/4190
In addition to Harry Ninh's solution, if you'd like to access the formDirective in your component without having to select a form button, then:
Template:
<form
...
#formDirective="ngForm"
>
Component:
import { ViewChild, ... } from '#angular/core';
import { NgForm, ... } from '#angular/forms';
export class MyComponent {
...
#ViewChild('formDirective') private formDirective: NgForm;
constructor(... )
private someFunction(): void {
...
formDirective.resetForm();
}
}
After reading the comments this is the correct approach
// you can put this method in a module and reuse it as needed
resetForm(form: FormGroup) {
form.reset();
Object.keys(form.controls).forEach(key => {
form.get(key).setErrors(null) ;
});
}
There was no need to call form.clearValidators()
Add the property -
#ViewChild(FormGroupDirective) formGroupDirective: FormGroupDirective;
and use this instead of this.myForm.reset();
this.formGroupDirective.resetForm();
This will reset the error display and also do the job of form.reset(). But the form, along with the fields, will still show ng-invalid class
Check this answer for more details - https://stackoverflow.com/a/56518781/9262627
The below solution works for me when trying to reset specific form controller in form group -
this.myForm.get('formCtrlName').reset();
this.myForm.get('formCtrlName').setValidators([Validators.required, Validators.maxLength(45), Validators.minLength(4), Validators.pattern(environment.USER_NAME_REGEX)]);
this.myForm.get('formCtrlName').updateValueAndValidity();
form.reset() won't work on custom form control like Angular Material that's why the function is not working as expected.
My workaround for this is something like this
this.form.reset();
for (let control in this.form.controls) {
this.form.controls[control].setErrors(null);
}
this.form.reset() the issue with this is that it will reset your formcontrol values but not the errors so you need to reset them individually by this line of code
for (let control in this.form.controls) {
this.form.controls[control].setErrors(null);
}
With this you don't need to use FormGroupDirective which is a cleaner solution for me.
Github issue: https://github.com/angular/angular/issues/15741
I found that after calling resetForm() and reset(), submitted was not being reset and remained as true, causing error messages to display. This solution worked for me. I found it while looking for a solution to calling select() and focus() on an input tag, which also wasn't working as expected. Just wrap your lines in a setTimeout(). I think setTimeout is forcing Angular to detect changes, but I could be wrong. It's a bit of a hack, but does the trick.
<form [formGroup]="myFormGroup" #myForm="ngForm">
…
<button mat-raised-button (click)="submitForm()">
</form>
submitForm() {
…
setTimeout(() => {
this.myForm.resetForm();
this.myFormGroup.reset();
}, 0);
}
resetForm() {
this.myFormGroup.reset();
this.myFormGroup.controls.food.setErrors(null);
this.myFormGroup.updateValueAndValidity();
}
UPDATE FROM 2021 - ANGULAR 11.2
The fact to use a [formGroup]="form and a #formDirective="ngForm" directly into the HTML function is not a good practise. Or maybe you would prefer to use #ViewChild, and do it directly from your .ts. Actually, the problem don't come from Angular, but Material.
If you take a look at their GitHub, you will see this :
/** Provider that defines how form controls behave with regards to displaying error messages. */
#Injectable({providedIn: 'root'})
export class ErrorStateMatcher {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
return !!(control && control.invalid && (control.touched || (form && form.submitted)));
}
}
The form will keep its submitted state. So you just have to delete the last part of the function.
Here is my solution (tested and working). I have a Material Module, into I've implemented this :
export class ShowOnInvalidTouchedErrorStateMatcher implements ErrorStateMatcher {
isErrorState(control: FormControl): boolean {
return !!(control && control.invalid && control.touched);
}
}
#NgModule({
providers: [
{
provide: ErrorStateMatcher, useClass: ShowOnInvalidTouchedErrorStateMatcher
}
],
exports: [
MatSnackBarModule,
MatTabsModule,
...
]
});
If you want to use this ErrorStateMatcher on only one form, it's possible. Please see this Material example. This is the same principle.
I had no luck with resetting the form directive. But You can also change the input state to pending to do that as well.
this.myForm.get("email").reset();
this.myForm.get("password").reset();
To anyone whom this may help, I am running Angular 9.1.9 and I didn't want to reset the form/controls just the overall validity of the form so I just ran:
this.registerForm.setErrors(null);
...where registerForm: FormGroup and that reset the form errors, leading to:
this.registerForm.valid
...returning true.
The same can be done for controls:
this.registerForm.get('email').setErrors(null)
As soon as the form is touched, these errors are re-evaluated anyway so if that's not good enough, you may need to have a boolean flag to further pin-down exactly when you want to start showing/hiding error UI.
I did not need to touch the directive in my case.
I was also having the same set of problems. My problem was that i was using mat-form-field and formGroup. After resetting the form submitted flag was not resetting.
So, the solution that worked for me is, putting a directive of ngForm along with formGroup and passing onSubmit(form). Added
#ViewChild('form') form;
in component and then I used
this.form.resetForm();
Nothing from above worked for me (Angular 7.2, Angular Material 7.3.7).
Try to pass with submit method an event on view:
<form [formGroup]="group" (ngSubmit)="onSubmit($event)">
<!-- your form here -->
</form>
Then use it to reset currentTarget and your form afterwards:
public onSubmit(event): void {
// your code here
event.currentTarget.reset()
this.group.reset()
}
Simple fix: use button with type="reset" and function submitForm() together
<form [formGroup]="MyForm" (ngSubmit)="submitForm()">
<input formControlName="Name">
<mat-error>
<span *ngIf="!tunersForm.get('Name').value && tunersForm.get('Name').touched"></span>
</mat-error>
<button type="reset" [disabled]="!MyForm.valid" (click)="submitForm()">Save</button>
</form>

Angular 4 shows time part wrong in datetime

I have an input on my Angular component html like this:
<input id="orderdate" class="form-control" value="{{order.OrderDate | date: 'd.M.yyyy H:mm:ss'}}" disabled />
order.OrderDate has value 2017-06-01T10:52:03.666723, and I expect to see value in format
1.6.2017 10:52:03.
However, what I actually get is
1.6.2017 10:00:6/1/2017 10:52:03 AM:6/1/2017 10:52:03 AM
If I use only date (d.M.yyyy), it works correctly (1.6.2017). Why is the time part showing wrong?
I think its supposed to be in this format 'd.M.y H:mm:ss'.
Angular Date Pipe
Thanks to comment by Thiagz, I found solution to this problem here: Angular2 date pipe does not work in IE 11 and edge 13/14.
This seems to be bug in IE and Edge, so I had to create my own pipe for date.
DatexPipe.ts:
import { Pipe, PipeTransform } from '#angular/core';
import * as moment from 'moment';
#Pipe({
name: 'datex'
})
export class DatexPipe implements PipeTransform {
transform(value: any, format: string = ""): string {
var momentDate = moment(value);
if (!momentDate.isValid()) return value;
return momentDate.format(format);
}
}
Use it like this:
<input id="orderdate" class="form-control" value="{{order.OrderDate | datex: 'd.M.YYYY H:mm:ss'}}" disabled />

Angular 2 Date Input not binding to date value

trying to get a form set up but for some reason, the Date input in my html is not binding to the object's date value, despite using [(ngModel)]
html:
<input type='date' #myDate [(ngModel)]='demoUser.date'/><br>
form component:
export class FormComponent {
demoUser = new User(0, '', '', '', '', new Date(), '', 0, [], []);
}
User class:
export class User {
constructor (
public id: number,
public email: string,
public password: string,
public firstName: string,
public lastName: string,
public date: Date,
public gender: string,
public weight: number,
public dietRestrictions: string[],
public fitnessGoals: string[]
){
}
}
A test output reveals the current "new" Date as the object's value, but the input doesn't update the User object's date value or reflect the value, suggesting neither of the two-way bindings are working. Help would be greatly appreciated.
Angular 2 , 4 and 5 :
the simplest way : plunker
<input type="date" [ngModel] ="dt | date:'yyyy-MM-dd'" (ngModelChange)="dt = $event">
Instead of [(ngModel)] you can use:
// view
<input type="date" #myDate [value]="demoUser.date | date:'yyyy-MM-dd'" (input)="demoUser.date=parseDate($event.target.value)" />
// controller
parseDate(dateString: string): Date {
if (dateString) {
return new Date(dateString);
}
return null;
}
You can also choose not to use parseDate function. In this case the date will be saved as string format like "2016-10-06" instead of Date type (I haven't tried whether this has negative consequences when manipulating the data or saving to database for example).
In your component
let today: string;
ngOnInit() {
this.today = new Date().toISOString().split('T')[0];
}
and in your html file
<input name="date" [(ngModel)]="today" type="date" required>
In .ts :
today: Date;
constructor() {
this.today =new Date();
}
.html:
<input type="date"
[ngModel]="today | date:'yyyy-MM-dd'"
(ngModelChange)="today = $event"
name="dt"
class="form-control form-control-rounded" #searchDate
>
use DatePipe
> // ts file
import { DatePipe } from '#angular/common';
#Component({
....
providers:[DatePipe]
})
export class FormComponent {
constructor(private datePipe : DatePipe){}
demoUser = new User(0, '', '', '', '', this.datePipe.transform(new Date(), 'yyyy-MM-dd'), '', 0, [], []);
}
Angular 2 completely ignores type=date. If you change type to text you'll see that your input has two-way binding.
<input type='text' #myDate [(ngModel)]='demoUser.date'/><br>
Here is pretty bad advise with better one to follow:
My project originally used jQuery. So, I'm using jQuery datepicker for now, hoping that angular team will fix the original issue. Also it's a better replacement because it has cross-browser support. FYI, input=date doesn't work in Firefox.
Good advise: There are few pretty good Angular2 datepickers:
https://github.com/emirdeliz/meus-projetos/tree/master/angular2-schedule/app/frontend/components/datepicker
https://github.com/MrPardeep/Angular2-DatePicker
https://www.npmjs.com/package/ng2-datepicker
As per HTML5, you can use
input type="datetime-local"
instead of
input type="date".
It will allow the [(ngModel)] directive to read and write value from input control.
Note: If the date string contains 'Z' then to implement above solution, you need to trim the 'Z' character from date.
For more details, please go through this link on mozilla docs.
If you are using a modern browser there's a simple solution.
First, attach a template variable to the input.
<input type="date" #date />
Then pass the variable into your receiving method.
<button (click)="submit(date)"></button>
In your controller just accept the parameter as type HTMLInputElement
and use the method valueAsDate on the HTMLInputElement.
submit(date: HTMLInputElement){
console.log(date.valueAsDate);
}
You can then manipulate the date anyway you would a normal date.
You can also set the value of your <input [value]= "..."> as you
would normally.
Personally, as someone trying to stay true to the unidirectional data flow, i try to stay away from two way data binding in my components.
you can use a workaround, like this:
<input type='date' (keyup)="0" #myDate [(ngModel)]='demoUser.date'/><br>
on your component :
#Input public date: Date,
In Typescript - app.component.ts file
export class AppComponent implements OnInit {
currentDate = new Date();
}
In HTML Input field
<input id="form21_1" type="text" tabindex="28" title="DATE" [ngModel]="currentDate | date:'MM/dd/yyyy'" />
It will display the current date inside the input field.