v-bind:class not working with array variables - vue.js

I have the following code in vue-js:
<table class="table table-sm user-perm-list">
<tbody v-for="index in totalUsers" v-bind:key="index">
<tr>
<td>
<div class="text-medium">{{userPermissionsName[index-1]}} </div>
<div class="d-flex py-1">
<a
style="cursor:pointer; margin-right:0.4rem"
#click=userPermissionsDownloadClicked(index-1)
:title="userPermissionsDownload[index-1] ? 'Download Permitted':'Download not permitted'"
v-bind:class="[userPermissionsDownload[index-1] ? 'permission-icon-class-active' : 'permission-icon-class-inactive']">
<downloadPermissionIcon/>
</a>
</div>
</td>
</tr>
</tbody>
</table>
the v-bind:class inside "a" tag is not working. Inside userPermissionsDownloadClicked, I am toggling the variable: userPermissionsDownload[index-1]. It works with a non array variable but not working with array variable

This is one of the "Vue Gotchas"
https://vuejs.org/2016/02/06/common-gotchas/
From "Why isn’t the DOM updating?"
When you modify an Array by directly setting an index (e.g. arr[0] = val) or modifying its length property. Similarly, Vue.js cannot pickup these changes. Always modify arrays by using an Array instance method, or replacing it entirely. Vue provides a convenience method arr.$set(index, value) which is syntax sugar for arr.splice(index, 1, value).

Related

inject render method into template

