typescriptvuejs3vue-component

Why are child nodes not triggering an emit event in a tree?


I have a Vue component that recursively generates a tree recursively with a <CustomTreeNode> component. It looks something like this:

<template>
  <div>
      <div>
        <span @click="nodeClicked($event, localValue)">
          {{ localValue.name }}
        </span>
      </div>

      <div v-if="open && value.children.length > 0">
        <!-- recursive component -->
        <CustomTreeNode
          v-for="citem in localValue.children"
          :key="citem.id"
          :value="citem"
          :selected="citem.selected"
          @node-clicked="nodeClicked($event, citem)"
        />
      </div>
    </div>
</template>

The handler on the TreeNode component emits the event, which is in turned handled by the page hosting the Tree.

const nodeClicked = (e: Event, node: ITreeNode) => {
  console.log(`KTREENODE: nodeClicked() called with item ${node.name}`)  // <------- always invoked
  emit('node-clicked', [e, node])  // <<-------- only invoked for root nodes, not children
  console.log(`    KETREENODE: after emit() called after [${node.name}] node clicked`)
}

The tree renders just fine, and when I click on a top-level node the events are passed up to the host and I can manage. In the handler for the node-clicked event in parent <CustomTree> component and its host. HOWEVER, when I click on a child item, the nodeClicked event handler method is called, and both console.log statements are printed. But the emit() just doesn't happen.

Any ideas why?

Note: the @node-clicked="nodeClicked($event, citem)" doesn't seem to make much difference, it just causes the handler to be called twice.

Problem is replicated here on Play.vuejs

After @yuanyxh's answer, I here's an updated Vue SFC Playground link that shows adding a handler on the child node. In this case, children's info are passed up the hierarchy, but not grandchildren.


Solution

  • When you recursively invoke this CustomTreeNode.vue component, you should propagate events down the hierarchy:

    // CustomTreeNode.vue
    
    <script setup lang="ts">
    // other code here...
    
    const childNodeClicked = (args: [Event,  ICustomTreeNode]) => {
      emit('node-clicked', [args[0], args[1]])
    }
    </script>
    
    <template>
      // other code here...
      <CustomTreeNode
        // other code here...
    
        @node-clicked="childNodeClicked($event)"
      />
    </template>
    

    Additionally, when you pass parameters as an array:

    emit('event-name', ['args1', 'args2'])
    

    The event handler also receives parameters as an array:

    const eventHandler = (args: [string, string]) => {};