0

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.

ATLANT
  • 68
  • 9
  • 2
    It's not a good idea to use classes with composition api, too much pitfalls without any benefits. Probably not specific to classes. You should list object keys in Vue 2 explicitly in order to make them reactive, `reactive({})` won't work. – Estus Flask Oct 04 '22 at 13:32
  • Our architecture is too complex to be stored in VUEX or other state managers. We also have a core that is used in different projects and frameworks, and it is desirable to reuse it. So, i need do this way – ATLANT Oct 04 '22 at 13:39
  • 1
    I don't talk about reuse. But the use of classes specifically, e.g. https://stackoverflow.com/questions/67894487/vue-3-reactivity-not-triggered-from-inside-a-class-instance . Everything you write as CanActiveViewService can be written with plain objects and functions with less problems. As for this problem, it's what's described in the answer indeed – Estus Flask Oct 04 '22 at 13:59
  • Oops, I thought I answered your comment. I got it, thanks for the comment, I'll note :) – ATLANT Oct 07 '22 at 17:08
  • No problems, you're welcome – Estus Flask Oct 07 '22 at 17:16

1 Answers1

1

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.

ATLANT
  • 68
  • 9