Must the render method of a Vue component be used exclusively or can it be combined with a template? Most of my component can be rendered using a template but just need a small part of it to be rendered using code. If this is possible how can I combine the render method output with the template?
Example in component:
<template>
<table>
<tr>
// use render method here
</tr>
<tr v-for="row in rows">
// use render method here
</tr>
</table>
</template>
Need render method in spots above to loop through the array $scopedSlots.column and render each <th> and <td> based on multiple <templates v-slot:column={row}> provided by parent.
As far as I know you can either use the render function or a template - but not both. They can not be combined.
What you could do to make your example work is to use the v-html-directive, which updates the innerHTML of an element https://012.vuejs.org/api/directives.html#v-html.
new Vue({
el: '#el',
data: {
rows: ['row1', 'row2', 'row3']
},
methods: {
renderRow(row) {
return `<td>${row}</td>`;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="el">
<table>
<tr v-for="row in rows" v-html="renderRow(row)">
</tr>
</table>
</div>
I'm having this same challenge as well. Which is how I came to your question.
As others have said, it seems you can either use a template or render function but not both.
My suggestion would be to have a component handle only the render function, while the parent component can use the template method
So, Per your example
<template>
<table>
<tr>
<component-using-only-render-method />
</tr>
<tr v-for="row in rows">
<component-using-only-render-method />
</tr>
</table>
</template>
I think its the best of both worlds with minimal compromise

Does vue.js have something like AngularJS' `ng-repeat-start`

Parsing vue.js' docs I can't see any hint that vue.js has something like a ng-repeat-start / ng-repeat-end
Is there a way to archive something like
<table>
<tr class="weather_warning top" ng-repeat-start="warning in dwd_warnings">
<td {{warning.valid_from}} → {{warning.valid_to}}</td>
<td><div style='background:{{warning.color}};'>{{warning.message}}</div></td>
</tr>
<tr class="weather_warning bottom" ng-repeat-end>
<td colspan="2">
{{warning.description}}<br/>
<span>Issued on the {{warning.effective}} ({{warning.headline}}). {{warning.ascertain}}</span>
</td>
</tr>
</table>
Each warning consists of 2 TRs which go together. One is the top half of the message in the iteration, the other one the lower half. It must be this way, because of some very weird formatting stuff, so there's no way around using 2 TRs for one item. Maybe some tags are off in this example, this is because I had to delete a ton of stuff in order to post it here. Note the colspan="2" in the TD of the second TR, which might give a bit of a hint of the problem which is tackled here.
Closest thing I can think of is to use a <template>, eg
<template v-for="item in items">
<header>
Header {{ item }}
</header>
<div class="body">
Body {{ item }}
</div>
<footer>
Footer {{ item }}
</footer>
</template
See https://v2.vuejs.org/v2/guide/list.html#v-for-on-a-lt-template-gt
... you can also use a <template> tag with v-for to render a block of multiple elements
Nothing changes with your edit, you just wrap your two <tr> elements in
<template v-for="warning in dwd_warnings">
<tr class="weather_warning top">
<!-- etc -->
</tr>
<tr class="weather_warning bottom">
<!-- etc -->
</tr>
</template>

How to properly use v-if and v-else in Vue JS <td> table tags

Since my searches doesn't suffice my issue, I'm posting it here to have some clarification on how to properly use v-if and v-else in <td> tags in HTML table in Vue.
//given the value of this.property is name
<tr>
<td v-if="this.property == 'name'">I'm name</td>
<td v-else>No I'm not</td>
</tr>
And my problem is, even though my this.property value is 'name' or NOT a 'name' it always falls to the v-else condition, and v-if is not executed. The output I want is to execute the v-if if the this.property value is 'name' and v-else if not.
Am I doing it the right way? I need your guidance here.
Remove this pointer. You don't need it.
//given the value of this.property is name
<tr>
<td v-if="property === 'name'">I'm name</td>
<td v-else>No I'm not</td>
</tr>

Vue: How to conditionally render tr in tbody

I have a table body with multiple rows, such as this:
<table>
<tbody>
<tr>...</tr>
<tr>...</tr>
</tbody>
</table>
I want to conditionally combine v-if an v-for, to conditionally render one or more additional rows. The Vue manual says to wrap the v-for in a v-if, such as follows:
<div v-if="team.positions != null">
<my-row v-for="position in team.positions"
:position="position"
:key="position.id">
</my-row>
</div>
The problem is that I can't put a div in a tbody, or any other element for that matter. What's the solution?
In those situations where no element would fit, you can use <template>, like:
<template v-if="team.positions != null">
<my-row v-for="position in team.positions"
:position="position"
:key="position.id">
</my-row>
</template>
Demo:
new Vue({
el: '#app',
data: {
showTwoRows: true
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<table>
<tr>
<td>A</td><td>B</td>
</tr>
<template v-if="showTwoRows">
<tr>
<td>1</td><td>2</td>
</tr>
<tr>
<td>3</td><td>4</td>
</tr>
</template>
<tr>
<td>C</td><td>D</td>
</tr>
</table>
<button #click="showTwoRows = !showTwoRows">Toggle two middle rows</button>
</div>
Though in that specific example of yours, it doesn't seem needed. Have you tried simply not using the v-if:
<my-row v-for="position in team.positions"
:position="position"
:key="position.id">
</my-row>
Because the v-for just won't iterate (without throwing errors) if its value is undefined/null/0/[]/'':
new Vue({
el: '#app',
data: {
message: "If I'm being displayed, Vue works!",
team: {
positionsU: undefined,
positionsN: null,
positionsZ: 0,
positionsE: [],
positionsS: ''
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<p>{{ message }}</p>
<table>
<tr v-for="position in team.positionsU"><td>u: {{ position }}</td></tr>
<tr v-for="position in team.positionsN"><td>n: {{ position }}</td></tr>
<tr v-for="position in team.positionsZ"><td>z: {{ position }}</td></tr>
<tr v-for="position in team.positionsE"><td>e: {{ position }}</td></tr>
<tr v-for="position in team.positionsS"><td>s: {{ position }}</td></tr>
<tr v-for="position in team.positionsF"><td>f: {{ position }}</td></tr>
</table>
</div>
You can use v-for and v-if on the same tag, however, it works differently to how you'd expect it to.
within the v-if you can reference the iterated item since v-for is performed before v-if
<div v-if="team.positions != null">
<my-row v-for="position in team.positions" v-if="position"
:position="position"
:key="position.id">
</my-row>
</div>
this would still iterate through all positions in team.positions, and not halt the for loop if the condition in the v-if was not met, but rather skip it.
think of it like this:
for (var i = 0; i < array.length-1; i++) {
if (array[i]) {
doTheThing();
}
}
I am not sure if this is exactly what the original question is looking for, but I just had a similar issue where I wanted to ignore rendering rows where the price of a item was 0.
I ran into the problem using v-if in the <tr> containing the v-for. I solved it by simply using a v-show instead.
So this worked perfectly in my case.
<tr v-show="item.price !== 0" :key="item._id" v-for="item in items"> ... </tr>

Condition in v-for of vue.js?

I would like to avoid image value in below code.image is a key for property. How can I do that ?
<tbody>
<tr v-for="obj in data" :id="obj.id">
<td v-for="property in obj">{{property}}</td>
</tr>
</tbody>
The Accepted answer is an anti-pattern because you should not mix v-for and v-if on the same node in VueJs 2+ as Thomas van Broekhoven pointed out. Instead, you can just chain a filter onto the object. Here is an example using an ES6 arrow function which should* work.
Untested syntax. Typing on my phone, in bed.
<tbody>
<tr v-for="obj in data" :id="obj.id">
<td v-for="property in obj.filter(property => property !== 'image')">{{property}}</td>
</tr>
</tbody>
Let check it out: v-for with an Object, v-for with v-if.
<td v-for="(value, property) in obj" v-if="property!='image'">
{{value}}
</td>