1

I'm still debating the approach but this is what I have so far. The filters work great but the data list is over 2k items, so some sort of limit is going to need to be applied. The issue is that any limit to the view would have to require a new API pull with any filter being set by any methods I've tried.

My question is should I somehow paginate from the API call or pull all data and paginate with vue? Should I perhaps filter in the backend and apply different API calls or should I use what I have below? The filtering works nicely but I am not clear how I could limit whats displayed yet filter through the entire list then return.

My Vue app and components:

Vue.component('filter', {

props: ['list', 'name'],

template: '#filter-template',

watch: {

    selected: function(currentValue) {

        this.$dispatch('filter-update', [this.name, currentValue]);

    }

}

 });

Vue.component('products', {

template: '#product-template',

created() {

    this.fetchProducts();

},

data:function(){
    return {
        products:[],
        category: 'All',
        collection: 'All',
        design: 'All'
    }
},

events: {

    'push-category': function(id) {

        this.category = id;
        this.filterProducts();

    },

    'push-collection': function(id) {

        this.collection = id;
        this.filterProducts();

    },

    'push-design': function(id) {

        this.design = id;
        this.filterProducts();

    }

},

computed: {

    total: function () {
        return this.products.length;
    }

},

methods: {

    fetchProducts: function() {

        this.$http.get('api/internal/products', function(products) {

            this.$set('products', products);

        });

    },

    filterProducts: function() {

        var parent = this;
        var catFilter = [];
        var collFilter = [];
        var designFilter = [];

        // Collect all the bad guys
        if(this.category == 'All') { 
            catFilter = [];
        } else {

            var key = 'Item_Disc_Group';

            filter(key, this.category, catFilter);

        }

        if(this.collection == 'All') { 
            collFilter = [];
        } else {

            var key = 'Collection';

            filter(key, this.collection, collFilter);

        }

        if(this.design == 'All') { 
            designFilter = [];
        } else {

            var key = 'Fancy_Color_Intensity';

            filter(key, this.design, designFilter);

        }

        // Hide all the bad guys, show any good guys
        for (var i = this.products.length - 1; i >= 0; i--) {
            var product = this.products[i];

            if(catFilter.indexOf(product) > -1) {
                product.On_The_Web = "0";
            } else if(collFilter.indexOf(product) > -1) {
                product.On_The_Web = "0";
            } else if(designFilter.indexOf(product) > -1) {
                product.On_The_Web = "0";
            } else {
                product.On_The_Web = "1";
            }               


        };

        function filter(key, active, array) {

            for (var i = parent.products.length - 1; i >= 0; i--) {
                var product = parent.products[i];

                if(product[key] != active) {
                    array.push(product);
                }

            };

        }

    }

}

});


new Vue({

el: '#product-filter',

created() {

    this.fetchCategories();
    this.fetchCollections();
    this.fetchDesigns();

},

methods: {

    fetchCategories: function() {

        this.$http.get('api/categories', function(categories) {

            this.$set('categories', categories);

        });

    },

    fetchCollections: function() {

        this.$http.get('api/collections', function(collections) {

            this.$set('collections', collections);

        });

    },

    fetchDesigns: function() {

        this.$http.get('api/designs', function(designs) {

            this.$set('designs', designs);

        });

    }

},

events: {

    'filter-update': function(data) {

        if(data[0] == "categories") {
            this.$broadcast('push-category', data[1]);
        } else if (data[0] == "collections") {
            this.$broadcast('push-collection', data[1]);
        } else {
            this.$broadcast('push-design', data[1]);
        }

    }

}

});

My markup:

<div id="product-filter">

<filter :list="categories" name="categories"></filter>

<filter :list="collections" name="collections"></filter>

<filter :list="designs" name="designs"></filter>

<div class="clearfix"></div>

<products></products>


<!-- Vue Templates -->

<template id="filter-template">
    <div class="form-group col-md-2">
        <label>@{{ name }}</label>
        <select v-model="selected" class="form-control input-small">
            <option selected>All</option>
            <option value="@{{ option.navision_id }}" v-for="option in list">@{{ option.name }}</option>
        </select>
        <span>Selected: @{{ selected }}</span>
    </div>
</template>

<template id="product-template">
    <div class="row mix-grid thumbnails">
        <h1>@{{ total }}</h1>
        <div v-for="product in products" v-if="product.On_The_Web == '1'" class="col-md-3 col-sm-6 mix category_1">
            <a href="/products/@{{ product.id }}">
                <div class="mix-inner">
                    <img class="img-responsive" src="https://s3-us-west-1.amazonaws.com/sg-retail/images/products/@{{ product.No }}.jpg" alt="">
                    <div class="prod-details">
                        <h3>@{{ product.No }}</h3>
                        <p></p>
                        <span class="price"></span>
                    </div>
                </div>
            </a>
            <div class="prod-ui">
            </div>
        </div>
    </div>
</template>

</div>

1 Answer 1

1

If the query takes more than a few seconds I would implement the server side filtering, but really thats preference for you. If you go with client-side filters, you can use the built in filterBy filter:

<div v-for="product in products | filterBy category in 'Item_Disc_Group' | filterBy collection in 'Collection' | filterBy design in 'Fancy_Color_Intensity'">

Just make the default value '' instead of All so that if no filter is selected then the list won't be filtered at all.

<option value="" selected>All</option>

Then you can also use a pagination filter to further page the results (borrowed from this fiddle):

data:function(){
    return {
        currentPage: 0,
        itemsPerPage: 1,
        resultCount: 0
    }
},
computed: {
    totalPages: function() {
      return Math.ceil(this.resultCount / this.itemsPerPage)
    }
},
methods: {
    setPage: function(pageNumber) {
      this.currentPage = pageNumber
    }
},
filters: {
    paginate: function(list) {
        this.resultCount = list.length
        if (this.currentPage >= this.totalPages) {
          this.currentPage = this.totalPages - 1
        }
        var index = this.currentPage * this.itemsPerPage
        return list.slice(index, index + this.itemsPerPage)
    }
}

That would be added last after the filtering:

<div v-for="product in products | filterBy category in 'Item_Disc_Group' | filterBy collection in 'Collection' | filterBy design in 'Fancy_Color_Intensity' | paginate">

You'd also want to add buttons to support pagination, ie:

<ul>
  <li v-repeat="pageNumber: totalPages">
    <a href="#" v-on="click: setPage(pageNumber)" v-class="current: currentPage === pageNumber">{{ pageNumber+1 }}</a>
  </li>
</ul>
Sign up to request clarification or add additional context in comments.

3 Comments

oMg this is WAY EASIER than i thought. I knew when I was getting so creative I must have missed something. I'll try this out tomorrow morning I'll possibly have more questions
Worked like a charm, I even got search enabled Vue is AWESOME
@KevinCompton haha I know the feeling! glad you got it up and running

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.