Vuejs computed properties and jquery ui sortable issue - vue.js

In my project I have a component with three ul lists. Also I have some kind of data array with items, each item has some properties. My aim is to:
Distribute items in basic array into three lists
Make it possible to drag&drop items between lists and accordingly update items data, since each item has a property which tells us which list the item belongs
Instead of copy-pasting a lot of code, I tried to reproduce the incorrect behaviour in jsfiddle by using simple example here:
https://jsfiddle.net/89pL26d2/4/
The thing is, when you drag&drop, you got exactly 2 items dragged, instead of one.
However, when I switched from computed properties to watch, I got the desired behaviour and everything worked just fine.
I figure out which line causes the error: the line when I update item property telling me which list the item should belong to after drag is finished. But I can't figure out why it causes this
I know that it's not the best way to work with HTML directly, but I'm okay with that for now.

Generally, whenever I see an issue in Vue, especially related to lists, where the wrong item is updated or something like that, it comes down to Vue trying to be smart but getting it wrong because it doesn't have the best information. This is almost always solved by using a key.
It is recommended to provide a key with v-for whenever possible,
unless the iterated DOM content is simple, or you are intentionally
relying on the default behavior for performance gains.
Key.
<div id="app">
<div>
<ul id="listA" data-list="A" class="connectedSortable">
<li v-for="item in listAFiltered" :key="item.id" :data-id="item.id">{{ item.title }}</li>
</ul>
<ul id="listB" data-list="B" class="connectedSortable">
<li v-for="item in listBFiltered" :key="item.id" :data-id="item.id">{{ item.title }}</li>
</ul>
</div>
</div>
Adding a key fixes your issue.

Related

Vue: communicating parameters from app to component and back, using computed properties

