Reputation: 7360
I'm trying to implement a date range selector in Angular 2.
I already have a working widget, which I have to link to the @angular/forms
subsystem.
What I would like is to be able to bind the two output values (let's say rangeStart
and rangeEnd
) to two distinct properties in the containing form's state.
Is there a way I can configure the NgModel settings to accomplish this?
An alternative could be to bind a single property of type DateRange:
type DateRange = {
from: Date,
to: Date
};
buyt I don't know if this is even possible.
Any suggestion on how to accomplish this?
Edit:
What I have is a jQuery-derived, JS component which exposes an onChange, like so:
component.on('change', (eventData) => {
// here I have eventData.from and eventData.to as Date values
});
I want to integrate this kind of handler in a Angular friendly component. But, I can't simply do this:
<my-date-range-picker name"xyz" [(NgModel)]="aDateRangeValue"></my-date-range-picker>
Because, AFAICT, change detection is not going to work with composite values.
What should I expose in my component? Two EventEmitter
s? Can I leverage NgModel
in some way?
Upvotes: 3
Views: 4888
Reputation: 7360
Well, as it turns out, you can have models of any kind.
So, I used the base classes from this article, here is the most relevant:
export class ValueAccessorBase<T> implements ControlValueAccessor {
private innerValue: T;
private changed = new Array<(value: T) => void>();
private touched = new Array<() => void>();
get value(): T {
return this.innerValue;
}
set value(value: T) {
if (this.innerValue !== value) {
this.innerValue = value;
this.changed.forEach(f => f(value));
}
}
touch() {
this.touched.forEach(f => f());
}
writeValue(value: T) {
this.innerValue = value;
}
registerOnChange(fn: (value: T) => void) {
this.changed.push(fn);
}
registerOnTouched(fn: () => void) {
this.touched.push(fn);
}
}
This happens to work even when T
is a class, with from
and to
properties, in my case:
@Component(
...
)
class DateRangeComponent extends ValueAccessorBase<DateRange> {
... implementation
// somewhere after the view init:
jqueryComponent.on('change', (eventData) => {
// here I have eventData.from and eventData.to as Date values
this.value = {
from: eventData.from,
to: eventData.to
};
});
}
So, if everyone else stumbles upon this question, the answer is: go ahead and write your own component.
As a side note, this works best when using forms only to prepare Json objects to be sent in Ajax calls. An old fashioned form-encoded submit would be less linear.
Upvotes: 1
Reputation: 2267
If you want to create you own ngModel
like two-way data-binding, that's what you should do:
@Component()
export class MyComponent {
myValue = 0;
@Output()
myValueChange = new EventEmitter();
@Input()
get myValue() {
return this.myValue;
}
set myValue(val) {
this.myValue= val;
this.myValueChange.emit(this.myValue);
}
}
Now you can use it as follows and have two-way data binding in effect:
<my-component [(myValue)]="someExpression"></my-component>
Adding a link to a simple tutorial on this as well: http://www.angulartraining.com/blog/tutorial-create-your-own-two-way-data-binding-in-angular/
Upvotes: 2
Reputation: 1468
You can use two Output
directives.
<my-date-range-picker name"xyz" [dateTo]="dateTo" [dateFrom]="dateFrom"></my-date-range-picker>
While in your component you would have
import { Output } from '@angular/core';
.
.
@Output() dateTo: any; // EventEmitter, Subject, Number, String, doesn't matter ...
@Output() dateFrom: any;
Here's a reference to component interaction using Input
and Output
Upvotes: 1