@vincej As a couple have said, Vue is based on state, and your template’s appearance should be a function of that view. If a CSS class should be applied to a particular table row, then it should be because the state of the object that row represents has changed and the condition evaluates to a truth-y value.
I’m unsure what class you’re trying to apply when from your example, so I’ll try to create a simplified example to hopefully demonstrate what we mean by using state to drive your UI.
A Vue component has data. So if you have a table, I imagine you have an array of objects representing some form of entity that you want to display as rows in a table. If you specify this array in the data key of your Vue component, then it becomes “reactive”. That means, any modifications to the array or objects inside it, will cause your template to re-rendered with the new state of that data:
data() {
return {
items: [
{ id: 1, name: 'Foo', isComplete: false },
{ id: 2, name: 'Bar', isComplete: false },
];
};
},
So we have an array with two objects, and each object has three keys: id, name, and isComplete. We can render that in a simple table in the template:
<table>
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
<tr v-for="item in items" v-bind:key="item.id">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>
<template v-if="item.isComplete">
<button type="button" v-on:click="markAsIncomplete(item)">Mark as incomplete</button>
</template>
<template v-else>
<button type="button" v-on:click="markAsComplete(item)">Mark as complete</button>
</template>
</td>
</tr>
</tbody>
</table>
So we have a simple table. There’s only one <tr> element inside the <tbody>, but that iterates for every element in the items data array (so in the browser they’ll actually be as many table rows as there are items in the array). If an element is added, the table will be updated to include a row for that element. If an element is removed, the corresponding row will be removed from the table. If an item’s name is updated, it will also be updated in the corresponding row. So we never manipulate the template directly; it updates in response to changes in the data (the state).
So, we can conditionally add a CSS class to each row based on an expression:
<tr v-for="item in items" v-bind:key="item.id" v-bind:class="{ 'is-complete': item.isComplete }">
So now, if the item’s isComplete attribute is true, a CSS class named is-complete will be added to this row. We can see this in action by implementing handlers for the buttons I defined in the template above:
markAsComplete(item) {
this.$set(item, 'isComplete', true);
},
markAsIncomplete(item) {
this.$set(item, 'isComplete', false);
},
The current item in the iteration was passed in as a parameter to this methods, so we can manipulate it and Vue’s reactivity will keep an eye on it. So if you change one of these object’s properties, then again the template will re-render to show the state of those latest changes.
So yeah, I think the takeaway is, don’t think in terms of manipulating the actual DOM and template itself. Instead, build your template that responds shows/hides elements, or adds/removes classes based on state. Instead of thinking, “when X happens, add this class to this element”, instead think, “this element has a class, but it should only be shown when X is true”.