Reputation: 961
I am new to Angular2.
I am trying to implement JQuery ui datepicker in angular2.
I have written a datePicker directive, there i enabled datepicker. Here i am able to select date but finding difficulty to emit selected date to the parent component.
to overcome this i created an object on window object and passing component reference to that object. from there i am calling component function.
I feel this is not a best practice to do.
Can someone help me to do in right way.
import { Directive, ElementRef, Input, NgZone,HostListener,Output,EventEmitter } from '@angular/core';
declare var $:any;
@Directive({
selector: '[uiDatePicker]',
})
export class UiDatePickerDirective {
@Input('uiDatePicker') setDate: string;
@Output() onSelectDate = new EventEmitter();
private el: HTMLElement;
constructor(el: ElementRef,public zone:NgZone) {
this.el = el.nativeElement;
window.angularComponentRef = {
zone: this.zone,
component: this
};
}
doEmitDate(dateText:string){
this.onSelectDate.emit(dateText);
}
ngOnInit() {
$(this.el).datepicker({
onSelect: function(dateText:string) {
window.angularComponentRef.component.doEmitDate(dateText);
}
});
}
}
Here i dont like to use window.angularComponentRef.component object. As it is just storing the reference in global object. this is not good for an application.
Upvotes: 1
Views: 5097
Reputation: 13049
import { Directive, ElementRef, Input, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
declare var $: any;
export const CUSTOM_INPUT_DATE_PICKER_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => UiDatePickerDirective),
multi: true
};
@Directive({
selector: '[uiDatePicker]',
host: { '(blur)': 'onTouched($event)' },
providers: [CUSTOM_INPUT_DATE_PICKER_CONTROL_VALUE_ACCESSOR]
})
export class UiDatePickerDirective implements ControlValueAccessor {
private innerValue: string;
@Input('changeMonth') changeMonth: boolean = true;
@Input('changeYear') changeYear: boolean = true;
constructor(private el: ElementRef) {
}
ngOnInit() {
$(this.el.nativeElement).datepicker({
changeMonth: true,
changeYear: true,
dateFormat: 'dd/mm/yy'
}).on('change', (e: any) => {
this.onChange(e.target.value);
});
}
public onChange: any = (_:any) => { /*Empty*/ }
public onTouched: any = () => { /*Empty*/ }
get value(): any {
return this.innerValue;
};
//set accessor including call the onchange callback
set value(v: any) {
if (v !== this.innerValue) {
this.innerValue = v;
this.onChange(v);
}
}
writeValue(val: string): void {
this.innerValue = val;
$(this.el.nativeElement).datepicker("setDate", this.innerValue);
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
}
<input type="text" uiDatePicker [(ngModel)]="note.ValidTo"/>
This is a modified version from here.
Upvotes: 2
Reputation: 2588
For all intents and purposes, you can use the var self = this
ideology and closures to maintain a reference to the component.
If you aren't doing any pre-processing in the onSelect
callback, you can further simplify things by passing a bound doEmitDate
function as the onSelect
handler.
import { Directive, ElementRef, Input, NgZone,HostListener,Output,EventEmitter } from '@angular/core';
declare var $:any;
@Directive({
selector: '[uiDatePicker]',
})
export class UiDatePickerDirective {
@Input('uiDatePicker') setDate: string;
@Output() onSelectDate = new EventEmitter();
private el: HTMLElement;
constructor(el: ElementRef, public zone: NgZone) {
this.el = el.nativeElement;
}
doEmitDate(dateText:string){
this.onSelectDate.emit(dateText);
}
ngOnInit() {
const component = this;
$(this.el).datepicker({
onSelect: function(dateText: string) {
component.doEmitDate(dateText);
}
});
// Or if you're not doing anything else in the onSelect callback
$(this.el).datepicker({
onSelect: this.doEmitDate.bind(this)
});
}
}
Upvotes: 1