How to reset a prop on cancel event? - vuejs2

I have a list of attachments that a user can manually delete via a click event. There is a problem when the user cancels his edits, the UI still shows an edited list of attachments despite the previous action being canceled. How can I reset the attachments so they revert back to the original listing?
Codesandbox (click POSTS --> post id --> EDIT THIS POST --> and then delete an attachment --> click CANCEL EDIT and you will see that the attachment you deleted are gone)
I am passing props to child component to be edited:
<EditPost
:post="post"
#update="editPostFormIsVis=false"
#cancel="editPostFormIsVis = false"
:attachmentsArray="attachmentsArray"
#deleteMediaAttachment="removeItem"
/>
</section>
Child component with the click event that emits data back to parent:
<ul>
<li>Media Attachments
<ul v-if="attachmentsFileNames && attachmentsFileNames.length">
<li v-for="(attachmentFileName, index) in attachmentsFileNames" :key="index">
<a href="
#
">{{ attachmentFileName }}</a>
<button
#click.prevent="$emit('deleteMediaAttachment', attachmentFileName)"
>Delete me!</button>
</li>
</ul>
</li>
</ul>
Anyone have ideas on how to reset my attachmentsArray on cancel?
Thanks!

I think in your created method of EditPost you'll want to add something like:
this.cachedAttachmentsArray = Object.assign([], this.attachmentsArray.attachments)
and in your cancelEdit method add something like:
Object.assign(this.attachmentsArray.attachments, this.cachedAttachmentsArray)
for a small change to your EditPost Component.
Hope it helps

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.

In Vue, how can I change one element generated by a v-for statement?

In my Vue app, I have a list of items that can be added to the basket generated by a v-for. I want to have it so that when the 'add to basket' button for an item is clicked, the text for the button on just that item changes to 'added' or whatever.
If I set the button text as a variable and update it when I call my addToBasket() function, it sets the text on all the items generated by the v-for, so every Add to Basket button in the list changes to 'added'. I want to target just the clicked button. How can I do that?
There are lots of ways you could do this, you need someway of tracking what is in the basket on a per-item basis.
For example, we could have a template like this;
<div id="app">
<ol>
<li v-for="item in basket">
{{ item.text }}
<button type="button" #click="item.added = !item.added">
<span v-if="item.added">Remove from basket</span>
<span v-else>Add to basket</span>
</button>
</li>
</ol>
</div>
Where we iterate with a v-for each item in the basket. When the button is clicked for that item, it will toggle the state on just that item. We can then use that state to determine what text we show in the button next to it.
Example fiddle here.

<router-link> Vue Router #click event

I tried using a click event on a <router-link>. It works, but it is reloading the page everytime the link is clicked. I would like to avoid it but I can't figure out how.
I am aware that <router-link> does not accept a simple #click event. I saw on some forums that #click.I native would work, but as we know, that is deprecated.
So I would like to know if there is any solution other than wrapping the router link in a div and putting the listener on that div.
The reason why I want to do this is that I want to bind a class dinamicaly when the link is clicked. I have created a dropdown menu which is triggered onClick. But then when I follow a link inside that dropdown menu, the menu remains open. Therefore, I would like to have an additional #click event to dinamically bind a class (display: none) to the dropdown menu. The thing is that the items inside the dropdown are iterated which send parameters to a Vuex Mutation and therefore i can’t use regular tags and wrapping the router-links with a span or div is also not giving me the desired effect.
Thank you !
Regards,
T.
I have managed to solve the problem using a div wrapper and changing my css (that was preventing the code to work properly)
<div class="dropdown">
<a class="dropbtn" #click="dropClick"><i class="ri ico ri-draft-line"></i> Docs <i class="ri ico ri-arrow-drop-down-line ri-1x"></i></a>
<div class="dropdown-content" :class="{ 'dropdown-content-display': clicked }">
<div class="wrapper" v-for="route in $store.state.menuItems" :key="route.name" #click="dropClick">
<router-link :to="{ name: 'docs', params: { title: route.name } }"> <i :class="'ico ri ' + route.icon"></i> {{ route.name }}
</router-link>
</div>
</div>
</div>
If a understand your question, there is a "active-class" property on vue-router(router-link). You can set your classes dynamically by based on an active route.

