简体   繁体   中英

VueJS filter doesn't work in v-for

I have an error on VueJS with a filter added in a v-for from an Axios response and doesn't understand how to solve it. The filter set_marked return a undefined value if i made a console.log on the value variable.

Here's the HTML:

<main id="app">
  <div v-for="item in productList" :key="item.id">
  <header>
    <h2>{{ item.title }}</h2>
  </header>
  <article class="product-card">
      {{ item.content | set_marked  }}
  </article>
  </div>
</main>

And the Javascript:

var app = new Vue({
  el: '#app',
  data: {
    loading: false,
    loaded: false,
    productList: []
  },
  created: function() {
    this.loading = true;
    this.getPostsViaREST();
  },
  filters: {
    set_marked: function(value) {
      return marked(value);
    }
  },
  methods: {
    getPostsViaREST: function() {
      axios.get("https://cdn.contentful.com/spaces/itrxz5hv6y21/environments/master/entries/1Lv0RTu6v60uwu0w2g2ggM?access_token=a2db6d0bc4221793fc97ff393e541f39db5a65002beef0061adc607ae959abde")
           .then(response => {
              this.productList = response.data;
            });
    }
  }
})

You can also try it on my codepen: https://codepen.io/bhenbe/pen/deYRpg/

Thank you for your help !

You are iterating with v-for on productList , but in your code productList is not an array but an object (a dictionary in other words). In fact if you look at it, it has this structure:

{
    "sys": {
        "space": {
            "sys": {
                "type": "Link",
                "linkType": "Space",
                "id": "itrxz5hv6y21"
            }
        },
        "id": "1Lv0RTu6v60uwu0w2g2ggM",
        "type": "Entry",
        "createdAt": "2017-01-22T18:24:49.677Z",
        "updatedAt": "2017-01-22T18:24:49.677Z",
        "environment": {
            "sys": {
                "id": "master",
                "type": "Link",
                "linkType": "Environment"
            }
        },
        "revision": 1,
        "contentType": {
            "sys": {
                "type": "Link",
                "linkType": "ContentType",
                "id": "page"
            }
        },
        "locale": "fr-BE"
    },
    "fields": {
        "title": "Retour sur douze années de design",
        "content": "Douze années ... vie."
    }
}

Iterating through it, on the first iteration will assign to item the value of the "sys" key, which is:

{
    "space": {
        "sys": {
            "type": "Link",
            "linkType": "Space",
            "id": "itrxz5hv6y21"
        }
    },
    "id": "1Lv0RTu6v60uwu0w2g2ggM",
    "type": "Entry",
    ...
    "locale": "fr-BE"
},

and on the second iteration the value of the "fields" key, which has the value:

{
    "title": "Retour sur douze années de design",
    "content": "Douze années ... vie."
}

Since you are accessing item.title and item.content , and title and content keys are not present in the first object, but only in the second, in the first iteration they will be undefined . So, in the first iteration you are passing undefined as the value of item.content to the set_marked filter.

productList is the response to the GET request, which as we have seen is not returning an array but an object.

If you add to the filter the check if (!value) return ''; it will work, but you are just hiding the problem of the discrepancy between what the API returns and what you are expecting.

If you build productList as an array by filtering through the sub-objects of result.data and keeping only those containing title and contents fields, it works:

 function marked(value) { return value.toUpperCase(); } var app = new Vue({ el: '#app', data: { productList: [] }, created: function() { this.loading = true; this.getPostsViaREST(); }, filters: { set_marked: function(value) { // console.log(value); return marked(value); } }, methods: { getPostsViaREST: function() { axios.get("https://cdn.contentful.com/spaces/itrxz5hv6y21/environments/master/entries/1Lv0RTu6v60uwu0w2g2ggM?access_token=a2db6d0bc4221793fc97ff393e541f39db5a65002beef0061adc607ae959abde") .then(response => { // this.productList = response.data; let data = response.data; let productList = [], key; for (key in data) { let val = data[key]; if ((val.title !== undefined) && (val.content !== undefined)) { productList.push(val); } } this.productList = productList; }); } } }) 
 @import url('https://fonts.googleapis.com/css?family=Lato'); body{ font-family: 'Lato', sans-serif; font-size: 1.125rem; } #app > div{ max-width: 68ch; margin: 0 auto; } 
 <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <main id="app"> <div v-for="item in productList" :key="item.id"> <header> <h1>{{ item.title }}</h1> </header> <article class="product-card" v-html="$options.filters.set_marked(item.content)"></article> </div> </main> 

Ok, i find in the documentation the solution.

I must add the following code to the filter set_marked to work properly:

if (!value) return '';
value = value.toString();

I currently don't know why this additional lines are required.

I had a second problem because the returned html is escaped by VueJS. The easiest way to avoid this issue is to used the v-html directive. To apply a filter in the v-html directive, you must use this syntax :

v-html="$options.filters.set_marked(item.content)"

You can find my pen here : https://codepen.io/bhenbe/pen/deYRpg

Usually I just create a method when I need filtering and then use it when needed in my component.

ie:

    methods: {
    getPostsViaREST: function() {
      axios.get("https://cdn.contentful.com/spaces/itrxz5hv6y21/environments/master/entries/1Lv0RTu6v60uwu0w2g2ggM?access_token=a2db6d0bc4221793fc97ff393e541f39db5a65002beef0061adc607ae959abde")
           .then(response => {
              this.productList = response.data;
            });
    },
    filterPost(post) {
     return _.toUpper(post);
    }
  }

And then in your component:

 <h1>{{ filterPost(item.title) }}</h1>

Find here the full example:

https://codepen.io/Venomzzz/pen/mLrQVm?editors=1011

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM