0

I've been teaching myself Vue.js, and have been utilising components to increase modularity.

One thing that I am struggling with is manipulating variables in the Vue instance from within the component. I have got it working well with v-model within a component by passing the variable in the jade file as a prop

eg loginform(slot="login-form" v-bind:form-submit="loginSubmit" v-bind:login-data="loginData")

Where loginData contains variables username & password which are 'v-modelled' to the inputs within the component. Then in the component template:

<input type="password" class="text-field" v-model="formData.password" />

However I have a tooltip component that I am wanting to use twice: One for the username field & one for the password field. The visibility of these tooltips are given by tooltips.username.vis and tooltips.password.vis respectively.

I can't seem to pass that variable as a prop in order to manipulate without getting the avoid manipulating props warning, despite v-model within the component not giving these warnings. The tooltip component is given below:

Vue.component('tooltip', {
  props: ['show', 'message', 'click'],
  template:
    <transition name="shrink">
      <div v-show="show" v-on:click="click" class="tooltip">
        <div class="tooltip-arrow"></div>
        <div class="tooltip-container">{{message}}</div>
      </div>
    </transition>
}); 

Does anyone have any idea on how I can achieve the desired affect (Hiding the tooltip on mouse click). I have tried passing a method as the click prop that has different arguments based on whether the tooltip is for the username or password input, however I get click undefined warnings. I could make two seperate functions but I would rather not explicitly write two functions that do the same thing.

2
  • Your username/password v-model won't give the warning because you're passing an object for the prop and then updating a property within that object. The prop is still referencing the same object. If you'd passed them without the wrapper object you'd have seen the same warning. Commented Apr 21, 2018 at 9:17
  • @skirtle ahh thanks! you just answered my comment on the below answer. Thanks for clarifying :) Commented Apr 21, 2018 at 9:20

1 Answer 1

2

You shouldn't attempt to modify props from within a component as Vue's warnings tell you, changes to props do not flow up from the component to the prop so any changes will be overwritten.

For what you're trying to achieve you should look into Vue's Custom Events https://v2.vuejs.org/v2/guide/components-custom-events.html

HTML

<div id="app">
   <form>
        <div>
           <label>Username</label>
           <input type="username" v-model="formData.username" />
           <tooltip :show="tooltips.username.vis" 
    :message="tooltips.username.message" @tooltip:hide="tooltips.username.vis = false" />
        </div>

        <div>
           <label>Password</label>
           <input type="text" v-model="formData.password" />
           <tooltip :show="tooltips.password.vis" 
   :message="tooltips.password.message" @tooltip:hide="tooltips.password.vis = false" />
        </div>
   </form>
</div>

JS

Vue.component('tooltip', {
  props: ['show', 'message'],
  template: `<transition name="shrink">
      <div v-show="show" class="tooltip" @click="hide">
        <div class="tooltip-arrow"></div>
        <div class="tooltip-container">{{message}}</div>
      </div>
    </transition>`,
  methods: {
    hide () {
      this.$emit('tooltip:hide');
    },
  }
}); 

new Vue({
    el: "#app",
    data: {
      formData: {
        username: '',
        password: ''
      },
      tooltips: {
        username: {
            message: 'Fix your username',
            vis: true
        },
        password: {
            message: 'Fix your password',
            vis: true
        }
    }
    }
});

https://jsfiddle.net/10fjkoob/12/

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

6 Comments

Oh thanks that answers it perfectly. I just noticed beforehand that when I pass the visibility variable (tooltips.username.show) directly as a prop and then try to edit it, it gives me a avoid manipulating prop warning. However if I pass the tooltips.username object as a whole (say as v-bind:tooltip, and then use `<... v-on:click="tooltip.show = false">, then it works without giving any errors (and changes the original show variable in the Vue instance). I'm guessing this is still bad practice, but do you happen to know why this works whereas the former doesn't.
I'm not entirely sure, it may have to do with how Vue's observables work, they probably aren't attached to each property within the object. If the component was re-rendered then I imagine that the modified object would be replaced by the unmodified version from the parent. Something else might be able to chime in with more detail but I would definitely avoid doing that.
When replicating the method you posted in jsfiddle it works fine, but when using it in browser, clicking a tooltip will hide it, but will also seem to temporarily hide the other tooltip (for around half a second - after which it re appears), and after that point further tooltip clicks will not do anything (on either the username or password tooltips). Could this be something with the custom @tooltip:hide event?
Tested in Chrome and Safari. I loaded your one and it worked fine so ill have to go through my code and see where I've gone wrong. Thanks for the help! :)
Incase anyone has the same problem as above, I realised I had another component in a different Vue app that was called tooltip from earlier testing. Moving/merging the tooltip components (they were near identical) into one global component in a seperate javascript file fixed the problem and allowed the tooltip to be used within both seperate apps
|

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.