How to set router-link for a component except some parts - vue.js

I've created a card using VueJS & tailwind. There are some buttons on the card like edit & delete. I want to add a router-link on this Card Component, so that when a user clicks on the card, it opens up, but then the edit and delete buttons don't work any more and clicking on them, redirects the page too.
How can I solve this problem?
Here is my code:
<router-link to="somewhere">
<card-component :data="products"></card-component>
</router-link>
Card Component:
<div :key="id" v-for="(row, id) in data">
<div>
<img alt="Product Image"
v-lazy="row.img_url"
class="vs-images-hover-zoom rounded-lg p-0 h-24 w-32 object-center object-cover"/>
</div>
<div>
<p class="product-name font-medium">{{ row.name }}</p>
</div>
<div class="whitespace-no-wrap">
<feather-icon icon="EditIcon" svgClasses="w-5 h-5 hover:text-primary stroke-current" #click="editPopup = true"/>
<feather-icon icon="TrashIcon" svgClasses="w-5 h-5 hover:text-danger stroke-current" class="ml-2" #click="deletePopup = true"/>
</div>
</div>

Seems like you're having trouble with event handling because button's clicks are being caught by router-link. You can do one of them:
Use #click.stop in your buttons inside the Card component (it will stop event propagation);
Don't use router-link wrapping the whole card and put it just on image and title elements, for example;
Don't use router-link at all and get the #click event in right peaces of card, using this.$router.push to handle it.

Related

HTMX: disable click event on child nodes

I have the following divs and want that when clicking on id="child" it should NOT execute a get to "/modal/closed" and NOT replace the id="modal". That is, do nothing HTMX related.
<div id="modal" hx-trigger="click"
hx-swap="outerHTML" hx-target="#modal" hx-get="/modal/closed" hx-params="none">
<div id="child">
</div>
</div>
Right now I have it working using the trigger "click consume" but need to specify an hx-get to an HTTP path that returns nothing (/nop).
Is there a cleaner way to do this?
<div id="modal" hx-trigger="click"
hx-swap="outerHTML" hx-target="#modal" hx-get="/modal/closed" hx-params="none">
<div hx-trigger="click consume" hx-swap="none" hx-get="/nop">
</div>
</div>
This can be solved using the event modifier "target:#modal" to indicate we are only interested on the click event originated from the id="modal".
This way clicks over id="child" that bubble up to id="modal" will be ignored.
<div id="modal" hx-trigger="click target:#modal"
hx-swap="outerHTML" hx-get="/modal/closed" hx-params="none">
<div id="child">
</div>
</div>
It works this way due to attribute inheritance in htmx https://htmx.org/docs/#inheritance. Placing the hx-trigger attribute on the outer-most div, means that element will listen for the event in all of it's children. When you add the same attribute to a child div with the consume modifier it stops the event bubbling up.
So the solution here would be to remove the hx-trigger from the outer div. In fact, if what you are trying to do is close a modal, you can add a close button somewhere else in the div. Something like this:
<div id="modal">
<button id="close" hx-trigger="click"
hx-swap="outerHTML" hx-target="#modal" hx-get="/modal/closed" hx-params="none">Close</button>
<div id="child">
</div>
</div>
Now when you click on the child div the event bubbles up and it's not caught by the sibling close button.

VueJS transition on computed values

I want to animate a block with posts that can be filtered.
Some filters trigger a computed method filteredPosts and they are assigned to a component liek that <block-article :posts="filteredPosts" />
In my <block-article> component I have something like that :
<template>
<div class="posts">
<div v-for="post in posts" :key="post.id"></div>
</div>
</template>
I want div .posts animate like a translation bottom/fade out on disappear and translation top/ fade in on appear.
I tried that :
<template>
<transition name="slide-fade">
<div class="posts">
<div v-for="post in posts" :key="post.id"></div>
</div>
</transition>
</template>
with corresponding css classes but it doesn't work.
I tried that :
<template>
<div class="posts">
<transition-group name="slide-fade">
<div v-for="post in posts" :key="post.id"></div>
</transition-group>
</div>
</template>
but my class .posts is a grid and here I lost the grid behavior.
THE AIM is to animate the entire div .posts rather than each elements of the v-for.
Any idea ?
Thanks all,
I finally achieve this with :
<transition name="slide-fade">
<div :key="posts.length" class="posts"></div>
</transition>
Nothe the :key="posts.length"
The problem is when posts.length doesn't change but it works in a lots of case. I will search how to fix this exception.
If you animate entire div, you should use transition, but in this case all inner elements not animated. If you want to animate all inner elements. You should use transition-group
In your case I think, need use all this method with build-in tag attribute.
Becouse in dock you can read
https://v2.vuejs.org/v2/guide/transitions.html
Unlike transition, it renders an actual element: a span by default. You can change the element that’s rendered with the tag attribute.
So you can write like this(its not full code, you must add name, key and other attrs)
<transition>
<transition-group tag="div" class="posts">
<div v-for="post in posts"></div>
</transition-group>
</transition>

Vue Transition inside the div element doesn't work

