title: Vue.js issue with access to properties of dynamically generated (based on Vuex data) table cells
body: <p><a href="https://i.stack.imgur.com/CaTz4.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/CaTz4.png" alt="Gantt Table"></a></p>
  
  <p>And after adding the hint header<br>
  <code>&lt;h4&gt;Click to change the Table's Mode and to wait for 2 second&lt;/h4&gt;</code><br>
  I see different unexpected results:</p>
  
  <p><a href="https://i.stack.imgur.com/E90wM.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/E90wM.png" alt="Variance 2"></a></p>
  
  <p><a href="https://i.stack.imgur.com/ZkbfK.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/ZkbfK.png" alt="Variance 3"></a></p>
  
  <p>For emulation of delay with asynchronous computed property (similar to receiving data from Vuex or AJAX request) I use <strong><em>sleepForDebug()</em></strong> function:</p>
  
  <pre><code>sleepForDebug(delay) {
    var start = new Date().getTime();
    while (new Date().getTime() &lt; start + delay);
  }
  </code></pre>
  
  <p>I have couple of questions:<br>
  1. Why watch: <strong><em>newTasks(tasks)</em></strong> doesn't trigger first time and the line doesn't appear<br>
  2. Why <code>let startRef = "cell"+task.start;</code> is <strong>undefined</strong> in a next line: <code>this.$refs[startRef][0];</code></p>
  
  <pre><code>watch: {
    newTasks(tasks) {
      console.log("watch: newTasks()-------------");
      console.log(tasks);
      this.tasks = tasks;
      this.$forceUpdate();
    }
  },
  </code></pre>
  
  <ol start="2">
  <li><p><a href="https://jsfiddle.net/AlexPilugin/98b0pw24/106/" rel="nofollow noreferrer">https://jsfiddle.net/AlexPilugin/98b0pw24/106/</a></p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>Vue.config.productionTip = false;
  Vue.config.devtools=false;
  new Vue({
    el: "#app",
    data: {
      longMode: false,
      columns: 7,
      tasks: []
    },
    created() {
      this.tasks = this.newTasks;
    },
    watch: {
      newTasks(tasks) {
        this.tasks = tasks;
        this.$forceUpdate();
      }
    },
    computed: {
      newTasks() {
        let tasks = null;
        if(this.longMode) {
          tasks = [
            {start: 0, end: 3},
            {start: 2, end: 5},
            {start: 1, end: 7}
          ];
        } else {
          tasks = [
            {start: 0, end: 4},
            {start: 2, end: 6}
          ];
        }
        this.sleepForDebug(2000);
        return tasks;
        /*
        this.delay(2000).then(() =&gt; {
          console.log("return new tasks:");
          console.log(tasks);
          return tasks;
        })
        */
      }
    },
    methods: {
      toggle() {
        this.longMode = !this.longMode;
        this.columns = this.longMode ? 14 : 7;
        this.tasks = this.newTasks;
        //console.log("clicked! longMode: " + this.longMode);
      },
      sleepForDebug(delay) {
        var start = new Date().getTime();
        while (new Date().getTime() &lt; start + delay);
      },
       delay(t) {
        return new Promise(function(resolve) {
          setTimeout(resolve, t)
        });
      },
      getLineStyle() {
      /*
       * The HTMLElement.offsetTop read-only property returns the distance of the current element relative to the top of the offsetParent node.    
       * getBoundingClientRect() is a very useful, cross browser safe method that returns top, right, bottom, and left position of any element relative to the viewport.
       */
        let styleObject = {left: 0, top: 5, width: 0};
        let rowIndex = 2;
        //console.log("getLineStyle("+rowIndex+"): ------------------");
        //console.log(this.$refs);
        if(this.newTasks &amp;&amp; this.newTasks.length&gt;0) {
          //console.log(this.newTasks);
          let task = this.newTasks[rowIndex];
          if(task) {
            //console.log("task:");
            //console.log(task);
            //let start = task.start; //&lt;---- this.$refs[startRef][0] NOT WORKING
            //let end = task.end; //&lt;---- this.$refs[endRef][0] NOT WORKING
            let start = 0;
            let end = 6;
            let startRef = 'cell'+start;
            let endRef = 'cell'+end;
            let startElement = this.$refs[startRef][0];
            let endElement = this.$refs[endRef][0];
            let startLeft = Math.floor(startElement.offsetLeft);
            let endLeft = Math.floor(endElement.offsetLeft);
            let ofsetTop = Math.floor(startElement.getBoundingClientRect().top);
            let taskWidth = (endLeft - startLeft);
            //console.log("startLeft: "+startLeft + ", endLeft: " +endLeft + ", ofsetTop:" +ofsetTop);
            styleObject = {left: startLeft+"px", top: ofsetTop+"px", width: taskWidth+"px"};
            
            //console.log("startElement ClientRect:");
            //console.log(startElement.getBoundingClientRect());
            //console.log("endElement ClientRect:");
            //console.log(endElement.getBoundingClientRect());
          }
  
          //console.log("styleObject:");
          //console.log(styleObject);
          return styleObject;
        }
      }
    }
  })</code></pre>
  <pre class="snippet-code-css lang-css prettyprint-override"><code>body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
  }
  
  #app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
  }
  
  table {
  border-collapse: collapse;
  table-layout: auto;
  /*fixed;*/
  border: 1px solid #012656;
  }
  
  .cell {
  padding: 5px;
  border: 1px solid #012656;
  cursor: pointer;
  }
  
  td.cell:nth-child(7n-1),
  td.cell:nth-child(7n) {
  background: #eee;
  }
  
  .line {
  position: absolute;
  top: 4px;
  height: 6px;
  border-radius: 3px;
  background: red;
  overflow: hidden;
  }</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"&gt;&lt;/script&gt;
  &lt;div id="app"&gt;
    &lt;h4&gt;Click to change the Table's Mode and to wait for 2 second&lt;/h4&gt;
      &lt;table class="gantt-table" @click="toggle"&gt;
        &lt;tr&gt;
          &lt;td 
            v-for="(cell, colIndex) in columns" 
            :ref="'cell'+colIndex" 
            :key="'col-'+colIndex" 
            class="cell"
          &gt;
            {{ colIndex }}
          &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr 
          v-show="newTasks &amp;&amp; newTasks.length &gt; 0" 
          v-for="(row, rowIndex) in newTasks" 
          :key="'tr-'+rowIndex"
        &gt;   
          &lt;div 
            v-show="newTasks &amp;&amp; newTasks.length &gt; 0" 
            style="position: relative;"
          &gt;
            &lt;div class="line" :style="getLineStyle()"&gt;&lt;/div&gt;
          &lt;/div&gt;  
    
          &lt;td 
            v-for="(cell, colIndex) in columns" 
            :key="'row-'+rowIndex+'col-'+colIndex" 
            class="cell"
          &gt;
            &lt;div&gt;&amp;nbsp&amp;nbsp&lt;/div&gt;
          &lt;/td&gt;
        &lt;/tr&gt;
      &lt;/table&gt;
    &lt;/div&gt;
    
    </code></pre>
  </div>
  </div>
  </p></li>
  </ol>