1
votes

As far as I can tell, what I'm doing is supposed to work correctly, based off of several examples I've found online, and a general pattern I believe I understood correctly from Wave UI's drawer source code.

Basically I have a sidebar component, and I want it to be hideable according to a value prop supplied to it (the idea being it can be changed according to whatever the parent component needs, screen size, toggle button, etc.). So the sidebar component has a watch function on it that is supposed to react to changes in props.value

Sidebar.vue

<template>
  <aside v-if="mountSidebar" class="min-h-screen">
    <slot></slot>
  </aside>
</template>

<script lang="ts">
import { defineComponent, ref, watch } from "vue";

export default defineComponent({
  name: "Sidebar",
  props: {
    value: {
      type: Boolean,
      default: true,
    },
  },
  setup(props) {
    const mountSidebar = ref(props.value);

    watch(
      () => props.value,
      (value) => {
        mountSidebar.value = value;
      }
    );

    return {
      close,
      mountSidebar,
    };
  },
});
</script>

And then the App.vue where the sidebar is currently displayed.

<template>
  <div>
    <navbar>
          <dm-button rounded type="primary" @click="showSidebar = !showSidebar">Toggle Sidebar</dm-button>
      Navbar Content
    </navbar>
    <div class="flex flex-row min-h-screen bg-gray-100 text-gray-800">
      <sidebar v-model="showSidebar">
        Sidebar Content
      </sidebar>
      <div class="flex justify-center w-full">
        <router-view />
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from "vue";
import Navbar from "@/components/Navbar.vue";
import Sidebar from "@/components/Sidebar.vue";
import DmButton from "@/components/Button.vue";

export default defineComponent({
  components: {
    Navbar,
    Sidebar,
    DmButton,
  },
  setup() {
    const showSidebar = ref(true);

    return {
      showSidebar,
    };
  },
});
</script>

My App.vue has a showSidebar boolean ref, which is supplied as a v-model on the sidebar component. There is also a dm-button (an extended button component I wrote) that toggles the value of showSidebar on click (I know this works, I can see the showSidebar value changing in the App.vue component in the Vue Devtools). However, the change is not reflected in the sidebar component. I'm not sure what I'm missing here.

FYI this code is simplified to have some unnecessary stuff removed (like the content inside the sidebar and navbars for example).

2

2 Answers

0
votes

To use v-model with a component you should name the prop modelValue and emit an event called update:modelValue to mutate it :

<template>
  <aside v-if="modelValue" class="min-h-screen">
    <slot></slot>
  </aside>
</template>

<script lang="ts">
import { defineComponent, ref, watch } from "vue";

export default defineComponent({
  name: "Sidebar",
  props: {
    modelValue: {
      type: Boolean,
      default: true,
    },
  },
emits:['update:modelValue'],
  setup(props,{emit}) {
  

      function close(){
         emit('update:modelValue',false)
      }

    return {
      close,
      mountSidebar,
    };
  },
});
</script>
0
votes

By creating a new ref from props.value you lost the reactivity. props is a reactive, so you can use toRef or toRefs.

const mountSidebar = toRef(props, 'value');
// or
const {value:mountSidebar} = toRefs(props);