Could someone explain me, why the following code doesn't work?
<template>
<div class="modal">
<transition name="slide-in">
<div #click.stop class="modal__container">
<div #click="close" class="modal__button">
<span class="modal__button--close">Close</span>
</div>
</div>
</transition>
<transition name="fade-in">
<div #click="close" class="modal__overlay"/>
</transition>
</div>
</template>
I'm trying to create modal with two different animations (slide-in for text area and fade-in for modal overlay).
If i delete the element with class modal and edit code to the following everything works fine.
<template>
<transition name="slide-in">
<div #click.stop class="modal__container">
<div #click="close" class="modal__button">
<span class="modal__button--close">Close</span>
</div>
</div>
</transition>
</template>
Referencing Vue.js documentation on transitions
Vue provides a variety of ways to apply transition effects when items are inserted, updated, or removed from the DOM
That means that DOM nodes that transition applies classes to, should be those which are inserted/updated/removed.
Since it is a modal window, I assume that it has v-if directive applied in parent component to handle it's visibility. In order for transition to work, it should wrap DOM element that will be updated.
You can understand it more easily if you move code of your modal window into the parent component. Just for better visualization of elements tree and transition's behavior.
In first example, conditional rendering (v-if) applies to <div class="modal">, which is not wrapped with transition - therefore no animation will be triggered. At the same time, nested nodes are wrapped with transition, but there is nothing that will update or remove them. They are statically displayed and inserted initially on component's creation. Nothing to animate.
In order for it to work as expected following structure would be advisable:
<template>
<transition name="fade-in">
<div
class="modal__overlay"
#click="close"
>
<transition name="slide-in">
<div
v-if="containerVisible"
class="modal__container"
#click.stop
>
<div #click="close" class="modal__button">
<span class="modal__button--close">Close</span>
</div>
</div>
</transition>
</div>
</transition>
</template>
This solution expects modal__overlay to have position: fixed; style and variable containerVisible to be set to true inside mounted hook of modal component.

Using v-for with v-on:click in a Vue Component

I have what I think is a basic question for Vue, but I'm trying to run a method on click while also running a v-for loop on a component.
I'm not sure why but I can't get anything to run on that click handler but I see nothing in the Vue documentation saying that this isn't possible. Right now I'd settle for getting my console log running.
<IconBox
v-for="step in steps"
:key="step.slug"
:step="step"
:formData="formData"
#click="console.log('click')"
/>
Here is the template for IconBox.vue:
<template>
<div class="icon-box">
<div
class="icon-holder"
:style="{ backgroundImage: 'url(' + step.image + ')' }"
>
</div>
<div class="text">
<div class="inner">
<h5>{{ step.name }}</h5>
<p v-if="step.description">{{ step.description }}</p>
{{ isSelected }}
</div>
</div>
</div>
</template>
I could run the click in the component itself but I need the parent well aware of what's happening to handle a selected boolean.
To use native events in component tags you should add .native modifier
<IconBox #click.native="yourMethod"/>
Check this guide.
To check it I suggest you to create a method and add console.log() inside it.
I have been playing around with Vue lately, and here's how I solved a similar problem in my project
<IconBox
v-for="step in steps"
:key="step.slug"
:step="step"
:formData="formData"
#click="console.log('click')"
/>
Changes to
<template v-for="step in steps">
<IconBox
:key="step.slug"
:step="step"
:formData="formData"
#click="console.log('click')"
/>
</template>

Vue - Nested Click Event Bubbling Issue

I have a menu button with a click event in Vue. When the button is clicked, it's supposed to activate the menu itself. This is the parent element which when clicked activates the menu (the toggleMenu function makes menuIsActive true). This part works fine:
<div class="navbar-item has-dropdown #click="toggleMenu">
<div class="navbar-link"></div>
<app-navmenu :class="{'is-active': menuIsActive}"/>
</div>
And this is the app-navmenu component that gets rendered:
<div class="navbar-dropdown" #click.stop>
<div class="container is-fluid">
<div class="column">
<h1 class="title">Title</h1>
<router-link class="navbar-item" :to="route" exact>
<div class="navbar-content">
<p class="has-text-info">info</p>
<small>meta info</small>
</div>
</router-link>
</div>
</div>
</div>
The problem I am running into is that I don't want the menu to disappear when I click on the actual navbar-dropdown div element, hence why I have a #click.stop. However, I do want the menu to disappear when I click on a router-link element, but since I have #click.stop in the navbar-dropdown element, the menu persists. If I don't have a #click.stop event on the navbar-dropdown element, then the menu disappears as soon as the navbar-dropdown element is clicked on, which I don't want.
How can I have the menu persist when clicking on the dropdown body, but also have it disappear when I click on a router-link? I've tried other click methods like .self and .prevent, but those don't seem to do what I need.
I am exactly not sure with your requirement, but following your comment, you can use something like this:
This will even push to the router link:
<router-link class="navbar-item" :to="route" exact
#click.native.prevent="callYourMethod">
This will prevent to go to the router link:
<router-link class="navbar-item" :to="route" exact
#click.native.prevent="callYourMethod" event="">
This is what I ended up doing to fix my issue. First, I moved the click function in the parent to one of its children:
<div class="navbar-item has-dropdown">
<div class="navbar-link" #click="toggleMenu"></div>
<app-navmenu :class="{'is-active': menuIsActive}"/>
</div>
This lets the body of the menu stay active even when I click on the body without having to use a #click.stop. Then in the menu itself, I did this so that links will close the menu:
<div class="navbar-dropdown" #click.stop>
<div class="container is-fluid">
<div class="column">
<h1 class="title">Title</h1>
<div #click="toggleMenu">
<router-link class="navbar-item" :to="route" exact>
<div class="navbar-content">
<p class="has-text-info">info</p>
<small>meta info</small>
</div>
</router-link>
</div>
</div>
</div>
</div>
One strange behavior I noticed is that if I put the #click="toggleMenu" function in the <router-link/> element itself, it doesn't get called on, even if I use .prevent. Hence the need for the div wrapper around the router-link element.