Data not filtering correctly with computed property

vue-core

#1

I’m building a fairly straight-forward sortable list for a portfolio. The data is being sourced from a CMS (WordPress). The portfolio items and the portfolio categories are two separate endpoints. The desired behavior is for all of the portfolio items to list on page load, along with all of the categories as buttons. Clicking a category button should then filter the list of portfolio items to those matching that category.

On the created lifecycle hook, I’m populating a categorySort array with the ID’s from each of the categories. On click, this array is then updated to the ID of the category clicked. The problem is within the computed property that should be sorting the list of portfolio items based on the cateogrySort array, which is returning an error (below).

The Vue instance:

const port = new Vue( {
        el: '#portfolio-app',
        data: {
            portfolio: {},
            portfolioCats: {},
            portfolioLoading: true,
            categorySort: {}
        },
        created: function() {
            let $this = this;

            //2 requests will be sent: 1 one for the CPT, and one for the custom taxonomy
            let request = new XMLHttpRequest();
            let catsRequest = new XMLHttpRequest();

            //The request for the CPT

            request.onload = function() {
                $this.portfolio = JSON.parse( request.responseText );
                $this.portfolioLoading = false;
            };

            request.onerror = function() {
                console.log( 'A connection error has occurred' );
            };

            request.open( 'GET', endPoint, true );

            request.send();

            //The request for the custom taxonomy
            catsRequest.onload = function() {
                $this.portfolioCats = JSON.parse( catsRequest.responseText );
                let catIDList = [];
                for ( cat of $this.portfolioCats ) {
                    let id = cat.id;
                    catIDList.push( id );
                    $this.categorySort = catIDList;
                }
            };

            catsRequest.onerror = function() {
                console.log( 'A connection error occurred' );
            };

            catsRequest.open( 'GET', catsEndPoint, true );

            catsRequest.send();
        },
        computed: {
            filteredPortfolio: function() {
                let $self = this;
                return portfolio.filter( function ( portfolio ) {
                    return portfolio.portfolio_category.filter( $self.categorySort );
                } );
            }
        }
    } );

The component for the buttons:

Vue.component( 'sort-button', {
        model: {
          prop: 'id',
          event: 'click'
        },
        props: {
            id: 'id',
            name: 'name'
        },
        template: `
          <button class="button sort-button"
            v-on:click="$emit( 'click', $event.target.id )" 
          >
          {{ name }}
          </button>
        `
    }
);

The template:

        <div class="portfolio-categories-wrapper">
			<sort-button v-for="cat in portfolioCats" :id="cat.id" :value="cat.value" :name="cat.name" v-model.number="categorySort"></sort-button>
		</div><!--ends portfolio-categories-wrapper-->

		<div class="portfolio-wrapper">

			<figure class="portfolio-item" v-for="post in filteredPortfolio">

				<a v-bind:href="post.link">
					<img v-bind:src="post._embedded['wp:featuredmedia'][0].media_details.sizes.medium.source_url">
				</a>
				<figcaption class="portfolio-caption"><a v-bind:href="post.link"><span v-html="post.title.rendered"></span></a></figcaption>
			</figure>

		</div><!--ends portfolio-wrapper-->

This results in the following error: “ReferenceError: portfolio is not defined”

Truly appreciate any assistance.


#2

I think you meant to put $self infront of portfolio.filter


#3

I thought that also at first, also, but that results in a TypeError: “$self.portfolio is not a function”. I removed it per these docs.


#4

Well we dont know your JSON response, and you are pre defining portfolio: {}, as an object. So I can only assume you are storing an object., Objects do not have the function filter only arrays do.

So first problem, yes you need to reference $self to access portfolio, and second is it an object? Or an array? If its an object then there is no filter function.


#5

Thanks for your reply and for looking at this.

portfolio is an array of objects.

I’ve moved this into a pen here so you can see the response, etc.: https://codepen.io/david-brown/pen/ZwxaeL
Note that if you replace filteredPortfolio with portfolio in the v-for, you’ll see everything loaded as intended but without the filtering functionality.


#6

You do need to put $self in front of the first portfolio inside filteredPorfolio(). The reason this gives you a "$self.porfolio is not a function" error is because you are initializing porfolio to an empty object (as @bolerodan said) . Initialise porfolio to an empty array and things start working.

Then you have another problem: filteredCats starts off as an empty object, then when the data loads you set it to an array of integers, then when you click on a filter button you set it to a single integer. You need to keep it a consistent data type, either an integer if you only want to filter one category, or an array if you want to filter multiple categories. Here’s a working pen where I’ve kept it null until you click on a filter button.


#7

Thanks for the in-depth explanation! This works perfectly and gives me insight into where I went wrong.

Appreciate everyone’s assistance.