The Architect
The Architect

Reputation: 11

How to do two way binding in vuejs with custom component in shopware?

I am trying to add two way binding to custom admin component in shopware.

<range-slider v-model="vuejsvar"></range-slider>

The problem is the v-model variable vuejsvar is not getting updated, as I change the value of the input range element specified below.

The template twig file.

 <% range_slider %>
 <template>
    <div class="range-slider">
      <label v-if="hasLabelSlot">
      <slot name="label">
      </slot>
      </label>
      <input type="range" min="0" max="100" step="1"  :value="currentValue" :name="name" v-bind="$attrs" @change="onChange" v-on="additionalListeners">
      <div class='range-slider__progress'>{{ currentValue }}</div>
    </div>
</template>
<% endblock %>

The issue in the below code.

 watch: {
        value(value) {
            this.currentValue = value;
        }
    },

This code updates the value for vuejsvar as [Object event] as i move the range slider. I event tried the following.

  watch: {
            value(event) {
                this.currentValue = event.target.value;
            }
        },

Still updates vuejsvar variable as [Object event].

Full code

The index.js file

const { Component, Mixin } = Shopware;
const { Criteria } = Shopware.Data;
import template from './range-slider.html.twig'; // template which will show data 
Shopware.Component.register('range-slider', {
    template: template,
    inheritAttrs: false,
    props: {
        value: {
            required: true,
        },
        name: {
            type: String,
            required:true,
            default:''
        }
    },
    data() {
        return {
            currentValue: this.value,
        };
    },
    computed: {
        hasLabelSlot() {
            return (this.$slots['label']);
        },
        additionalListeners() {
            const additionalListeners = Object.assign({}, this.$listeners);
            delete additionalListeners.change;
            return additionalListeners;
        },
    },
    watch: {
        value(value) {
            this.currentValue = value;
        }
    },
    methods: {
        onChange(event) {
            this.$emit('change', event.target.value );
        },
    }
});

I based my code of https://github.com/shopware/administration/tree/trunk/Resources/app/administration/src/app/component/form/sw-text-field

Upvotes: 1

Views: 419

Answers (1)

Tolbxela
Tolbxela

Reputation: 5183

If you want to use v-model then you have to fire the input event.

Check the Vue Docs: Using v-model on Components

<input v-model="searchText">

Is the same as:

<input
  v-bind:value="searchText"
  v-on:input="searchText = $event.target.value">

So, you have to use the input event or catch the change event yourself.

Like this:

<range-slider :value="vuejsvar" @change="onChange"></range-slider>

and

methods: {
    onChange(event) {
            this.vuejsvar = event;
        }
    }

But than you don't need the v-model at all.

Check the playground:

 var RangeSlider = {     
    template: '#range-slider', 
    props: {
        value: {
            required: true,
        },
        name: {
            type: String,
            required:true,
            default:''
        }
    },
    data() {
        return {
            currentValue: this.value,
        };
    },
    watch: {
        value(value) {
            this.currentValue = value;
        }
    },
    methods: {
        onChange(event) {
            this.$emit('input', event.target.value );
        },
    }
};
  
var Main = {
    el: '#app',
    components: {
      'range-slider': RangeSlider,
    },
    data() {
      return {
        vuejsvar: 50
      }
    },
    methods: {
        onChange(event) {
            this.vuejsvar = event;
        },
    }
};

Vue.component('range-slider', RangeSlider)
const vm = new Vue(Main);
<html>
<head>
</head>
<body>
<div id="app">
  vuejsvar: {{vuejsvar}}<br />
  range-slider: <range-slider v-model="vuejsvar" name="slider"></range-slider>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<script type="text/x-template" id="range-slider">
<div class="range-slider">
      <label>
      <slot name="label">
      </slot>
      </label>
      <input type="range" min="0" max="100" step="1"  :value="currentValue" :name="name" v-bind="$attrs" @change="onChange" >
      <div class='range-slider__progress'>{{ currentValue }}</div>
    </div></script>
</body>
</html>

Upvotes: 0

Related Questions