Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define a temporary variable in Vue.js template

Here is my current template:

<a-droppable v-for="n in curSize" :key="n - 1" :style="{width: `${99.99 / rowLenMap[orderList[n - 1]]}%`, order: orderList[n - 1]}">   <a-draggable :class="{thin: rowLenMap[orderList[n - 1]] > 10}">     <some-inner-element>{{rowLenMap[orderList[n - 1]]}}</some-inner-element>   </a-draggable> </a-droppable> 

The problem is that i have to write rowLenMap[orderList[n - 1]] multiple times, and i'm afraid vue.js engine will also calculate it multiple times.

What i want is something like this:

<a-droppable v-for="n in curSize" :key="n - 1" v-define="rowLenMap[orderList[n - 1]] as rowLen" :style="{width: `${99.99 / rowLen}%`, order: orderList[n - 1]}">   <a-draggable :class="{thin: rowLen > 10}">     <some-inner-element>{{rowLen}}</some-inner-element>   </a-draggable> </a-droppable> 

I think it's not difficult to implement technically because it can be clumsily solved by using something like v-for="rowLen in [rowLenMap[orderList[n - 1]]]". So is there any concise and official solution?

like image 563
silvestris Avatar asked May 16 '17 11:05

silvestris


People also ask

How do I make my Vue variable reactive?

Using reactive instead: import { reactive } from 'vue'; // Can be done entirely outside of a Vue component const externalVariable = getValue(); // Reactive only works with objects and arrays const anotherReactiveVariable = reactive(externalVariable); // Access directly console.

What does template tag do in Vue?

Vue uses an HTML-based template syntax that allows you to declaratively bind the rendered DOM to the underlying component instance's data.

What is inline template in Vue?

x Syntax x, Vue provided the inline-template attribute on child components to use its inner content as its template instead of treating it as distributed content. html <my-component inline-template> <div> <p>These are compiled as the component's own template.</


2 Answers

I found a very simple (almost magical) way to achieve that, All it does is define an inline (local) variable with the value you want to use multiple times:

<li v-for="id in users" :key="id" :set="user = getUser(id)">   <img :src="user.avatar" />   {{ user.name }}   {{ user.homepage }} </li> 

Note : set is not a special prop in Vuejs, it's just used as a placeholder for our variable definition.

Source: https://dev.to/pbastowski/comment/7fc9

CodePen: https://codepen.io/mmghv/pen/dBqGjM


Update : Based on comments from @vir us

This doesn't work with events, for example @click="showUser(user)" will not pass the correct user, rather it will always be the last evaluated user, that's because the user temp variable will get re-used and replaced on every circle of the loop.

So this solution is only perfect for template rendering because if component needs re-render, it will re-evaluate the variable again.

But if you really need to use it with events (although not advisable), you need to define an outer array to hold multiple variables at the same time :

<ul :set="tmpUsers = []">   <li v-for="(id, i) in users" :key="id" :set="tmpUsers[i] = getUser(id)" @click="showUser(tmpUsers[i])">     <img :src="tmpUsers[i].avatar" />     {{ tmpUsers[i].name }}     {{ tmpUsers[i].homepage }}   </li> </ul> 

https://codepen.io/mmghv/pen/zYvbPKv

credits : @vir us

Although it doesn't make sense here to basically duplicate the users array, this could be handy in other situations where you need to call expensive functions to get the data, but I would argue you're better off using computed property to build the array then.

like image 199
Mohamed Gharib Avatar answered Oct 19 '22 06:10

Mohamed Gharib


Judging by your template, you're probably best off with a computed property, as suggested in the accepted answer.

However, since the question title is a bit broader (and comes up pretty high on Google for "variables in Vue templates"), I'll try to provide a more generic answer.


Especially if you don't need every item of an array transformed, a computed property can be kind of a waste. A child component may also be overkill, in particular if it's really small (which would make it 20% template, 20% logic and 60% props definition boilerplate).

A pretty straightforward approach I like to use is a small helper component (let's call it <Pass>):

const Pass = {   render() {     return this.$scopedSlots.default(this.$attrs)   } } 

Now we can write your component like this:

<Pass v-for="n in curSize" :key="n - 1" :rowLen="rowLenMap[orderList[n - 1]]" v-slot="{ rowLen }">   <a-droppable :style="{width: `${99.99 / rowLen}%`, order: orderList[n - 1]}">     <a-draggable :class="{thin: rowLen > 10}">       <some-inner-element>{{rowLen}}</some-inner-element>     </a-draggable>   </a-droppable> </Pass> 

<Pass> works by creating a scoped slot. Read more about scoped slots on the Vue.js documentation or about the approach above in the dev.to article I wrote on the topic.


Appendix: Vue 3

Vue 3 has a slightly different approach to slots. First, the <Pass> component source code needs to be adjusted like this:

const Pass = {   render() {     return this.$slots.default(this.$attrs)   } } 
like image 25
Loilo Avatar answered Oct 19 '22 05:10

Loilo