1

Why can't I iterate inside the component template as follows?

<!DOCTYPE html>
<html>
<head>
  <title>My first Vue app</title>
  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
  <style>
  </style>
</head>
<body>
    <div id="blog-post-demo">
      <blog-post :posts="posts"></blog-post>
    </div>
  <script>
    
Vue.component('blog-post', {
  props: ['posts'],
  template: `
    <div class="blog-post" v-for="post in posts">
      <h3> {{ post.title }}</h3>
      <button>Enlarge text</button>
      <div v-html="post.content"></div>
    </div>`,
})
new Vue({
  el   : '#blog-post-demo',
  data : {
    posts : [
      {id: 1, title : 'My Journey to Africa',    content : 'I am the post'},
      {id: 2, title : 'My Journey to America',   content : 'I am the post'},
      {id: 3, title : 'My Journey to Antartica', content : 'I am the post'},
      {id: 4, title : 'My Journey to Asia',      content : 'I am the post'},
    ],
  }
})
  </script>
</body>
</html>

The one below works but I am confused as to why the one above doesn't work. Any explanation would help!

<!DOCTYPE html>
<html>
<head>
  <title>My first Vue app</title>
  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
    <div id="blog-post-demo">
      <blog-post v-for="post in posts" :post="post"></blog-post>
    </div>
  <script>
    
Vue.component('blog-post', {
  props: ['post'],
  template: `
    <div class="blog-post">
      <h3> {{ post.title }}</h3>
      <button>Enlarge text</button>
      <div v-html="post.content"></div>
    </div>`,
})
new Vue({
  el   : '#blog-post-demo',
  data : {
    posts : [
      {id: 1, title : 'My Journey to Africa',    content : 'I am the post'},
      {id: 2, title : 'My Journey to America',   content : 'I am the post'},
      {id: 3, title : 'My Journey to Antartica', content : 'I am the post'},
      {id: 4, title : 'My Journey to Asia',      content : 'I am the post'},
    ],
  }
})
  </script>
</body>
</html>
Robert
  • 10,126
  • 19
  • 78
  • 130

2 Answers2

2

Simple:

"Cannot use v-for on stateful component root element because it renders multiple elements."

A template can only have on root element, you can wrap your component in a DIV tag if needed but in this simple case I actually think that your second example is more elegant and conforms to the single responsibility principle for that component.

More information can be found within the VueJS official documentation: https://v2.vuejs.org/v2/guide/components.html#A-Single-Root-Element

tony19
  • 125,647
  • 18
  • 229
  • 307
Tim Wickstrom
  • 5,476
  • 3
  • 25
  • 33
  • I like the recommendation you pointed out about the single responsibility. I think this is a valuable piece of info as I am learning more about front end dev. – Robert May 21 '19 at 00:25
  • if the v-for is on the component I am assuming the single root element would be `id="blog-post-demo"` no? – Robert May 21 '19 at 00:26
1

The problem is that you cannot have a repeated element as your template root.

If you use a development version of Vue.js, you would see...

[Vue warn]: Error compiling template:

Cannot use v-for on stateful component root element because it renders multiple elements.

Change your component's template to

template: `<div><div v-for="post in posts" :key="post.id">...</div></div>`

Demo

Vue.component('blog-post', {
  props: ['posts'],
  template: `<div>
    <div class="blog-post" v-for="post in posts" :key="post.id">
      <h3> {{ post.title }}</h3>
      <button>Enlarge text</button>
      <div v-html="post.content"></div>
    </div>
  </div>`,
})
new Vue({
  el   : '#blog-post-demo',
  data : {
    posts : [
      {id: 1, title : 'My Journey to Africa',    content : 'I am the post'},
      {id: 2, title : 'My Journey to America',   content : 'I am the post'},
      {id: 3, title : 'My Journey to Antartica', content : 'I am the post'},
      {id: 4, title : 'My Journey to Asia',      content : 'I am the post'},
    ],
  }
})
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.min.js"></script>
<div id="blog-post-demo">
  <blog-post :posts="posts"></blog-post>
</div>
Community
  • 1
  • 1
Phil
  • 157,677
  • 23
  • 242
  • 245
  • Thanks @Phil! I have wrapped it in a the specified parent div. Where in the docs is this topic mentioned? I feel like I missed this part. – Robert May 10 '19 at 00:29
  • Not sure if it's in the docs but the error message you get is pretty clear – Phil May 10 '19 at 00:31
  • I will have to use development version then, to get these errors – Robert May 10 '19 at 00:33