Home > Mobile >  Vue.js 2: How the reusing element logic works in `v-if` `v-else-if` (Why it does not work on nested
Vue.js 2: How the reusing element logic works in `v-if` `v-else-if` (Why it does not work on nested

Time:01-07

I have this code: (Note the structure of inputs and labels and v-ifs and v-elses)

new window.Vue({
  el: "#app",

  template: `
    <div>
      This will reset:
      <div v-if="loginType === 'username'">
        <label>Username: <input/></label>
      </div>
      <div v-else>
        <label>Email: <input/></label>
      </div>

      <br />

      This will not:
      <div v-if="loginType === 'username'">
        <label>Username: </label>
        <input/>
      </div>
      <div v-else>
        <label>Email: </label>
        <input/>
      </div>

      <br />

      This will reset too:
      <div v-if="loginType === 'username'">
        <label>Username: </label>
        <input key="input-for-username"/>
      </div>
      <div v-else>
        <label>Email: </label>
        <input key="input-for-email"/>
      </div>

      <br />

      <button @click=changeLoginType>Change Username</button>
    </div>`,

  data: () => {
    return { loginType: "email" };
  },

  methods: {
    changeLoginType() {
      if (this.loginType === "email") this.loginType = "username";
      else this.loginType = "email";
    }
  }
});

Executable CodeSandbox: https://codesandbox.io/s/jovial-elion-cntkin?file=/src/index.js

This code renders a switchable input (switches between "email" input and "username" input). I got this code from Vue.js 2 documentation: https://v2.vuejs.org/v2/guide/conditional.html#Controlling-Reusable-Elements-with-key

The docs say VUe.js is reusing the input element in v-if block and v-else block, so the value user entered stays the same. It does not reset when you switch between username and email. But in my code, the first example's input is reset on toggle. Is that because Vue.js only keeps the first-level direct-child identical elements (label in this case)? I mean it changes every attribute and all HTML content of it and just not the element itself?

I tried playing with it a little bit and I'm trying to find a way to tell Vue.js to reuse the input in the code excerpt below:

<div v-if="loginType === 'username'">
  <label>Username: <input/></label>
</div>
<div v-else>
  <label>Email: <input/></label>
</div>

(by reuse I mean if I switch the loginType I except the value of the input stays the same as user entered.

CodePudding user response:

In MVVM world the better option is not to rely on framework in sence that it re-uses things like we see it, but to use "VM" and explicitly bind data in component templates. The idea is not to re-use DOM as much as possible (actually, I never rely on that), but "to connect" input values to each other so no matter if they're re-used or not, visually they'll always display same data

What you have to do is to define state property in component and attach it to both inputs using "v-model" directive:

<template>
    <div v-if="loginType === 'username'">
        <label>Username: </label>
        <input v-model="userLogin" />
    </div>
    <div v-else>
        <label>Email: </label>
        <input v-model="userLogin" />
    </div>

    <button @click="changeLoginType()">Change Username</button>
</template>

<script>
export default {
    name: 'App',
    data: () => {
        return { loginType: 'email', userLogin: '' };
    },
    methods: {
        changeLoginType() {
            if (this.loginType === 'email') this.loginType = 'username';
            else this.loginType = 'email';
        },
    },
};
</script>

In this scenario no metter which of options you choose, your input value would always take value from component state and changes in input would be reflected in state (so-called 2-way data binding)

Also, interesting thing here is that when I played with initial code sample in newly created application using Vue 3 - input value was always reset when clicking the button and changing login type. So, no matter how it behaves in Vue 2, in newer versions of Vue things could change and previously working code could fail, so it's definitely better to define things explicitly in terms of long-term project support

CodePudding user response:

The key attribute is key when using v-for but it also helps with v-if/v-else. It prevents Vue from reusing the same DOM node.
You should try something like this:

<template>
  <div>
    <label for="account">{{ loginType === 'username' ? 'Username' : 'E-mail' }}</label>
    <input id="account" v-model.trim="usernameOrEmail" :type="loginType === 'username' ? 'text' : 'email'">
  </div>
</template>

CodePudding user response:

As in Vue, You have to pass the inputs value from View to Model or vice versa (two way data binding) and to achieve this you need v-model directive of Vue.

Now, To achieve the requirement you have to just bind the input value to a variable and then reset/clear it on the changeLoginType() method.

Live Demo :

new Vue({
  el: '#app',
  data: {
    loginType: "email",
    inputVal: ''
  },
  methods: {
    changeLoginType() {
      this.inputVal = '';
      this.loginType = this.loginType === 'email' ? 'username' : 'email';
    }
  }
})
<div id="app">
  <div v-if="loginType === 'username'">
    <label>Username: </label>
    <input v-model="inputVal"/>
  </div>
  <div v-else>
    <label>Email: </label>
    <input v-model="inputVal"/>
  </div>
  <button @click=changeLoginType>Change Username</button>
</div>

  • Related