Wildhoney
Wildhoney

Reputation: 4969

Passing in two-way binding prop to a slot

I'm somewhat new to Vue, and I'm having particular difficulty in passing in formData to my individual child nodes. Ideally each child node simply updates the parent formData object allowing me to submit the form data later on as a whole.

I've setup a JSFiddle to illustrate: https://jsfiddle.net/3nm1mrLo/

My current thinking is that I should v-bind:field="formData.name" but that throws an error. It seems as though formData doesn't exist in the slot based HTML.

Any pointers would be gratefully received. Thanks!

Upvotes: 3

Views: 2644

Answers (1)

Mani Jagadeesan
Mani Jagadeesan

Reputation: 24265

As you rightly said, you need to use v-bind:field="formData.name" or :field="formData.name".

It is not working because you have defined the main app template directly in your html, and used "content distribution" to include <example-form> and <example-input>.

As defined in the docs, this content-distribution (or "transclusion" if you are familiar with Angular 1.x) works fine, but the scope belongs to the main app (the root instance, because the template belongs to it).

Ref: https://v2.vuejs.org/v2/guide/components.html#Compilation-Scope

Quote:

A simple rule of thumb for component scope is:

Everything in the parent template is compiled in parent scope; everything in the child template is compiled in child scope.

If you are curious, try changing your main app (root instance) as follows:

new Vue({
    el: '*[my-app]',
    data: function() {
        return {
            formData: { name: 'abc', location: 'xyz'}
        };
    }
});

Now you will see that formData is not undefined anymore.

But a more proper method is to include <example-input> as part of the template of example-form component as follows:

Vue.component('example-form', {
    template: `
        <div class="my-example-form">
            <form><pre>{{formData}}</pre><slot></slot></form>
            <example-input :field="formData.name"></example-input>
            <example-input :field="formData.location"></example-input>
        </div>
    `,
    data: function() {
        return {
            formData: { name: '', location: ''}
        };
    }
});

Now it will bind to the right formData as you would expect.

But this will still not work for you because props is a one-way binding only. The child component (example-input) will get the value, but will not pass the data changes back to parent component (example-form)

For child to pass data back to parent, the right way is to use $emit as explained in this question: Updating parent data via child component?

If you want to have an <example-input> component to work as form elements, here is a related answer which works like what you expect: https://stackoverflow.com/a/40337942/654825 - there is a working jsFiddle also.

Upvotes: 3

Related Questions