angular2 access ngModel variable from a directive
Asked Answered
L

3

16

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.

Lefevre answered 23/5, 2016 at 2:40 Comment(1)
doesn't angular pass the model value to your transform function of your pipe?Thyself
A
20

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);
            }
        });
    }
}
Archenteron answered 21/10, 2016 at 14:59 Comment(3)
@Dhrumil Bhankar, why $(that.element.nativeElement).bind("customEvent",function (someNewValue) { that.ngModel.update.emit(someNewValue); } }); isn't fired in my codeKiddush
@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.Archenteron
For this example to work with Angular Ivy (> v9), the line providers: [NgModel] should be removedDebera
U
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

Undesigned answered 23/5, 2016 at 3:5 Comment(5)
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?Lefevre
@Lefevre I see , so you want to override the method toString() ? If so, you need to override the method of Date not ngModelUndesigned
No, I do not want to override Date.toString. I want to override toString method of a Date instance, which is a ngModel.Lefevre
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.Lefevre
Alright, I think I got it :D. For that, you need a custom value accessor check this post. In particular, the method writeValue. Sorry I am writing from my phone.Undesigned
N
4

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);
  }
}
Naturalist answered 12/1, 2019 at 12:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.