1

I'm very new to VueJS but loving the possibilities. I started with one of my easier projects (single page php/jquery app) and have been working on converting it to VueJS/Webpack. It's basically a table that grabs json data into an array and populates a table and has filter controls. The drop down controls that filter are populated by unique values from a json field. That's what I'm currently working on (already got the json call and table working, along with modals that appear when you click on a table row).

What I'm noticing that is strange, is if I'm working and I save my work in my IDE (vscode) and the localwebserver refreshes my page, I see what is expected in my code to happen. However if I click REFRESH in my browser again, nothing works as expected. A good example is in one of my components, I'm building an array to use in my drop down controls. I'm outputting the array I'm using to populate my drop down using console.log() to make sure it works as I hope, and when I just save the file in vscode, it outputs the data correctly in the browser. If I immediately refresh my page in browser, it doesn't output the array as it just did, and instead I see [__ob__: Observer] with a length:0 where my array was previously output due to the console.log(). It also doesn't populate my dropdown.. Is this expected behavior?

Using npm with webpack and (for the first time ever) the built in webserver and compiler. I'm using the single page approach, App.vue, and I have 3 components in that App.vue. I'm filling an array named fans using json data (on my mounted() hook) and passing it as a prop into each component. Here is my App.vue:

<template>
  <div>
      <fan-modals v-bind:fans="fans"></fan-modals>

      <div class="container">
          <filter-controls v-bind:fans="fans"></filter-controls>
          <fans v-bind:fans="fans"></fans>
      </div>  <!-- end #container -->
  </div>
</template>

<script>
  import FilterControls from './components/FilterControls.vue';
  import Fans from './components/Fans.vue';
  import FanModals from './components/FanModals.vue';

  export default {
    name: 'app',
    data: function() {
      return {
        fansUrl: 'example.com',
        fans: []
      }
    },
    components: {
      filterControls: FilterControls,
      fans: Fans,
      fanModals: FanModals
    },
    methods: {
    },
    mounted() {
        var self = this;
        $.getJSON(self.fansUrl, function(data){
            self.fans = data;
        });
    }
}
</script>

The component that is giving me problems output is my FilterControls component, seen here (I left out all other controls for brevity):

<template>

        <div class="row">
            <div class="col-xs-6 col-sm-6 control-wrap">
                <select id='selectVoltage' class='form-control' v-model="voltageArray">
                <option>Voltage</option>
                <option v-for="(voltage, index) in voltageArray" :key="index" v-bind:value="voltage">
                    {{ voltage }}
                </option>
                </select>
            </div>
            <div class="col-xs-6 col-sm-6 control-wrap">
                <select id='selectMaxRPM' class="form-control">
                <option>Max RPM</option>
                </select>
            </div>
        </div>
        </template>

<script>
    export default {
        data: function() {
            return {
                voltageArray: [],
            }
        },
        props: {
            fans: {
                type: Array,
                required: false
            }
        },
        methods: {
            populateControls() {
                var vi = this;
                console.log("populateControls() called ");
                if (vi.fans) {
                    console.log("there are fans ");

                    var voltage = [];

                    $.each(vi.fans, function(index, value) {
                        voltage.push(vi.fans[index].voltage);

                    });

                    vi.voltageArray = $.unique(voltage);

                    console.log(vi.voltageArray);
                }

            }
        },
        mounted() {
            this.populateControls();
        }
    }
</script>

I'm passing the same array I use to populate my table as a prop to the component.

When I save my work and it's auto refreshed in the browser it works. When I refresh the page in the browser it doesn't. I'm baffled.

ItsPronounced
  • 5,191
  • 12
  • 40
  • 78

1 Answers1

1

This is a race condition. The timing of the mounting of <filter-controls> and the asynchronous network response from $.getJSON is going to vary. In cases where you'd see the expected output, the network response is likely received before <filter-controls> has mounted. The network response can be delayed for various reasons, including poor network connectivity.

I believe what's happening in your case is that the network response is received after <filter-controls> mounts and logs fans (which is based on the network response). Later you make an edit, webpack-dev-server hot-reloads, and the browser gets the cached network response (no network request) before <filter-controls> mounts.

demo of bug

You can also prove this to yourself in your sandbox by disabling the cache in Chrome's DevTools.

It's best to remove the assumption that the prop data would be available when the component is mounted. Instead, use a watcher to react to prop changes:

export default {
  mounted() {
    // this.populateControls(); // DON'T DO THIS
  }
  watch: {
    fans(value) { // <-- called whenever fans prop changes
      this.populateControls(value);
    }
  },
  methods: {
    populateControls(fans) {
      console.log('new fans', fans);
    }
  }
}

Also note that the v-model of <select> should hold the value of the selected voltage, not the voltageArray, which is used to populate the <option>s. You should add a data variable exclusively for the selected voltage:

<!-- <select v-model="voltageArray">  // DON'T DO THIS -->
<select v-model="selectedVoltage">

demo of fix

tony19
  • 64,135
  • 13
  • 95
  • 146
  • Thank you for this answer! This is amazing and exactly what I was looking for. I hadn't considered the cached page being used and the loading order of the components. Thank you so much! It works as expected now and I learned a ton! – ItsPronounced Sep 10 '18 at 13:11
  • @drpcken No problem :) – tony19 Sep 10 '18 at 15:42