Reuse / reuse of component with reactivity in Vue

9

I'm trying to reuse components in VueJS , one of my attempts is to reuse forms, for example:

I have in several forms of my site the login and password fields, so I created a component called userform :

<template>
    <section>
        <input v-model="login">
        <input v-model="password" type="password">
    </section>
</template>

<script>

export default {
    name: "userform",
    data(){
        return {
            login: 'blabla',
            password: ''
        }
    }
}
</script>

The idea now is to get the userform component created and reuse it on several components to form forms, for example in the login component:

<template>
    <section class="col-md-4 col-md-offset-4">

        <userform></userform>
        <button @click="sendLogin()">
            Logar
        </button>

    </section>
</template>

<script>

import userform from './../../forms/userform.vue'

export default {
    name: "login",
    components: {userform},
    mixins: [userform],
    methods: {
        sendLogin() {
            console.log( "Usuario " +  userform.data().login )
            console.log( "Senha " + userform.data().password )
        }
    }
}
</script>

But the reactivity does not work, when I enter the component login the data of login and password until they are caught, but never change, always the output in the console is:

Usuario blabla
Senha 

How do I make reactivity work in this case? Am I using it the right way? Could you give an example of how this type of reuse is done? I'm having trouble learning this part.

    
asked by anonymous 22.01.2017 / 03:42

1 answer

6

Components are meant to be reused, it would not be necessary to use mixin , which is reuse of component fragments.

You have two problems there:

Mixin Injects values into components, but their values are not shared

Mixin is meant for you not to need to repeat codes on distinct components that have some common function / data, but they are not shared with each other, one, does not change that of the other, Vue does not allow this¹. Since you are using mixin within the login component that < > userform , you are only replicating the userform value within login (you can check out the VueDevTools ).

Butthisisnotthecauseofyourproblem,solet'sgotoit...

Calluserformdirectly,causesyoutoselecttheuserformLoginandnottheuserformcomponentitself.

I'mnotsureifthisisthebehavior,butcallinguserformyou'recallingtheinstantiatedobjectwithincomponents:{userform},sothinktome,andifyouhad2<userform>tags,whichonewouldyouaccess?Reactivityisinherenttoeachcomponent,independently.

[tl;dr]Soittakesthevalueoftheuserformobject,whichiswiththedefaultvalues,whichwillonlychangeifyoumovetheobject,buteventhen,withoutchangethevalueofthecomponents,whicharealreadyotherobjects.

Solution: Reference

One way to work around this problem is to use the references. To use them, you must add ref property to the component and access it using this.$refs.nome_da_ref.nome_do_dado .

Ex (component myComponent.vue):

<template>
  <input v-model='x'>
</template>  
<script>
export default {
  data (){
    return {
      x: 10
    }
  }
}
<script>

Ex (Test.vue component):

<template>
  <my-component ref='meuComponent'>
</template>
<script>
import myComponent from './myComponent'

export default {
  components: { myComponent }
  methods: {
    showData() {
      console.log(this.$refs.x);
    }
  }
}
<script>

As soon as you change the input value of <my-component> the reactivity works and the showData() method will print the value entered.

Result

Some changes need to be trade shows in the login component:

  • Remove mixin
  • Add the reference
  • Change your method to use the reference instead of the object
  • Code:

    <template>
        <section class="col-md-4 col-md-offset-4">
    
            <userform ref='meuForm'></userform>
            <button @click="sendLogin()">
                Logar
            </button>
    
        </section>
    </template>
    
    <script>
    
    import userform from './../../forms/userform.vue'
    
    export default {
        name: "login",
        components: {userform},
        methods: {
            sendLogin() {
                console.log( "Usuario " +  this.$refs.meuForm.login )
                console.log( "Senha " + this.$refs.meuForm.password )
            }
        }
    }
    </script>
    

    This should resolve.

    Recommendations and Conventions

    • If you want to use the same data globally, for several components in the same view, I suggest you take a look at Vuex .

    • Use some prefix in your components, this may prevent later headaches (third-party components with the same name, components already in HTML, etc.). Ex: <lvcsUserForm> or <lvcs-user-form> .

    • Treat components as objects that are by using camelCase in their names during import , and repeat them in the files. This even helps you, since you do not need the name attribute in the component, and it can have your tag written in two ways, with PascalCase or kebab-case , but this is defined by the name used in import , as import MeuForm from './../../forms/userform' can be used as <MeuForm> or <meu-form> .

      * Note: The same does not work for props , they must be named in camelCase and will always be written in kebab-case in the component, eg. <meu-form my-prop="10"> .

    26.01.2017 / 02:30