I have a logical component that doesn't have a template, but only stores the active/not active state.
So. It works, but without ID.
I use the id when necessary to make the block's state consistent with other blocks that may be active.
For example, when I click on a menu or product catalog button, their body is displayed in the same place. To avoid layering, I say that when the menu is enabled, the product catalog is hidden. And vice versa. The catalog turned on - the menu was hidden.
For each entity that can be active, I assign an id, and a list of exclusion ids. The list is deactivated by id.
The component does not see that the Service changes its state. In devtools, I need to click the "refresh" button to see the new state of the service.
Here is the code.
Menu.vue template:
<template>
<section class="menu" :style="{'--sticky-bottom': StickyBottom + 'px'}">
<CanActive id="header-menu" :excludeIds="['header-catalog']" v-slot="{isActive, toggleShow}">
<div class="menu__bg" :class="{active: isActive}"></div>
<button class="menu__btn"
:class="{active: isActive}"
@click="toggleShow"
>
<span></span>
</button>
<MenuBody class="menu__body"
:class="{active: isActive}"
:style="{'--sticky-bottom': StickyBottom + 'px'}"
/>
</CanActive>
</section>
</template>
CanActive.vue:
<template>
<div class="logical">
<slot :isActive="IsActive" :toggleShow="toggleShow"></slot>
</div>
</template>
<script lang="ts">
import {Component, Vue} from "~/tools/version-types";
import {Prop} from "vue-property-decorator";
import {canActiveViewService} from "@shared/services/ui/view/canActive.view.service";
@Component({
name: "CanActiveComponent",
})
export default class CanActiveComponent extends Vue {
@Prop({default: false}) startValue: boolean;
@Prop({default: null}) id: string | null;
@Prop({default: []}) excludeIds: string[];
isShowInner: boolean = false;
mounted() {
this.IsActive = this.startValue;
}
toggleShow() {
this.IsActive = !this.IsActive;
}
close() {
this.IsActive = false;
}
get Service() {
return canActiveViewService;
}
get IsActive() {
if (this.id) {
return this.Service.state[this.id];
} else {
return this.isShowInner;
}
}
set IsActive(value) {
if (this.id) {
this.Service.setIdValue(this.id, value, this.excludeIds)
} else {
this.isShowInner = value;
}
}
}
</script>
<style lang="scss" scoped>
.logical {
display: contents;
}
</style>
CanActiveViewService:
import {reactive} from "vue";
class CanActiveViewService {
// reactive - try to fix problem
state: Record<string, boolean> = reactive({});
setIdValue(id: string, val: boolean, excludedIds: string[]) {
this.state[id] = val;
excludedIds.forEach((id) => {
this.state[id] = false;
});
console.dir(this.state);
}
}
export const canActiveViewService = reactive(new CanActiveViewService());
This trick works on Vue 3, why doesn't it work here?
I am using Vue 2 and Nuxt.
Oh God, I got it 🤦♂️
A very important difference between Vue 2 and Vue 3 is that
in Vue 2 we have to use Vue.set(object, id, val);
for reactivity.
That is, the service
code should look like this.
import {reactive} from "vue";
import {Vue} from "~/tools/version-types";
class CanActiveViewService {
state: Record<string, boolean> = {};
setIdValue(id: string, val: boolean, excludedIds: string[]) {
Vue.set(this.state, id, val);
excludedIds.forEach((id) => {
Vue.set(this.state, id, false);
});
}
}
export const canActiveViewService = reactive(new CanActiveViewService());
Now it works.