Conditionally Bind Custom-directives In Vue Js For 'clicking Outside An Element Event'
Solution 1:
Why don't you just do the selected check inside the click outside handler? You'll also need a way of passing the clicked item to the handler.
<div id="list-item" v-on-click-outside="outSideClickHandler(item)"></div>
outSideClickHandler(item) {
returnevent => {
if (item.selected) {
// Handle the click outside
}
};
}
Call the handler in the directive like this:
binding.value(event);
You do not get the automatic "expression/statement" binding for custom directives like do you with v-on
which is why you need to curry the handler function when you want to pass extra arguments to it.
By this I mean the argument passed to v-on
can be an expression or a statement, such as:
@click="handler" - handler is an expression (the function itself)
@click="handler(item)" - handler(item) is a statement
But with custom directives you can only pass expressions; the second line above is not possible unless handler()
returns another function.
I think there is some confusion because it seems what you want is to have a custom directive which is used only in this specific situation with your list items, but my solution above is more about writing a general "click outside" directive which you can use in any situation.
Also I think you do not want the directive to register any event listeners if the list item is not selected (for performance reasons?). If that's the case, then you can use event delegation instead.
There is no way to conditionally enable/disable a directive, you would have to do something like Morty's answer, both of which is kind of messy.
This seems workable but the whole point of using custom directives is to write reusable dom manipulation code
Are you against writing DOM manipulation code outside of directives? Angular 1 had this philosophy. Unless you want to reuse the directive in different situations then it may be overkill to write a directive for this situation just so that "DOM manipulation code does not pollute my component". If I'm going to write a directive, then I would want it to be as general as possible so that I can use it in many different situations.
I don't even need to pass the item in that case. Cause I have a component inside a v-for and not a div and I bind the custom directive on that component of which the handler is a method
Then I'm not sure why you'd want to implement this as a directive, which is rarely needed anyway. You can just register the body click event in the mounted
hook and remove it in the destroyed
hook. All of the click-outside logic can be contained within the component itself.
If your main concern is not having to register a body click event listener for each list item, you can do something like this:
const handlers = new Map();
document.addEventListener('click', e => {
for (const handler of handlers.values()) {
handler(e);
}
});
Vue.directive('click-outside', {
bind(el, binding) {
const handler = e => {
if (el !== e.target && !el.contains(e.target)) {
binding.value(e);
}
};
handlers.set(el, handler);
},
unbind(el) {
handlers.delete(el);
},
});
You can go one step further and automatically add the body click event listener whenever handlers
is nonempty and remove the listener when handlers
is empty. It's up to you.
Solution 2:
Currently there is no easy way to do conditional directive binding. You might considered using v-if
.
<divv-for='item of list'><divv-if='item.selected'id="list-item"v-on-click-outside="outSideClickHandler"
/><divv-elseid="list-item"v-on-click-outside="outSideClickHandler"
/></div>
Another approach would be modifying you directive implementation, so that it accepts another active boolean flag to opt out inside the eventListener.
<div id="list-item" v-on-click-outside="{handler: outSideClickHandler, active: item.selected}" />
Post a Comment for "Conditionally Bind Custom-directives In Vue Js For 'clicking Outside An Element Event'"