最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Vue.js v-if not reacting to variable change - Stack Overflow

programmeradmin2浏览0评论

i'm playing around with vue.js and have some struggle in using v-if correctly.

i'm trying to have a conditional template rendering inside a template. in the created method, the variable isloaded is set to true, which should lead the template to be rerendered and let the "Loading Data" message disappear.

However, the log indicated, the delay of 2s works, so the callback is reached, but it looks like i'm not updating the data variables correctly.

I tried already several things and now i changed "true/false" to be strings in order to be closer to the exampled given on the official homepage, but still no success. i hope you can help me and therefor thanks a lot in advance.

please find below the interesting codesnippets:

var step1 = Vueponent('step1', {
  template: '#step1',
  data: function() {
    return {
      accounts: [],
      isloaded: 'false'
    }
  },
  created: function() {
    console.log("created");
    setTimeout(function() {
      console.log("times over");
      this.isloaded = 'true';
      this.accounts = [{
        id: 1234,
        name: "germany"
      }];
    }, 2000);
  },
  methods: {
    loaded: function() {
      console.log(this.isloaded === 'true');
      return this.isloaded === 'true';
    }
  }

})

new Vue({
  el: '#main'
});
<script src="/[email protected]/dist/vue.js"></script>
<template id="step1">
  <div class="step1">
    <h1>Welcome please choose your account:</h1>
    <template v-if="isloaded === 'false'">
      <p>Loading Data</p>
    </template>
    <template v-else>
      <select>
        <template v-for="account in accounts">
          <option :value="account.id">{{ account.name }}</option>
        </template>
      </select>
    </template>

  </div>
</template>

<div id="main">
  <step1></step1>
</div>

i'm playing around with vue.js and have some struggle in using v-if correctly.

i'm trying to have a conditional template rendering inside a template. in the created method, the variable isloaded is set to true, which should lead the template to be rerendered and let the "Loading Data" message disappear.

However, the log indicated, the delay of 2s works, so the callback is reached, but it looks like i'm not updating the data variables correctly.

I tried already several things and now i changed "true/false" to be strings in order to be closer to the exampled given on the official homepage, but still no success. i hope you can help me and therefor thanks a lot in advance.

please find below the interesting codesnippets:

var step1 = Vue.component('step1', {
  template: '#step1',
  data: function() {
    return {
      accounts: [],
      isloaded: 'false'
    }
  },
  created: function() {
    console.log("created");
    setTimeout(function() {
      console.log("times over");
      this.isloaded = 'true';
      this.accounts = [{
        id: 1234,
        name: "germany"
      }];
    }, 2000);
  },
  methods: {
    loaded: function() {
      console.log(this.isloaded === 'true');
      return this.isloaded === 'true';
    }
  }

})

new Vue({
  el: '#main'
});
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<template id="step1">
  <div class="step1">
    <h1>Welcome please choose your account:</h1>
    <template v-if="isloaded === 'false'">
      <p>Loading Data</p>
    </template>
    <template v-else>
      <select>
        <template v-for="account in accounts">
          <option :value="account.id">{{ account.name }}</option>
        </template>
      </select>
    </template>

  </div>
</template>

<div id="main">
  <step1></step1>
</div>

Share Improve this question asked Jan 31, 2017 at 14:39 Manuel JainManuel Jain 7511 gold badge6 silver badges16 bronze badges 1
  • This won't fix your issue but may prevent further issues down the line. Since isLoaded is a boolean, you should treat it as such. 'true' and 'false' should just be true and false – ZeroBased_IX Commented Jan 31, 2017 at 14:44
Add a comment  | 

4 Answers 4

Reset to default 6

From the Vue.js - Reactivity in Depth docs:

For example, when you set vm.someData = 'new value', the component will not re-render immediately. It will update in the next “tick”, when the queue is flushed.

With the data being mutated in the created lifecycle hook, we need to use $nextTick More info on that can be found in the above page.

var step1 = Vue.component('step1', {
  template: '#step1',
  data: function() {
    return {
      accounts: [],
      isloaded: false
    }
  },
  created: function() {
    console.log("created");
    var self = this;
    setTimeout(function() {
      console.log("times over");
      self.$nextTick(function() {
        self.isloaded = true;
        self.accounts = [{
          id: 1234,
          name: "germany"
        }];
      })

    }, 2000);
  }

})

new Vue({
  el: '#main'
});
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<template id="step1">
  <div class="step1">
    <h1>Welcome please choose your account:</h1>
    <template v-if="isloaded===false">
      <p>Loading Data</p>
    </template>
    <template v-else>
      <select>
        <template v-for="account in accounts">
          <option :value="account.id">{{ account.name }}</option>
        </template>
      </select>
    </template>

  </div>
</template>

<div id="main">
  <step1></step1>
</div>

However, if you place the code that is loading the data inside a method, and call that method from the created hook. It works without $nextTick.

methods: {
    loadData: function() {
        var self = this;
        setTimeout(function() {
            console.log("times over");
            self.isloaded = true;
            self.accounts = [{
                id: 1234,
                name: "germany"
            }];
        }, 2000);
    }
},
created: function() {
    console.log("created");
    this.loadData();
}

The scope of this is incorrect. The function () syntax creates its own scope. You can save the correct scope in one var and use that, like following:

created: function() {
  var that = this
  setTimeout(function() {
    console.log("times over");
    that.isloaded = 'true';
    that.accounts = [{
      id: 1234,
      name: "germany"
    }];
  }, 2000);
},

Alternatively, you can use arrow function, which does not bind it's own this, arguments, super, or new.target.

created: function() {
  setTimeout(() => {
    console.log("times over");
    this.isloaded = 'true';
    this.accounts = [{
      id: 1234,
      name: "germany"
    }];
  }, 2000);
},

Sometimes this can be caused by an error in the template. For example, I used an undefined method in the v-else case, and that's why looks like v-if is not reactive. The problem will be solved if you remove the undefined method in v-else or define that method.

Note: Even if the method is defined on the instance, be sure to check that you are sending a valid value as parameter.

new Vue({
  el: '#app',
  data(){
    return {
      isLoading: true
    }
  },
  mounted() {
    setTimeout(() => {
      this.isLoading = false
    }, 2000)
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <template v-if="isLoading">
    Loading...
  </template>
  <template v-else>
    loading finished
    
    {{ an_undefined_method() }}
  </template>
</div>

Update your method to have just one =, and remove the return.

methods: {
  loaded: function() {
    console.log(this.isloaded === 'true');
    this.isloaded = 'true';
  }
} 
发布评论

评论列表(0)

  1. 暂无评论