import {autoinject, bindingMode, customAttribute} from 'aurelia-framework';
@customAttribute('number-value', bindingMode.twoWay)
@autoinject
export class NumberValueCustomAttribute {
private inputElement: HTMLInputElement;
private value: number = NaN; // Bound value set by Aurelia
constructor(inputElement: Element) {
this.inputElement = inputElement as HTMLInputElement;
}
// noinspection JSUnusedGlobalSymbols
/**
* Hook into Aurelia component lifecycle
* Data binding is activated on the view and view-model
* @param _bindingContext the primary binding context that this view is data-bound to
* @param _overrideContext the override context which contains properties capable of overriding those found on the binding context
*/
bind(_bindingContext: object, _overrideContext: object): void {
this.valueChanged(); // Since we implement both the bind and valueChanged callbacks, only bind will be called when the value is initially bound
this.inputElement.addEventListener('blur', this.inputValueChanged.bind(this));
}
// noinspection JSUnusedGlobalSymbols
/**
* Items have changed
* @param _newValue the new value
* @param _oldValue the old value
*/
valueChanged(_newValue?: string, _oldValue?: string): void {
// Synchronize the input element with the bound value
if (this.value == undefined || Number.isNaN(this.value)) {
this.inputElement.value = '';
}
else {
this.inputElement.value = this.value.toString(10);
}
}
// noinspection JSUnusedGlobalSymbols
/**
* Hook into Aurelia component lifecycle
* Component is unbound
*/
unbind(): void {
this.inputElement.removeEventListener('blur', this.inputValueChanged.bind(this));
}
/**
* Handle the input element's value changing
* @param _event the focus event
* @private
*/
private inputValueChanged(_event: FocusEvent): void {
this.value = Number.parseFloat(this.inputElement.value);
}
}
But when I try to use it with the binding behavior updateTrigger, e.g.
// ensure the binding's target observer has been set.
let targetObserver = binding.observerLocator.getObserver(binding.target, binding.targetProperty);
if (!targetObserver.handler) {
throw new Error(notApplicableMessage);
}
Any ideas on how to get my custom attribute to work with the updateTrigger binding behavior?
It’s throwing because it cannot find a property handler on the observer of the property value of your number-input custom attribute. The reason is it’s supposed to work in bindings for native input elements only.
I’m not sure how to make it work together yet, but since you want to have events configurable, maybe do
bind(_bindingContext: object, _overrideContext: object): void {
this.valueChanged(); // Since we implement both the bind and valueChanged callbacks, only bind will be called when the value is initially bound
- this.inputElement.addEventListener('blur', this.inputValueChanged.bind(this));
+ this.inputElement.addEventListener(this.event, this.inputValueChanged.bind(this));
}
and have event as a bindable property on the number input attribute? then in your template, it could be like this:
you really don’t need all of that at all.
html input already have a native way of extracting the value as a number, and aurelia can bind directly to it.
so just use <input type="number" value-as-number.two-way="myVal" />,
no extra custom attribute required.
the reason we need two-way instead of regular bind. is because aurelia’s default binding mode for value is two-way be default. but for value-as-number is not.
Thanks @bigopon, this is exactly what I ended up doing and it worked perfectly. Actually, no quotes on the event, i.e. number-value="value.bind: fooNum; event: input".
I was just wondering if there was an easy way to get my input / custom attribute to behave more like a native element.
this is very weird, because valueAsNumber is a native property just like value.
It seems logical to me that whatever work aurelia has done for value, can be applied similarly on valueAsNumber.
I assume there is a if somewhere in the framework, that disallow wrong uses of bindings, and that if didn’t take valueAsNumber and valueAsDate into account.
the same way we can see that the default bindingMode for those properties are not two-way, but its very logical that they should be.
they should behave exactly like value does.
if we find and fix that behavior in the framework - this code (and many other bits of tiny things) should work auto-magiclly.
well… valueAsNumber and valueAsDate should be added to that list.
but I’m not entirely sure that will fix the current issue - their must also be other places where this kind of logic take place. right?
Beside that part, Aurelia also needs to be taught what events to use for observing the input element as well. In v2, this’ll be easier, I’ll follow up with an example how to do it.