1

I have the need to create a dynamic form, which builds up in a tree kind of way. The form can change at any time by the user (who creates/designs the form) and so the inputs I have change dynamically too.

Example:

root
--groeisnelheid
----niveau
------beginner (input radio)
------recreatief (input radio)
------competitie (input radio)
------tour (input radio)
----input text
----begeleiding
------another input
------and another
--another category
----speed
------input
------input

As you can see, not the easiest form... The user (admin user in this case) has the ability to edit or create new forms.

I probably have underestimated the job, since I am trying to create the input side of it, and am already struggling.

What I have done so far:

TreeComponent.vue

<template>
    <div class="tree">
        <ul class="tree-list">
            <tree-node :node="treeData"></tree-node>
        </ul>
    </div>
</template>

<script>
export default {
    props: {
        treeData: [Object, Array]
    },

    data() {
        return {
            treeValues: []
        };
    },

    methods: {
        sendForm: function() {}
    }
};
</script>

TreeNodeComponent.vue

<template>
    <li v-if="node.children && node.children.length" class="node">
        <span class="label">{{ node.name }}</span>

        <ul>
            <node v-for="child in node.children" :node="child" :key="child.id"></node>
        </ul>
    </li>
    <div v-else class="form-check form-check-inline">
        <input
            type="radio"
            class="form-check-input"
            :name="'parent-' + node.parent_id"
            :id="node.id"
        />
        <label for class="form-check-label">{{ node.name }}</label>
    </div>
</template>

<script>
export default {
    name: "node",
    props: {
        node: [Object, Array]
    }
};
</script>

This results in all the inputs showing up as I want. But now the real question is; how do I get the value of these inputs in my root component (TreeComponent.vue), so I can send this to the server. Either on change or when the user proceeds in the form.

I am used to working with v-model on this, but I have no clue on how to use this on recursive components, since the documentation only covers setting the data of the direct parent.

Any help would be much appreciated.

2 Answers 2

1

One way of doing this is to pass a prop down from TreeComponent to each node.

<template>
  <div class="tree">
    <ul class="tree-list">
        <tree-node :node="treeData" :form="formRepo"></tree-node>
    </ul>
  </div>
</template>

Then each node passes the prop down to its children.

 <node v-for="child in node.children" :node="child" :key="child.id" :form="form"></node>

This way each node will have a direct reference to the TreeComponent.

In each node you can watch the model and update the form prop. You need to use your child.id so you know which field is which.

Your formRepo can be a fully fledged object, but a hash could work just as well.

data() {
    return {
        treeValues: [],
        formRepo: {
        }
    };
}

Note: if you want formRepo to be reactive you'll need to use Vue.set to add new keys to it.

Sign up to request clarification or add additional context in comments.

Comments

0

Thank you for your answer.

I managed to solve it by posting the data to the server on every change, since this was a handy feature.

The way I did so was:

Call function from the input (same call for text inputs)

<input
    type="radio"
    class="form-check-input"
    :name="'parent-' + node.parent_id"
    :id="node.id"
    @input="change($event, node.id, node.parent_id)"
/>

Have some data variables to fill (Route is required for the axios request)

data() {
    return {
        input: {
            id: null,
            parentId: null,
            radio: false,
            value: null
        },
        route: null
    };
},

And then some magic. The change method. (Left the axios bit out.)

methods: {
    change: function(event, id, parentId) {
        this.input.parentId = parentId;
        this.input.id = id;

        if (event.target.value === "on" || event.target.value === "off") {
            this.input.radio = true;
            this.input.value = event.target.value === "on" ? true : false;
        } else {
            this.input.value = event.target.value;
        }

        if (this.input.value) {
            axios.put().then().catch()
        }
    }
}

I know there is some room of improvement in the validation bit. If a user enters 'on' in a text field, this will probably fail. So there is work to be done, but the basic filling of a form is working.

As to if this is the best way, I have no clue, since I'm new to Vue.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.