2

I am learning Vue.js by writing a small script and ended up with a catch 22 kind of situation.

The JS script (explorer.js) which handles the Vue.js part:

var vm = new Vue({
    el: "#root",
    data: {
        posts: {},
        tags: {}
    }
})

qwest.get('/posts.json')
    .then(function (xhr, response) {
        vm.posts = response;
    });

qwest.get('/tags.json')
    .then(function (xhr, response) {
        vm.tags = response;
    });

Case 1: loading explorer.js early:

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/qwest/4.4.6/qwest.min.js"></script>
<link rel="stylesheet" href="/static/css/explorer.css">
<script src="/static/js/explorer.js"></script>

<div id="root">
    <button v-for="(content, title) in posts" class="l1 btn">
        {{ title }}
        <button v-for="tag in content.tags" class="l2 btn">
            {{ tag }}
            <button v-for="t in tags[tag]" class="l3 btn">
                {{ t }}
            </button>
        </button>
    </button>
</div>

I get a Cannot find element: #root error from Vue. This is understandable: when explorer.js runs, the <div id="root"> is not known yet.

Case 2: loading explorer.js late:

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/qwest/4.4.6/qwest.min.js"></script>
<link rel="stylesheet" href="/static/css/explorer.css">

<div id="root">
    <button v-for="(content, title) in posts" class="l1 btn">
        {{ title }}
        <button v-for="tag in content.tags" class="l2 btn">
            {{ tag }}
            <button v-for="t in tags[tag]" class="l3 btn">
                {{ t }}
            </button>
        </button>
    </button>
</div>

<script src="/static/js/explorer.js"></script>

I get a Property or method "data" is not defined on the instance but referenced during render. error (and other of the same style).
This is also understandable: the v-for functions try to access data which have not been defined yet.

How (or rather - where) should I load explorer.js?

WoJ
  • 19,312
  • 30
  • 122
  • 230

3 Answers3

2

First, you should add explorer.js at the bottom of your file. You could add it at the top of your file if you wrapped the instantiation of the Vue (new Vue(...) in a function that only ran when the page was completely loaded, but it's standard practice to add the script at the bottom.

Your primary issue is that nested buttons are invalid HTML. This is the issue that is throwing

Property or method "content" is not defined on the instance but referenced during render.

This appears to mainly be Vue complaining about your invalid HTML. I've modified your template below to something that will actually render, but you'll need to tailor it to your needs.

<div v-for="(content, title) in posts" class="l1 btn">
  <button>{{ title }}</button>
  <span v-for="tag in content.tags" class="l2 btn" style="margin-left:1em">
     <button>{{ tag }}</button>
     <span v-for="t in tags[tag]" class="l3 btn">
       <button>{{ t }}</button>
     </span>
  </span>
</div>

Here is a working example.

Bert
  • 70,407
  • 13
  • 164
  • 146
  • Thank you. I would have never caught the fact that the buttons were causing the issue. I converted them to `div` and everything is fine (with `explorer.js` at the bottom) – WoJ May 31 '17 at 11:48
1

I actually wrote a post that explains the Vue Cannot Find Element error. You'll get this error if you try to instantiate Vue to0 early. In my Vue.js training course, all examples load the Vue.js framework towards the end of the file. In general, I have the Vue.js framework loaded via a script tag and then another script block that initializes the Vue instance itself. These two items happen just before the closing body tag.

In regards to Case #1, just move your script elements after your <div id="root" ... element and you should be in good shape.

You can use the v-cloak directive to create smooth loading experience. The reason that I recommend loading your Vue last is based on recommendations from PageSpeed Insights. These may, or may not, be relevant to your scenario though.

Chad Campbell
  • 909
  • 7
  • 14
0

Vue.js should be loaded at the start.

You don't need to specify the data property in order to access it's properties, see the Hello World example:

https://jsfiddle.net/chrisvfritz/50wL7mdz/

I'm not sure what is your data structure but, you could try something like that (without the data property):

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/qwest/4.4.6/qwest.min.js"></script>

<div id="root">
    <button v-for="post in posts" class="l1 btn">
        {{ post.title }}
        <button v-for="tag in tags" class="l2 btn">
            {{ tag }}
        </button>
    </button>
</div>

Just work according to your data structure but without the data

Also, with Vue.js you can't have dynamically added root properties, see https://vuejs.org/v2/guide/reactivity.html, so you must specify the root properties in your data as your initial structure.

The explorer.js should be loaded after your html template (at the end), the warnings you get/errors is due to not defining your model at the start, even nested roots !

other than that everything seems to be fine.

jony89
  • 3,706
  • 2
  • 22
  • 36
  • I am not sure I understand. My script is at the very top of the question, with the data structure. In the HTML, `(data, title) in posts` allows me to get the value (data, this is indeed an unfortunate name, but has nothing to do with the `data`structure in the definition of my Vue object), and the key (`title`), for each of the elements of `posts`. – WoJ May 30 '17 at 06:47
  • I changed the name of the variable to make it clear (`data` -> `content`) – WoJ May 30 '17 at 06:49
  • see my edit, you need to declare all root elements in the initial data. also I don't see your full data structure (as an object), if you will add it, it will be helpful. – jony89 May 30 '17 at 07:05
  • I understand, but they **are** declared (see the first block of code in my question - this is the JS which manages the Vue object, together with its data) – WoJ May 30 '17 at 07:07
  • I re-clarified the question, explicitly calling the JS script in the description. Hope this helps to clarify the problem. – WoJ May 30 '17 at 07:12
  • The explorer.js should be loaded after your html template (at the end), the warnings you get/errors is due to not defining your model at the start, even nested roots ! – jony89 May 30 '17 at 07:34
  • Sorry, but I still do not understand. The model is defined in `explorer.js`. you suggest to put it at the end of the HTML, and then mention that the errors are due to the fact that it is not at the beginning of the HTML... – WoJ May 30 '17 at 09:06
  • i suggest that you write your model more than posts and tags (go down one/two more level). one of the ideas of Vue.js is that you define your schema there and if possible provide defaults as well – jony89 May 30 '17 at 14:35