0

I've made a component (is that overkill?) to call recursively in order to render a tree, but it seems to be impossible to handle click events because stuff rendered inside the component can't see the method I want in the Vue's methods; this appears to be some monstrous complication about components and events.

How can I handle these click events? Does it really have to be so difficult?

    <script type="text/html" id="template-tree">
        <ul>
            <c-tree-item v-for="item in items" v-bind:item="item"></c-tree-item>
        </ul>
    </script>

    <script type="text/html" id="template-tree-item">
        <li v-on:click="clickTreeItem"> <!-- click doesn't work :( -->
            <p> {{ item.Name }}</p>
            <ul v-if="item.Items.length > 0">
                <c-tree-item v-for="i in item.Items" v-bind:item="i"></c-tree-item>
            </ul>
        </li>
    </script>

    <script type="text/javascript">
        var vueApp = undefined;

        var ct = Vue.component('c-tree', {
            template: document.getElementById('template-tree').innerHTML,
            props: ['items']
        });
        var cti = Vue.component('c-tree-item', {
            template: document.getElementById('template-tree-item').innerHTML,
            props: ['item']
        });

        $(document).ready(function () {
            var router = new VueRouter({
                mode: 'history',
                routes: []
            });

            vueApp = new Vue({
                router,
                el: '#vue',
                data: {
                    tree: JSON.parse(document.getElementById('data').innerHTML)
                },
                methods: {
                    clickTreeItem: function(event) {
                        console.log('click');
                        console.log(JSON.stringify(event));
                    }
                }
            });
        });
    </script>

<div id="vue">
    <c-tree v-bind:items="tree"></c-tree>
</div>
Richard Barraclough
  • 2,625
  • 3
  • 36
  • 54
  • Can you reproduce in a codepen? – Adam Orłowski Mar 14 '20 at 20:43
  • https://jsfiddle.net/1mspo85g/1/ The click is plumbed i on line 27 on the HTML. Here even the `console.log` causes all sorts of errors. If you change it to `v-on:click="clickTreeItem"` then you get the same _Property or method "clickTreeItem" is not defined on the instance but referenced during render._ error. – Richard Barraclough Mar 14 '20 at 21:24
  • https://jsfiddle.net/1mspo85g/2/ (fixed the `v-for :key` thing) – Richard Barraclough Mar 14 '20 at 21:35
  • (I'm new to Vue as of today, but have used KnockoutJS, KendoUI observables and briefly React (hate it). I'm expecting to populate an editor with the data of the clicked item and have the list item text update when I update the `data` with the edited name.) – Richard Barraclough Mar 14 '20 at 21:38
  • It would be easier to use Vuex in this situation. – Dan Mar 15 '20 at 06:34

1 Answers1

0

A component can only raise events -- which may have data associated with them.

A component has to listen to it's sub-components' events and may, in turn, pass them upwards as its own events.

Example: https://jsfiddle.net/gkch7bnr/1/

StackOverflow is stroppy about JSFiddle so here is some code:

<head>
<script
              src="https://code.jquery.com/jquery-3.4.1.min.js"
              integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
              crossorigin="anonymous"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.common.dev.js" integrity="sha256-soI/D3XnqcarOMK229d8GWs8P+gYViEsbWBeMaRoSPk=" crossorigin="anonymous"></script>

      <script type="application/json" id="data">
        [
          {"Id": 1, "Name": "Name1", "Items": []},
          {"Id": 2, "Name": "Name2", "Items": []},
          {"Id": 3, "Name": "Name3", "Items": [
            {"Id": 31, "Name": "Name31"},
            {"Id": 32, "Name": "Name32"},
            {"Id": 33, "Name": "Name33"}
          ]} 
        ]
    </script>

      <script type="text/html" id="template-tree">
        <ul>
            <c-tree-item v-for="item in items" v-bind:item="item" v-on:click="$emit('click', $event)"></c-tree-item>
        </ul>
    </script>
    <script type="text/html" id="template-tree-item">
        <li>
            <p v-on:click="$emit('click', item)" > {{ item.Name }}</p>
            <ul v-if="item.Items && item.Items.length > 0">
                <c-tree-item v-for="i in item.Items" v-bind:item="i" v-on:click="$emit('click', $event)"></c-tree-item>
            </ul>
        </li>
    </script>
</head>
<body>
  <div id="vue">
    <c-tree v-bind:items="tree" v-on:click="clickTreeItem"></c-tree>
</div>
</body>

var vueApp = undefined;

        var ct = Vue.component('c-tree', {
            template: document.getElementById('template-tree').innerHTML,
            props: ['items'],
            methods: {
                clickTI: function(e) { console.log('clickTI: ' + e.Name);}
            }
        });
        var cti = Vue.component('c-tree-item', {
            template: document.getElementById('template-tree-item').innerHTML,
            props: ['item']
        });

        $(document).ready(function () {
            vueApp = new Vue({
                el: '#vue',
                data: {
                    tree: JSON.parse(document.getElementById('data').innerHTML)
                },
                methods: {
                    clickTreeItem: function(event) {
                        console.log('clickTreeItem: ' + event.Name);
                    }
                }
            });
        });
Richard Barraclough
  • 2,625
  • 3
  • 36
  • 54