Vue: communicating parameters from app to component and back, using computed properties - vue.js

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.

Related

CMS Outlet References is not clear how to work

In this doc I found that I can customize our CMS templates, slots and components:
Data-driven outlets are provided by the CMS structure. There are three types, as follows:
CMS Page layout name: Each page layout is available as an outlet reference.
CMS page slot positions: Each slot position is an outlet reference. Since slot positions are not necessarily unique throughout the CMS structure, an outlet template might occur more then once. There is currently no standard technique available to limit the outlet for a specific position or page.
CMS Component type: Each component type is available as an outlet. While component type-driven outlets can be used, it is generally considered best practice to leverage Customizing CMS Components for introducing custom component UI.
Could you please provide any example how to do that with outlets if I have next custom structure:
<main>
<cx-page-layout class="AccountDetailsPageTemplate">
<cx-page-slot class="BottomHeaderSlot"></cx-page-slot>
<cx-page-slot class="AccountMenuSlot"></cx-page-slot>
<cx-page-slot class="AccountContentSlot"></cx-page-slot>
</cx-page-layout>
</main>
As result I would like to have next layout view:
I know how to do that using global styles only, but preferably is to use templates (outlets), because it can be the case when we require to add some extra parent div's over the slots and so on.
To build such structure with provided set of Slots, we can do next:
app.component.html (REQUIRED)
<ng-template cxOutletRef="AccountDetailsPageTemplate">
<app-account-details-page></app-account-details-page>
</ng-template>
account-details-page.component.html
<h1>
Hello AccountDetailsContentSlot
</h1>
<div class="d-flex">
<div class="w-50">
<cx-page-slot position="AccountMenuSlot"></cx-page-slot>
</div>
<div class="w-50">
<cx-page-slot position="AccountContentSlot"></cx-page-slot>
</div>
</div>
I didn't put here BottomHeaderSlot, because it should't be here, it should be implemented in cx-header component.

When exactly are keys useful in Vue?

I still don't quite get the use of keys even after reading through the docs but I understand that it has to do with keeping each item unique. I've been solely adding a key when using a v-for and only to the outer most parent (not the children of the v-for. Are there any other situations when keys should be used?
<div v-for="(person,index) in people" :key="index>
<div class="name-label>
<img :src="person.img/>
<div> {{ person.name }} </div>
</div>
</div>
The key directive alongside v-for, is used to uniquely identify each parent element rendered on the v-for operation.
The same happens in Reactjs:
{elements.map((value, index) => {
return <li key={index}>{value}</li>
})}
The key directive in Vuejs is also used to force Vuejs to re-render an element that contains it every time :key receives a new value.
As #firmino-changani indicated, other uses of key is if you want Vue to force a re-render or replacement of an element or component. See key special attribute documentation:
It can also be used to force replacement of an element/component
instead of reusing it. This can be useful when you want to:
Properly trigger lifecycle hooks of a component
Trigger transitions For example:
<transition>
<span :key="text">{{ text }}</span>
</transition>
When text changes, the <span> will always be replaced instead of patched, so a transition will be triggered.
Another example, lets say you have a dialog popup to add new data and another component that needs updating once the new data was entered via the popup. This second component might have logic inside lifecycle hooks like onMounted etc. Using a :key you can force re-rending this component once the dialog popup is saved e.g.
<template>
<page>
<add-data-popup #save="myKey++" />
<show-data :key"myKey" />
</page>
</template>
From the vueJs docs around Keys:
The key special attribute is primarily used as a hint for Vue’s virtual DOM algorithm to identify VNodes when diffing the new list of nodes against the old list.
To simply put it, if a div is rendered via a For-loop the div will be identical to each other div. When a user clicks on one of these Div's and you instantiate a #click function, how will vueJs know what differentiates each of these divs? How will vue know how to reorder these div's in the correct order if you remove a middle div?
VueJs uses the key to know how to reorder the DOM when an action is taken on one of these objects in your For loop.
Here is an article fully explaining about vueJs keys, and more details on the VueJS reactivity engine which is good to know about: https://www.telerik.com/blogs/in-vue-when-do-i-actually-need-the-key-attribute-and-why

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 draggable items between 2 components

I have a component that displays a list. I have another component that also displays a list.
They sit together on another component eg:
<template>
<div>
<my-component-1 :items="listItems1"/>
<my-component-2 :items="listItems2"/>
</div>
</template>
Is it possible to use a library such as vue-draggable to drag items from one component to the other?
Thanks
Yeah.
vuedraggable support many Sortable events like move, update ...
you can get new index and old index with this events and change items with indexes in your data().
https://www.npmjs.com/package/vuedraggable#events

Should I turn this code into own component if I am not sure whether I'd need to use it elsewhere?

I have a page in which I loop through an array of objects and render a <p> element for each element like this:
<p #click='selectCity(index)' v-for='(location, index) in locations'>
{{ location.city }}, {{ location.country }}
</p>
This is just an example and in the future I might have to render more stuff than just a <p> element per object in the array. Should I make a component out of this and just v-for the component by passing the item to it?
Even though I've used Vue for about 3 weeks, I'm still a little fuzzy when should I make out a component of something and when should I not.
I wouldn't. You use a component if you need some component feature like local state, a computed for each item, or lifecycle hooks. Or, obviously, if you're using the block of code in multiple places.
I don't see any reason here to move to a component. When your requirements change, maybe you will need one.