Reputation: 1476
Simple use case: Specific elements should get an active class by setting property "active" to true (v-bind:class). The property "active" is set within a foreach loop, after checking some conditions, within method "handleScroll".
This works, if I call "handleScroll" directly in created lifecycle.
<template>
<div class="c-sidebar">
<p
v-for="(sidebarItem, index) in sidebarItems"
v-bind:key="index"
v-bind:class="{ 'active': sidebarItem.active }"
>
{{ sidebarItem.label }}
</p>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator";
import {SidebarItem, SidebarNavItem} from "../../Interfaces";
@Component
export default class Sidebar extends Vue {
sidebarItems: SidebarItem[];
public created() {
this.sidebarItems = [
{
label: "First item",
active: true
},
{
label: "Second item",
active: false
}
];
this.handleScroll();
}
public handleScroll() {
this.sidebarItems.forEach((sidebarItem: SidebarItem, index) => {
if (index == 1) {
sidebarItem.active = true;
} else {
sidebarItem.active = false;
}
});
}
}
</script>
If I call "handleScroll" from within a window event, reactivity gets lost.
Change
public created() {
...
this.handleScroll();
}
to
public created() {
...
window.addEventListener("scroll", this.handleScroll);
}
does not work. The method is executed, but reactivity in template gets lost.
Question: How to I set these properties in a global window event and assign them back to view?
Upvotes: 0
Views: 284
Reputation: 794
Try setting the value with Vue.set, as explained in the caveats section, like this:
Vue.set(sidebarItems[index], 'active', true)
or
Vue.set(sidebarItems, index, { ...sidebarItems[index], { active: true})
Upvotes: 1
Reputation: 1729
You'll have to use either a computed property or a function which directly gets called from the vue in order to reflect changes. Below code should work -
<template>
<div class="c-sidebar">
<p
v-for="(sidebarItem, index) in sidebarItems"
:key="index"
:class="getActiveClass(index)"
>
{{ sidebarItem.label }}
</p>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator";
import {SidebarItem, SidebarNavItem} from "../../Interfaces";
@Component
export default class Sidebar extends Vue {
sidebarItems: SidebarItem[];
public created() {
this.sidebarItems = [
{
label: "First item",
active: true
},
{
label: "Second item",
active: false
}
];
}
public getActiveClass(index: number) {
return index === 1 ? 'active': '';
}
}
</script>
Upvotes: 0
Reputation: 1935
There's no obviously good reason this wouldn't work, based solely on the code shown here. Vue's reactivity caveat is regarding adding/removing array items, not changing attributes.
I would make triple sure that the method is actually being run, with some console.log:
this.sidebarItems.forEach((sidebarItem: SidebarItem, index) => {
if (index == 1) {
sidebarItem.active = true;
console.log('on: ' + sidebarItem)
} else {
sidebarItem.active = false;
console.log('off: ' + sidebarItem)
}
});
If that doesn't give your answer, it'd be best to recreate your problem in jsfiddle. That'll probably show you what you need. If it doesn't, you can post the fiddle here.
Upvotes: 0
Reputation: 22413
It might be Vue reactivity problem.
Please try changing object reference by creating a deep copy using JSON.parse(JSON.stringify())
public handleScroll() {
this.sidebarItems.forEach((sidebarItem: SidebarItem, index) => {
if (index == 1) {
sidebarItem.active = true;
} else {
sidebarItem.active = false;
}
});
this.sidebarItems = JSON.parse(JSON.stringify(this.sidebarItems))
}
Upvotes: 1