Child component using parent component data to re-direct

I currently have a website/app that displays "FeatureCard" (components) onto a "Grid" component.
The feature cards have all the basic props to pass to the parent (Grid) component, so that data can be grabbed and a v-for used to create as many "featureCards" as possible.
That is all fine and working.
The issue comes when I want a title of a Feature card to link to its on slug-route.
Here is a snippet of my "FeatureCard" component -
<div class="card-content">
<div class="row-1">
<!-- <h4 class="bold">{{ resourceTitle }}</h4> -->
<router-link :to="{ name: 'FigmaFind', params: {resource_slug: this.slug} }"><h4 class="bold">{{ resourceTitle }}</h4></router-link>
<button class="price"><h6 class="bold">{{ resourcePrice }}</h6></button>
</div>
<div class="row-2">
<a :href=creatorProfile target="_blank" >
<img :src="creatorImage">
</a>
<a :href=creatorProfile target="_blank" >
<p>By <strong>{{ creatorsName }}</strong></p>
</a>
</div>
<div class="row-3">
<a :href="downloadLink" class="btn main" target="_blank" >
<p class="light semibold" for="info" >Download Resource</p>
</a>
<a :href="resourceOriginalLink" class="btn icon" target="_blank">
<p class="material-icons light md-18">info</p>
</a>
</div>
</div>
As you can see, in "row-1" I have tried including a router-link, but obviously it doesn't work as it has not yet recieved the data for "this.slug" but essentially, that is where I want a user to click this, and be redirected to "website.com/selected-item-1"
My issue is, I do not pull the "slug" data in, until it is rendered onto the Grid component. So, until then I am not sure how to reference it for later use, I referenced the titles as "resource title" or links as ":href="resourceOriginalLink"" but no idea how to do it for a router item. I could just use ":href="resourceOriginalLink"" but I don't know how I would pass the route-info through...
any ideas?
UPDATE
So, I figured it out. It may be the wrong way to do it, or there may be an easier way. But here is what I have done.
I passed the data "slug" as a prop to my child component (featureCard).
I then set slug: this.resourceSlug in my data.
Then within my title section I simply set <router-link :to="{ name: 'FigmaFind', params: { resource_slug: this.slug }}"><h4 class="bold">{{ resourceTitle }}</h4></router-link>
And, it seems to be working!!
Obviously, if there is a better way please, someone let me know.
UPDATE
So, I figured it out. It may be the wrong way to do it, or there may be an easier way. But here is what I have done.
I passed the data "slug" as a prop to my child component (featureCard). I then set slug: this.resourceSlug in my data. Then within my title section I simply set {{ resourceTitle }}
And, it seems to be working!!
Obviously, if there is a better way please, someone let me know.

how to pass index to sibling div/tag

I'm developing a mobile app using phonegap, vuejs2 and onsenui. I installed vue2-touch-events plugin for touch events. In this app i'm rendering some data using v-for. Here is the div.
<v-ons-list-item class="swipeArea"
v-for="(list, index) in todoLists"
v-bind:key="list.todo"
v-if="!list.completed"
v-touch:swipe.left="deleteLisItem(index)"
v-touch:swipe.right="doneLisItem(index)"
v-touch:longtap="deleteOrDone(index)" tappable>
<label class="center">
<i class="zmdi zmdi-check" aria-hidden="true"></i> {{ list.todo }}
</label>
</v-ons-list-item>
And then when user longtap a list there will be a pop with 3 button. Done, Delete and cancel. When user tap done, the item will be mark as done, if click delete, the item will be deleted and click cancel will cancel the event. Here is the div
<div v-if="doneOrDelete" id="doneOrDelete">
<div class="action-sheet-mask"></div>
<div class="action-sheet">
<button class="action-sheet-button" #click="doneLisItem">Done</button>
<button class="action-sheet-button action-sheet-button--destructive" #click="deleteLisItem">Delete</button>
<button class="action-sheet-button" #click="doneOrDelete=false">Cancel</button>
</div>
</div>
Now all i have to do is pass the index to the method. Can anyone help me how pass the index? TIA