0

I wanted to refactor the code from this tutorial about render functions to use Composition API. Everything is working fine except context.emit("update:activeTabId", tab.props.tabId); line in onClick function. Console logging the tab.props.tabId shows that onClick function works and properly read the tabId, but the update:activeTabId doesn't update the activeTabId value. Is there another way of emitting the event with sync modifier in Composition API or am I doing something wrong?

Here is my code:

---- App.vue ----

<template>
<!-- eslint-disable-next-line -->
  <tab-container v-model:activeTabId="activeTabId">
    <tab tabId="1">Tab #1</tab>
    <tab tabId="2">Tab #2</tab>
    <tab tabId="3">Tab #3</tab>

    <tab-content tabId="1">Content #1</tab-content>
    <tab-content tabId="2">Content #2</tab-content>
    <tab-content tabId="3">Content #3</tab-content>
  </tab-container>
</template>

<script>
import {ref} from 'vue'
import {Tab, TabContent, TabContainer} from './components/tabs.js';

export default {
  components: {
    Tab, 
    TabContent,
    TabContainer
  },
  setup() {
    const activeTabId = ref('1');

    return {
      activeTabId,
    };
  },
};
</script>

--- tabs.js ---

import { h } from "vue";

export const TabContainer = {
  props: {
    activeTabId: {
      type: String,
      required: true,
    },
  },
  emits: ['update:activeTabId'],
  setup(props, context) {
    const $slots = context.slots.default();
    const tabs = $slots
      .filter((x) => x.type === Tab)
      .map((tab) =>
        h(tab, {
          class: {
            tab: true,
            active: props.activeTabId === tab.props.tabId,
          },
          onClick: () => {
            console.log(tab.props.tabId)
            context.emit("update:activeTabId", tab.props.tabId);
          },
        })
      );

    const content = $slots.find(
      (slot) =>
        slot.props.tabId === props.activeTabId && slot.type === TabContent
    );

    return () => [
      h(() => h("div", { class: "tabs" }, tabs)), 
      h(() => h("div", content))
    ];
  },
};

const tabItem = (content) => ({
  ...content,
  props: {
    tabId: {
      type: String,
      required: true,
    },
  },
  setup(_, context) {
    return () => h("div", context.slots.default())
  }
});

export const Tab = tabItem({ name: "Tab" });
export const TabContent = tabItem({ name: "TabContent" });
bukacz
  • 25
  • 6
  • I’ve did a quick test and your code seems to work, the parent model is correctly being updated for me: https://codesandbox.io/s/flamboyant-brook-46r62?file=/src/App.vue – LeBen Dec 29 '20 at 12:27
  • Thank you @LeBen for your response. Yes, value of the `activeTabId` is being updated in the App.vue, but somehow new value is not being passed to the `TabContainer`. It seems as `TabContainer` is rendered only once and never updates. On tab click `Content` value should change. – bukacz Dec 29 '20 at 15:43

1 Answers1

0

After some research, I've found that activeTabId is being updated but TabContainer somehow doesn't consider it reactive value so it wasn't changing. I've wrapped all TabContainer logic with watchEffect watching for activeTabid changes and it now works as it should!

https://codesandbox.io/s/solitary-currying-50ick?file=/src/App.vue

bukacz
  • 25
  • 6