Sorry for the beginner question (I'm fairly sure this will be a duplicate, but I actually can't figure out what terms to use in order to find it). I just started with Vue.
I am just getting started with Vue, and following this course (https://www.vuemastery.com/courses/intro-to-vue-js/communicating-events). In this problem, there is (and here I don't know the term, so I'm going to go with...) app-level data parameter called cart. cart is an array which holds the id of each item a user has added to cart.
The problem tells us to add a button to remove items from the cart.
I ran into problems trying to create a computed property, which would allow me to hide the "remove" button in the event the selected item is not in the cart (eg. `
Communicating data from the app-level (the cart array), to the component-level (to a computed property in the product component), so that I could use something like :hidden="!inCart" on the "Remove from Cart" button, which is itself defined in the component. inCart would be a computed value here.
Communicating the selected product from the component to the app-level, computing inCart at the app level, then using the computed value at the component-level.
Either way, I can't seem to figure out how to do this in the way I would want to, which would look something like how v-bind operates. Namely, I think I may be able to hack together a solution using methods (which I believe have to be triggered by certain events), but I don't understand how I might go about this using built-in functionality such that the value of inCart is dynamically auto-computed.
Maybe there would be an answer to this in the next few courses, but I don't see us covering that in the intro material. Sorry for the neophyte question. Thank you in advance.
In Vue the way you communicate "state" from higher-level objects to lower-level objects is through props.
So, assuming your app looks something like...
<MyApp>
<MyShoppingPageWithItems>
<MyItem></MyItem>
<MyItem></MyItem>
<MyShoppingPageWithItems>
</MyApp>
You need to pass the cart object down as a prop.
So in your MyShoppingPageWithItems template, you'll have something like...
<template>
<div>
<MyItem v-for="item in items" :item=item :cart="cart"?
</MyItem>
</div>
</template>
And in your item template...
<template>
<div>
<div>
{{item.name}}
</div>
<div v-if="cart.includes(item.id)">
Remove button or whatever
</div>
</div>
</template>
Not that the .includes() method is a native JavaScript method, which you can read more about here.
Edit
To reference a prop in a computed property (or anywhere else in a Vue component), just refer to this.propName, as demonstrated here in the Vue docs.
So, if you want to create a computed property, you can do the following:
<template>
<div>
<div>
{{item.name}}
</div>
<div v-if="isInCart">
Remove button or whatever
</div>
</div>
</template>
<script>
export default {
props: ['cart', 'item'],
computed: {
isInCart() {
return this.cart.includes(this.item.id)
}
}
}
</script>
Note that the formula is the exact same as above, but just includes this. for cart and item. In templates, the this. is implied when referring to props, data, and computed properties.

Why are Vue dynamic components destroyed inside loop every re-render?

I run into an issue with Vuejs 2.x version (latest). While rendering a list of item inside a loop, if I make changes to the items then the normal components are not destroyed but the dynamic components will always be destroyed:
I have put a short sample code here:
https://gist.github.com/yellow1912/fc1c053e07c1ca136148484cf7f79d1a
I have also put a codepen here:
https://codepen.io/raineng/pen/zYGOXYY?editors=1111
<nl-test inline-template>
<div>
<div v-on:click="increase"> increase here please </div><br><br>
<div v-on:click="decrease"> decrease here please </div>
<ul>
<li v-for="(value, key) in getItems()" :key="key">
printing
<component :is="getItem()" :key="key"></component>
<nl-test inline-template>
<div>
this is a test here
</div>
</nl-test>
</li>
</ul>
</div>
</nl-test>
To see what I mean, open the console tab on codepen, click the add item and you will see that the dynamic component items are destroyed and re-created everytime.
I found out why, I need to use keep-alive:
https://v2.vuejs.org/v2/guide/components-dynamic-async.html
To quote:
When switching between these components though, you’ll sometimes want
to maintain their state or avoid re-rendering for performance reasons
Recreating dynamic components is normally useful behavior, but in this
case, we’d really like those tab component instances to be cached once
they’re created for the first time. To solve this problem, we can wrap
our dynamic component with a element
Wasted 2 days on this issue and then I found the answer just a moment after posting this on StackOverflow. Hope it helps someone.

Vue v-for with v-if

I found some inconsistency in the Vue documentation. If someone clarify this please. Looking at v-for-with-v-if it says it could be useful to do it. Which in my case I am in that exact situation. But now eslint is complaining.
So I looked at the style guide and its telling me to avoid this. So there is some clear contradiction.
Question: Is it really that bad that you should avoid it?
My Opinion: I don't see it as bad. I have quite a few use cases where this is useful.
Way 1:
all nodes will be rendered on every items[] change
<span v-for="item in items" v-if="item.shouldRender">...</span>
Way 2:
all nodes will be rendered once
<template v-for="item in items">
<span v-if="item.shouldRender">...</span>
</template>
Way 3:
only filtered nodes will be rendered. Filtered list is cached.
<span v-for="item in computedShouldRenderItems">...</span>
I think that 'way-1' is not REALLY bad. But I prefer to avoid it.

VueJS: <template> vs <div> or related for grouping elements for conditional rendering

In Vue.js, if you want to conditionally render multiple elements via a v-if/v-else-if directive you can wrap them inside <template> tags and apply the directive to the <template> tag, as explained here. However, you can also do the same with <div> tags wrapping the multiple elements. Are there any noticeable performance benefits to using <template> over <div> or any other similar native HTML5 tag?
I doubt there is a performance change but a big difference is that the <template> node does not appear in the DOM.
This is an important distinction especially when grouping children that are the only valid node types for their parent such as list items, table rows, etc:
This is valid:
<ul>
<template v-if="something">
<li>Text</li>
<li>Text</li>
</template>
</ul>
This is not:
<ul>
<div v-if="something">
<li>Text</li>
<li>Text</li>
</div>
</ul>
I know that the question is quite old, but I found out one more thing
if you use divs - your div will be in DOM, but empty, if v-if is false and it can make some spaces looks like margins
if you use template - you don't have anything in DOM and it just don't appear

Superfish menu - applying hoverClass onclick to non-sub-menu items?

Bit of a weird problem that I'm finding inexplicable - would appreciate any assistance! Unfortunately I can't provide a link to the development site in question, as I don't have permission to release the password, so I'll try to describe the issue in as much detail as possible.
Basically, I have a Superfish menu set up using the standard structure, abridged code as follows:
<ul class="sf-menu top-level">
<li class="level-1"><a class="level-1" href="/menu/">Menu Title</a>
<ul>
<li class="level-2"><a class="level-2" href="/sub-menu/">Sub-Menu Link</a></li>
<li class="level-2"><a class="level-2" href="/sub-menu/">Sub-Menu Link</a></li>
</ul>
</li>
<li class="level-1"><a class="level-1" href="/link/">Link Title</a></li>
<li class="level-1"><a class="level-1" href="/link/">Link Title</a></li>
</ul>
Menu is all working correctly as it should. However, the sfHover class - in addition to being applied to li's with child ul elements - is also being applied to li's WITHOUT child ul on click, which is resulting in styling being applied to these items without sub-menus that I don't want. I've checked the demo version of Superfish, and this anomaly also applies, so it seems to be a 'bug' with the default code, but I can't figure out how to fix it.
http://users.tpg.com.au/j_birch/plugins/superfish/#examples
Basically, I want sfHover (i.e. the class defined in sfDefaults as hoverClass) to be applied only to the items that actually have sub-menus. This is working correctly on hover, but on click applies the class across the board, which looks really weird for a split second before the new page loads in my case, as the styling applied to sfHover is very obviously only designed for items that have some sub-menu content.
I really hope that made sense, and would be incredibly grateful for anyone who can shed some light on this or suggest a workaround, as I'm tearing my hair out!