14

I am trying to build a datetime picker directive like the following.
<input [(ngModel)]="date1" datetime-picker date-only />

and date1 is assigned as a Date, e.g., new Date()

When I display this in html, text in input element looks like the following
Thu Jan 01 2015 00:00:00 GMT-0500

I want to display like the following instead
2015-01-01 00:00:00

I want to format date WITHIN a directive using DatePipe instead of showing result from default toString() function.

My question is; "within a directive, how do I access ngModel variable?", e.g., date1, so that I can add toString() method.

If my approach is not right, please advise me.

allenhwkim
  • 25,529
  • 15
  • 80
  • 114

3 Answers3

18

Here is simple and easy way to listen and notify ngModel. I have just demonstrated with jQuery for easy understanding. Practically, it can be anything.

import { Directive, ElementRef } from '@angular/core';
import { NgModel } from '@angular/forms';

@Directive({
    selector: `[ngModel][customDir]`,
    providers: [NgModel]
})
export class CustomDirective {


    constructor(private element: ElementRef, private ngModel: NgModel) {

    }

    ngOnInit() {
        let that = this;
        /* Here you can write custom initialization code */

        /* Listening to the value of ngModel */
        that.ngModel.valueChanges.subscribe(function (value) {
            /* Set any value of your custom control */
            $(that.element.nativeElement).data("newValue",value);
        });

        /* Inform ng model for any new change happened */
        $(that.element.nativeElement).bind("customEvent",function (someNewValue) {
                that.ngModel.update.emit(someNewValue);
            }
        });
    }
}
Dhrumil Bhankhar
  • 1,154
  • 1
  • 13
  • 14
  • @Dhrumil Bhankar, why $(that.element.nativeElement).bind("customEvent",function (someNewValue) { that.ngModel.update.emit(someNewValue); } }); isn't fired in my code –  Apr 10 '17 at 07:21
  • @gentos Please note that this is generic example. Not actual code. Here "customElement" can be any event. If you want model to update on click you should use "click" instead of that "customEvent" word. – Dhrumil Bhankhar Jun 16 '17 at 06:03
  • 4
    For this example to work with Angular Ivy (> v9), the line `providers: [NgModel]` should be removed – Javier Marín Feb 12 '20 at 09:57
14

For accessing ngModel, you can simply just have an @Input() in your datetime-picker. And since you are using 2-way data binding, you have to emit the changes you make to ngModel.

@Directive({
  selector:'[date-time-picker]'
})
export class DateTimePicker{
  @Input() ngModel;
  @Output() ngModelChange = new EventEmitter();

  ngOnInit(){
    this.ngModelChange.emit(this.ngModel.toDateString());
  }
}

Check this plunk


The better way IMHO, is using the DatePipe

@Component({
  selector: 'my-app',
  directives:[DateTimePicker],
  template: `
      <input (ngModelChange)="myDate = $event" [ngModel]="myDate | date:'short'" datetime-picker  />
  `
})
export class AppComponent {
  myDate = new Date();
}

Check this plunk

Abdulrahman Alsoghayer
  • 15,570
  • 7
  • 48
  • 55
  • This solution is good to display date in format. However, it does not change a method, `toString`, of a ngModel, which actually does the formatting of a date. It actually change an ngModel from `Date` to `string`, which I do not intend to do. @Abdulrahman, do we need to emit a new ngModel with toString modified? – allenhwkim May 23 '16 at 17:29
  • @allenhwkim I see , so you want to override the method `toString()` ? If so, you need to override the method of `Date` not `ngModel` – Abdulrahman Alsoghayer May 23 '16 at 17:35
  • No, I do not want to override `Date.toString`. I want to override `toString` method of a Date instance, which is a ngModel. – allenhwkim May 23 '16 at 17:38
  • `ngOnInit(): void { // emit toString Modified(date formatted) instance let newNgModel = new Date(this.ngModel.toString()); newNgModel.toString = () => { return new DatePipe().transform(this.ngModel, 'yyyy-MM-dd'); }; this.ngModelChange.emit(newNgModel); }` Although this works, I am not sure it has its own cons. – allenhwkim May 23 '16 at 17:50
  • Alright, I think I got it :D. For that, you need a custom value accessor [check this post](http://www.bennadel.com/blog/3092-creating-an-abstract-value-accessor-for-ngmodel-in-angular-2-beta-17.htm). In particular, the method `writeValue`. Sorry I am writing from my phone. – Abdulrahman Alsoghayer May 23 '16 at 17:51
3

You can use "keyup" and "this.ngModel.model"

import { Directive, HostListener } from '@angular/core';
import { NgModel } from '@angular/forms';

@Directive({
  selector: '[ngModel][customDir]',
  providers: [NgModel]
})
export class CustomDirective {

  constructor(private ngModel: NgModel) { }

  @HostListener('keyup', ['$event']) onKeyUp(event) {
    let value = this.ngModel.model;
    this.ngModel.update.emit(value);
  }
}