Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

vue.js wrapping components which have v-models

I have a 3rd party input component (a vuetify v-text-field).

For reasons of validation i prefer to wrap this component in my own.

my TextField.vue

<template>
    <v-text-field
            :label="label"
            v-model="text"
            @input="onInput"
            @blur="onBlur"
            :error-messages="this.getErrors(this.validation, this.errors)"
    ></v-text-field>
</template>

<script>
    import VTextField from "vuetify/es5/components/VTextField";
    import {vuelidateErrorsMixin} from '~/plugins/common.js';
    export default {
        name: "TextField",
        props: ['label', 'value', 'validation', 'errors'],
        mixins: [vuelidateErrorsMixin], //add vuelidate
        data: function() {
            return {
                'text': this.value
            }
        },
        components: {
            VTextField
        },
        methods : {
            onInput: function(value) {
                this.$emit('input', value);
                this.validation.$touch();
            },
            onBlur: function() {
                this.validation.$touch();
            }
        },
        watch: {
            value: {
                immediate: true,
                handler: function (newValue) {
                    this.text = newValue
                }
            }
        }
    }
</script>

which is used in another component

<template> 
 ...   
  <TextField v-model="personal.email" label="Email" 
        :validation="$v.personal.email" :errors="[]"/> 
   ... 
</template> 
<script> 
      ...imports etc. 
      export default {   ...     
          data: function() {
              return {
                  personal: {
                      email: '',
                      name: ''
                  }
            }      
      },      
      components: [ TextField ] 
    } 
</script>

This works fine but i wonder if there is a much more cleaner approach than to replicate the whole v-model approach again. As now my data is duplicated in 2 places + all the extra (non needed) event handling...

I just want to pass the reactive data directly through to the v-text-field from the original temlate. My TextField doesn't actually need access to that data at all - ONLY notified that the text has changed (done via the @input, @blur handlers). I do not wish to use VUEX as this has it's own problems dealing with input / forms...

Something more close to this...

<template>
    <v-text-field
            :label="label"
            v-model="value" //?? SAME AS 'Mine'
            @input="onNotify"
            @blur="onNotify"
            :error-messages="this.getErrors(this.validation, this.errors)"
    ></v-text-field>
</template>

<script>
    import VTextField from "vuetify/es5/components/VTextField";
    import {vuelidateErrorsMixin} from '~/plugins/common.js';
    export default {
        name: "TextField",
        props: ['label', 'validation', 'errors'], //NO VALUE HERE as cannot use props...
        mixins: [vuelidateErrorsMixin], //add vuelidate
        components: {
            VTextField
        },
        methods : {
            onNotify: function() {
                this.validation.$touch();
            }
        },
    }
</script>

I cannot find anything that would do this.

Using props + v-model wrapping is what i do.

like image 918
hobbit_be Avatar asked Jun 06 '18 15:06

hobbit_be


People also ask

How do you wrap a Vue component?

Place the component you wish to wrap into the template of the wrapper component, add v-bind="$attrs" v-on="$listeners" to that component tag, then add the inner component (and, optionally, inheritAttrs: false ) to the wrapper component's config object.

How do you use v-model components?

Adding v-model to Custom Components To let our component support v-model two-way binding, the component needs to accept a value prop and emit an input event. To support v-model , the component accepts a value prop and emits an input event. With that, the custom component supports v-model two-way binding.

What is the difference between v-model and V bind?

v-model is for two way bindings means: if you change input value, the bound data will be changed and vice versa. But v-bind:value is called one way binding that means: you can change input value by changing bound data but you can't change bound data by changing input value through the element.

What is the use of v-model in Vue JS?

Vue v-model is a directive that creates a two-way data binding between a value in our template and a value in our data properties. A common use case for using v-model is when designing forms and inputs. We can use it to have our DOM input elements be able to modify the data in our Vue instance.


1 Answers

You need to forward the value prop down to the wrapped component, and forward the update event back up (see https://vuejs.org/v2/guide/components.html#Using-v-model-on-Components for more details):

<template>
  <wrapped-component
    :value='value'
    @input="update"
  />
</template>

<script>
  import wrappedComponent from 'wrapped-component'

  export default {
    components: { 'wrapped-component': wrappedComponent },
    props: ['value'],
    methods: {
      update(newValue) { this.$emit('input', newValue); }
    }
  }
</script>

Somewhere else:

<my-wrapping-component v-model='whatever'/>
like image 197
Meekohi Avatar answered Oct 25 '22 02:10

Meekohi