1
votes

I am new to Vue and I am trying to bind an element with a fairly complex data object using Vue with a nested component.

I believe I have set this up correctly according to the documentation but I'm not seeing any examples that match my situation exactly.

The error I'm getting is vue.js:584 [Vue warn]: Property or method "factions" is not defined on the instance but referenced during render. This message is displayed for every json object property that is referenced in the markup. I'm guessing there is something relatively simple here that I'm missing but I cannot determine what that is. Any help is appreciated, thank you!

Here is a fiddle: https://jsfiddle.net/rjz0gfLn/7/

And the code:

var x = {
  "factions": [
    {
      "id": 0,
      "name": "Unknown",
      "img": "Unknown.png"
    },
    {
      "id": 1,
      "name": "Light",
      "img": "Light.png"
    },
    {
      "id": 2,
      "name": "Dark",
      "img": "Dark.png"
    }
  ],
  "roles": [
    {
      "id": 0,
      "name": "Unknown",
      "img": "Unknown.png"
    },
    {
      "id": 1,
      "name": "Assassin",
      "img": "Assassin.png"
    },
    {
      "id": 2,
      "name": "Mage",
      "img": "Mage.png"
    }
  ],
  "cacheDate": 1521495430225
};

console.log("data object", x);

Vue.component("filter-item", {
    template:   `<li class="fitem">
                    <input type="checkbox" class="fcheck" />
                    <img :src="img" class="fimg" />
                    <span class="fname">
                        {{name}}
                    </span>
                </li>`});

Vue.component("filter-items", {
    template: `<ul class="flist">
                    <filter-item v-repeat="factions"></filter-item>
                    <li class="fseperator"/>
                    <filter-item v-repeat="roles"></filter-item>
                </ul>`});

var v = new Vue({
  el: "#filters",
  data: x
});
<nav id="filter-drawer">
  <ul id="filters" class="flist">
        <filter-items></filter-items>
    </ul>
</nav>
2

2 Answers

2
votes

You have to declare every data you want to pass to the children as props in those child components. Additionally you need to pass each prop from the parent using :fieldnameinchild="value" (shorthand for v-bind:fieldnameinchild="value".

In your case, you'll need to do that in several places:

Parent:

<filter-items :factions="factions" :roles="roles"></filter-items>

And child:

<filter-item v-for="f in factions" :img="f.img" :fimg="f.fimg" :name="f.name" :key="'f'+f.id"></filter-item>
<filter-item v-for="r in roles" :img="r.img" :fimg="r.fimg" :name="r.name" :key="'r'+r.id"></filter-item>

Notice, in vue2 you want to use v-for instead of v-repeat.

Also, add :key to the v-for:

[Vue tip]: : component lists rendered with v-for should have explicit keys. See https://vuejs.org/guide/list.html#key for more info.

See updated fiddle: https://jsfiddle.net/acdcjunior/rjz0gfLn/21/

var x = {
  "factions": [
    {
      "id": 0,
      "name": "Unknown",
      "img": "img.png"
    },
    {
      "id": 1,
      "name": "Light",
      "img": "/a/a6/Light.png"
    },
    {
      "id": 2,
      "name": "Dark",
      "img": "/0/0e/Dark.png"
    }
  ],
  "roles": [
    {
      "id": 0,
      "name": "Unknown",
      "img": "img.png"
    },
    {
      "id": 1,
      "name": "Assassin",
      "img": "/6/69/Assassin.png"
    },
    {
      "id": 2,
      "name": "Mage",
      "img": "/2/20/Mage.png"
    }
  ],
  "cacheDate": 1521495430225
};

console.log("data object", x);

Vue.component("filter-item", {
    template:   `<li class="fitem">
                    <input type="checkbox" class="fcheck" />
                    <img :src="img" class="fimg" />
                    <span class="fname">
                        {{name}}
                    </span>
                </li>`,
  props: ['img', 'fimg', 'name']
                
});

Vue.component("filter-items", {
  template: `<ul class="flist">
            	  <filter-item v-for="f in factions" :img="f.img" :fimg="f.fimg" :name="f.name" :key="'f'+f.id"></filter-item>
            	  <li class="fseperator"/>
               <filter-item v-for="r in roles" :img="r.img" :fimg="r.fimg" :name="r.name" :key="'r'+r.id"></filter-item>
             </ul>`,
  props: ['factions', 'roles']
});

var v = new Vue({
  el: "#filters",
  data: x
});
<script src="https://unpkg.com/vue"></script>

<nav id="filter-drawer">
  <ul id="filters" class="flist">
        <filter-items :factions="factions" :roles="roles"></filter-items>
    </ul>
</nav>
1
votes

factions belongs to the root Vue instance, filter-items has a different data object. You probably want to pass in factions as a prop to filter-items.

https://vuejs.org/v2/guide/components.html#Passing-Data-with-Props

Vue.component("filter-items", {
    props: ['factions'],
    template: `<ul class="flist">
                <filter-item v-repeat="factions"></filter-item>
                <li class="fseperator"/>
                <filter-item v-repeat="roles"></filter-item>
        </ul>`});


var v = new Vue({
  el: "#filters",
  data: x
});
<nav id="filter-drawer">
  <ul id="filters" class="flist">
        <filter-items :factions="factions"></filter-items>
    </ul>
</nav>

EDIT: v-repeat was deprecated, use v-for, and you're not passing a prop down to filter-item