I’m trying to write a simple component that as soon as the user clicks away from the component, an event fires onBlur() restoring the state of the component. I have tried using the aurelia-blur-attribute but can’t seem to get it work.
I guess I’m missing something really obvious here.
I would appreciate any thoughts.
My componenent as follows:
import { autoinject, bindable, bindingMode, observable } from "aurelia-framework";
@autoinject
export class SearchBox {
@bindable({ defaultBindingMode: bindingMode.twoWay }) public value: string;
@bindable public label: string = "";
public showInput: boolean = false;
public formIsBlur = true;
public showInputField() {
this.showInput = !this.showInput;
}
public updateInput() {
this.showInput = (this.value && this.value.length > 0);
}
}
export class SearchBox {
@bindable({ defaultBindingMode: bindingMode.twoWay }) public value: string;
@bindable public label: string = "";
public showInput: boolean = false;
@observable public formIsBlur = true;
public showInputField() {
this.showInput = !this.showInput;
}
public updateInput() {
this.showInput = (this.value !== undefined && this.value.length > 0);
}
protected formIsBlurChanged() {
this.updateInput();
}
}
and
<form blur.delegate="formIsBlur = true">
The searchBox works fine, except when you mouseclick into the input, do not type anything and then mouseclick anywhere else on the page - in this case the formIsBlurChanged is not fired.
I also tried changing the config to
const listeningModeOptions = {
pointer: true, // listen for pointer event interaction
touch: true, // listen for touch event interaction
mouse: true, // listen for mouse event interaction
focus: true, // listen for foucus event
windowBlur: true // listen for window blur event (navigating away from window)
};
The blur attribute plugin was developed to solve a problem that I had: less boiler plate & error prone code determining focus state of an element.
To know why this is a problem, we need to get familiar with focus events of the web: the main ones that are used are “focus” and “blur” event. Unfortunately, they come with a bunch of annoyance when combined together, across multiple elements:
They don’t bubble
They only reflect the focus state of a single element, not its descendants.
So there’s a need to have something simple, that can reliably reflect the focus state of an element. (1) An element is determined as “focused” when either itself is receiving focus, or (2) any of its descendants is receiving focus. The blur attribute was developed to reflect the absence of both (1) and (2). When neither (1) nor (2) is true, it sets its main bindable value to false, then gets reflected to any model bound to it.
<form blur.bind="formIsFocused">
<!-- ...10 of inputs ... -->
</form>
With the above, whenever the loses its focus, formIsFocused will be set to false, then we can handle it easily via a change handler:
export class MyEl {
formIsFocusedChanged(isFocused) {
// do something
}
}
Bear in mind that change handler is only invoked when the value gets changed, so if formIsFocused is never set to true, assigning false over and over won’t invoke our change handler. To solve this, we can use capture binding command to set formIsFocused to false whenever the form or anything inside it receives the focus:
With this code, I’ve managed to get the component to behave correctly on loss of focus. However, the first time I click into the input, I need to click twice on the label to hide the label and show the input.
It’s because focus.capture was never triggered, due to no focus event fired when clicking on the label. You can fix this via assigning a tabindex attribute to the label, demo here Readme Example - CodeSandbox