Select theme:
Checkout Radzen Demos and download latest Radzen!
Updated on October 26, 2017 to fix the plunkr demos and update them to Angular 4.
In this blog post I will show you how to use a jQuery plugin in an Angular 4 app. The plugin is the popular jQuery UI DatePicker.
Native date inputs are not widely available even though it is 2016. The HTML5 standard includes <input type="date">
and <input type="datetime-local">
but FireFox, desktop Safari and
Internet Explorer do not support them at the time of this writing. jQuery UI DatePicker comes to the rescue!
Before using a jQuery plugin we must include its dependencies in our page. We include jQuery, the JavaScript implementation and the CSS files that provide the visual theme.
<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.11.4/themes/ui-darkness/jquery-ui.css">
We are ready to implement the Angular component.
import { ViewChild, ElementRef, AfterViewInit, Component } from '@angular/core';
declare var jQuery: any;
@Component({
selector: 'my-datepicker',
template: `<input #input type="text">`
})
export class DatePickerComponent implements AfterViewInit {
@ViewChild('input') input: ElementRef;
ngAfterViewInit() {
jQuery(this.input.nativeElement).datepicker();
}
}
You can try it live in this plunkr demo.
Let's explain the code so far.
input
element because this is what the jQuery UI DatePicker initializes from.ViewChild
attribute maps an element from the template
to a property of the component.ElementRef
type is a DOM node wrapper. Angular wraps DOM nodes in order to support server-side
rendering or non-browser platforms such as NativeScript. On those platforms the DOM is not available.ngAfterViewInit
lifecycle method because at this point all ViewChild properties are ready to use.
Accessing the input
property earlier will cause an error.While the component itself works it is not very useful yet. There is no way to set its value or get notified when the user picks a date. Let's fix that.
import { Input, Output, EventEmitter, ViewChild, ElementRef, AfterViewInit, Component } from '@angular/core';
declare var jQuery: any;
@Component({
selector: 'my-datepicker',
template: `<input #input type="text">`
})
export class DatePickerComponent implements AfterViewInit {
@Input() value = '';
@Output() dateChange = new EventEmitter();
@ViewChild('input') input: ElementRef;
ngAfterViewInit() {
jQuery(this.input.nativeElement).datepicker({
onSelect: (value) => {
this.value = value;
this.dateChange.next(value);
}
})
.datepicker('setDate', this.value);
}
}
Our component has a value
property that allows us to set the current date from code. It also emits a dateChange
event when the user
selects a date.
Here how to use the new property and event (plunkr):
@Component({
selector: 'my-app',
template: `
<div>
<my-datepicker [value]="date" (dateChange)="onDateChange($event)"></my-datepicker>
<div>Date: {{ date }}</div>
</div>
`,
})
export class AppComponent {
date = "11/13/2016";
onDateChange(date) {
this.date = date;
}
}
Still something is off. Our component does not support forms. We should implement the ControlValueAccessor
interface to fix that.
import { forwardRef, Input, Output, EventEmitter, ViewChild, ElementRef, AfterViewInit, Component } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
const DATE_PICKER_VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DatePickerComponent),
multi: true
};
declare var jQuery: any;
@Component({
selector: 'my-datepicker',
template: `<input #input type="text">`,
providers: [DATE_PICKER_VALUE_ACCESSOR]
})
export class DatePickerComponent implements AfterViewInit, ControlValueAccessor {
private onTouched = () => { };
private onChange: (value: string) => void = () => { };
@Input() value = '';
writeValue(date: string) {
this.value = date;
jQuery(this.input.nativeElement).datepicker('setDate', date);
}
registerOnChange(fn: any) {
this.onChange = fn;
}
registerOnTouched(fn: any) {
this.onTouched = fn;
}
@Output() dateChange = new EventEmitter();
@ViewChild('input') input: ElementRef;
ngAfterViewInit() {
jQuery(this.input.nativeElement).datepicker({
onSelect: (value) => {
this.value = value;
this.onChange(value);
this.onTouched();
this.dateChange.next(value);
}
})
.datepicker('setDate', this.value);
}
}
We register a new provider to tell Angular that our DatePicker component implements the ControlValueAccessor
methods.
The DatePicker implements a few new methods: writeValue, registerOnChange and registerOnTouched.
writeValue
to sync a component with its value. In our case we set the jQuery UI DatePicker date.registerOnChange
method registers a callback which tells Angular that the component value changed.registerOnTouched
method registers a callback which tells Angular that the user interacted with the component.We invoke the onChange
and onTouched
callbacks whenever the user selects a new date.
Here is a sample form that uses the DatePicker component (plunkr).
@Component({
selector: 'my-app',
template: `
<form>
<my-datepicker name="date" [(ngModel)]="form.date"></my-datepicker>
<div>form: {{ form | json }}</div>
</form>
`,
})
export class AppComponentComponent {
form = {
date: "11/13/2016"
};
}
The jQuery UI DatePicker has a lot of configuration options. Let's support all of them by implementing a new Input
property.
@Component({
selector: 'my-datepicker',
template: `<input #input type="text">`
})
export class DatePickerComponent implements AfterViewInit, ControlValueAccessor, OnDestroy {
@Input() options: any = {};
/* snip */
ngAfterViewInit() {
jQuery(this.input.nativeElement).datepicker(Object.assign({}, this.options, {
onSelect: (value) => {
this.value = value;
this.onChange(value);
this.onTouched();
this.dateChange.next(value);
}
}))
.datepicker('setDate', this.value);
}
}
We created a new options
property and pass it to the jQuery UI DatePicker plugin. This allows us to pass configuration options
like this (plunkr):
<my-datepicker [options]="{numberOfMonths: 2}"></my-datepicker>
There is one remaining thing. We should cleanup the resources claimed by the jQuery UI DatePicker to avoid memory leaks. The
ngOnDestroy
method is the perfect place to do that.
@Component({
selector: 'my-datepicker',
template: `<input #input type="text">`
})
export class DatePickerComponent implements AfterViewInit, ControlValueAccessor, OnDestroy {
/* snip */
ngOnDestroy() {
jQuery(this.input.nativeElement).datepicker('destroy');
}
}
Our component is now ready to use. It supports validation, two-way data-binding, reactive forms and benefits from all the features provided by the jQuery UI DatePicker.
Until next time!
Radzen is free to use. You can also test the premium features for 15 days.
Download NowSelect theme: