theUnforgiven's avatar

Issue with custom e-commerce & select dropdowns

Hi all,

I have a small problem and not sure how to fix it.

I have a custom e-commerce website, selling clothing which has different sizes & colors. The problem I'm having now if the client enters a product with one color and 2 or 3 different sizes it shows up like this:

The database looks like:

As you can see that's probably what is expected, but I need it do be Yellow one then I use Vue to populate the sizes dropdown once a color is selected.

This is the block of code in question:

<div class="col s12 m6">
            @if(! $product->options->isEmpty() && ! $product->options->isEmpty())
            <select name="colour" class="browser-default colors" data-index="{!! $product->id !!}" v-model="colour" v-on="change:selectedColour" id="optionID">
                <option value="" disabled selected>Choose a Colour</option>
                @foreach($product->options as $option)
                    <option v-model="option_id" value="{!! $option->product_id !!}-{!!  $option->colour  !!}" id="optionId" data-optionId="{!! $option->id !!}">{!!  $option->colour  !!} @if ($option->stock > 0) - In Stock @endif</option>
                @endforeach
            </select>
            @else
            <select class="browser-default colors">
                 <option value="Out of Stock">(Out of Stock)</option>
            </select>
            @endif
            <label></label>
        </div>

This is the VueJS

new Vue({
            el: '#cart',

            data: {
                colour: '',
                sizes: '',
                selectedSize: ''
            },

            methods: {
                fetchSizes: function() {
                    this.$http.get('/related-sizes?info=' + this.colour, function(sizes) {
                        this.$set('sizes', sizes);
                    });

                },

                selectedColour: function() {
                    this.fetchSizes();
                }

            }
        });

Which uses the following method on a controller

public function selectedColour(Request $request)
    {
        $info = explode('-', $request->input('info'));
        $cat = Options::where('product_id', $info[0])
            ->where('colour', $info[1])
            ->having('stock', '>', 0)
            ->orderBy('size', 'asc')
            ->get(['id', 'size', 'colour', 'stock']);
        return Response::json($cat, 200);
    }

So question is how can I get it to just show Yellow once in this example but yet show the 2-3 different sizes available?

0 likes
21 replies
jekinney's avatar

I can explain further if needed from pic instead of mobile.

I a assuming it's on a show page with a single product. What I have found to work the best is pass the product object with required relations to vue by a html data-attribute. In vue set a ready function to grab all the data, colors and sizes. Set your vue data as appropriate and on change of color show the sizes for the color. Set an add button or how ever you need that will show a new color selection box and continue chaining.

Great use for a component by the way. Submit form via php and Laravel.

This eleminates Ajax calls slowing things down when your grabbing the data anyways (presumably) and easier to keep each part separate.

theUnforgiven's avatar

Think i have this solved now, just need some JS to group together one colour then upon selecting the colour show all available in stock sizes.

1 like
theUnforgiven's avatar

Can anyone advise/help on this please, based on what I've put in previous posts.

ralee's avatar

@Istables,

I suggest populating the color dropdownlist with vue.js too. That way on ready() you can call a grabColor function that returns a unique list of colors from the server and populate the dropdownlist with it. When that dropdownlist changes, just do the fetchSizes function and retrieve the size list. Which you have already done.

1 like
theUnforgiven's avatar

@ralee How would you do this based on what I've already posted? Not quite sure how to achieve that.

ralee's avatar

@lstables,

new Vue({
            el: '#cart',

            data: {
                colour: '',
                sizes: '',
                selectedSize: '',
            },
      ready(){
        this.fetchColor();
      },
            methods: {
        fetchColor:function(){
          this.$http.get('/get-color?para1=' + id, function(color) {
            this.color = color;
                    });
        },
                fetchSizes: function() {
                    this.$http.get('/related-sizes?info=' + this.colour, function(sizes) {
                        this.$set('sizes', sizes);
                    });

                },

                selectedColour: function() {
                    this.fetchSizes();
                }

            }
        });
 <select name="colour" class="browser-default colors" data-index="{!! $product->id !!}" v-model="colour" v-on="change:selectedColour" id="optionID">
                <option value="" disabled selected>Choose a Colour</option>
        <option v-for="color in colors">
            @{{color.color}}
        </option>
            </select>

function getColor($id)
{
//do your logic here 
}

Codes aint tested, I hope this gives you an idea on solving the problem.

1 like
theUnforgiven's avatar

@ralee Thanks I'll give this a try and yes I can see where it's going now, makes sense.

Thanks again :-)

theUnforgiven's avatar

@ralee, I'm afraid that doesn't work or I'm not doing it right.

Tried your way with the following method:

public function getColor(Request $request)
    {
        $info = $request->input('id');
        $cat = Options::where('id', $info[0])
            ->where('colour', $info[1])
            ->having('stock', '>', 0)
            ->orderBy('size', 'asc')
            ->get(['id', 'size', 'colour', 'stock']);
        return Response::json($cat, 200);
    }

But it doesn't know where to get this from as it's not been passed anywhere, so maybe I'm missing something just not sure what or where.

theUnforgiven's avatar

Also surely that would have the same effect has my 1st post image also showing Yellow twice rather than once then showing available colours for that colour.

theUnforgiven's avatar

This is what I had before, which shows the colors as "one" rather than how ever many times its been entered for different sizes, but it's not quite right and not as quick hence looking to do it with Vue.

        $(function(){

            $('.colors').on('change', function(e){
                var cat_id = e.target.value;
                var current = $(this).data("index");
                var select = $("select[data-index='"+current+"'][class='sizes']");

                $.get('/related-sizes?info=' + cat_id, function(data){
                    select.empty();
                    $.each(data, function(index, subcatObj){
                        select.append('<option value="'+subcatObj.size+'">'+subcatObj.size+'</option>');
                    });
                });
            });
            var a = new Array();
            $(".colors").children("option").each(function(x){
                test = false;
                b = a[x] = $(this).val();
                for (i=0;i<a.length-1;i++){
                    if (b ==a[i] && b != '') test =true;
                }
                if (test) $(this).remove();
            });
        });
davidrushton's avatar

Here is the javascript we used for a custom e-commerce app if that helps

   (function(){

        var price_field = $('#product-price');
        var options = $('.product-options select');
        var original = [];
        var selected = [];
        var values = [];

        //$(function(){

        $(options).each(function(index)
        {
            var option = $(this);
            var id = option.data('optionId');

            original[id] = $.map($('option:not([value=""])', option),function(value) {
                return value.value;
            });

            if ( index > 0 ) {
                option.addClass('disabled').attr('disabled','disabled').html('<option value="">--Select options above to filter--</option>');
            }
        });

        $(options).change(function()
        {
            var current = $(this);
            var position = options.index(current);
            selected = [];

            $(options).each(function(index){

                var option = $(this);
                var id = option.data('optionId');

                if ( index > position ) {
                    option.parent().addClass('loading');
                    option.addClass('disabled').attr('disabled','disabled').html('<option value="">--Select options above to filter--</option>');
                } else {
                    if ( option.val() != "" ) {
                        selected[id] = option.val();
                    }
                }
            });

            if ( selected.length == 0 ) {
                $('.product-options.loading').removeClass('loading');
                return false;
            }

            $.ajax({
                type: 'GET',
                url: '{{ url('api/v1/products/'.$product->ref.'/options') }}',
                data: { options: selected },
                success: function(response) {
                    updateOptions(current, response.data);
                    updatePrice(response.data);
                }
            });

        });

        function updatePrice(data)
        {
            if ( typeof data.price == 'undefined' ) {
                price_field.html();

                if ( $(options).filter('.disabled').length == 0 ) {
                    $(options).last().change();
                }

                return;
            }

            var price = parseInt(data.price_inc_tax);

            total = price / 100;

            price_field.html('£' + total.toFixed(2));

        }


        function updateOptions(changed, data)
        {
            var position = options.index(changed);
            var values = data.options;

            $(options).each(function(index){
                if ( index <= position ) {
                    return;
                }

                var option = $(this);
                var id = option.data('optionId');

                option.parent().removeClass('loading');

                if ( ! values[id] ) {
                    option.html('No options available');
                    return;
                }

                option.html('');
                $(values[id]).each(function(index, value)
                {
                    option.append($("<option>", { value: value, html: value }));
                });
                option.removeClass('disabled').removeAttr('disabled');

            });
        }


    })();
1 like
theUnforgiven's avatar

Anyone else willing to advise how to change this out to VueJS:

 $(function(){

            $('.colors').on('change', function(e){
                var cat_id = e.target.value;
                var current = $(this).data("index");
                var select = $("select[data-index='"+current+"'][class='sizes']");

                $.get('/related-sizes?info=' + cat_id, function(data){
                    select.empty();
                    $.each(data, function(index, subcatObj){
                        select.append('<option value="'+subcatObj.size+'">'+subcatObj.size+'</option>');
                    });
                });
            });
            var a = new Array();
            $(".colors").children("option").each(function(x){
                test = false;
                b = a[x] = $(this).val();
                for (i=0;i<a.length-1;i++){
                    if (b ==a[i] && b != '') test =true;
                }
                if (test) $(this).remove();
            });
        });

This then showed the colours has one colour rather than in my original post showing it twice like Yellow

ralee's avatar

@lstables ,


//try returning without json encoding it

//on a side note. You can do some php check to manipulate the array to remove duplicates here before returning $cat. 
public function getColor(Request $request)
    {
        $info = $request->input('id');
        $cat = Options::where('id', $info[0])
            ->where('colour', $info[1])
            ->having('stock', '>', 0)
            ->orderBy('size', 'asc')
            ->get(['id', 'size', 'colour', 'stock']);
        return $cat;
    }

On the receiving end. Bind them the the colors object.

 fetchColor:function(){
          this.$http.get('/get-color?para1=' + id, function(color) {
            this.colors = color;
                });
        },

populate your dropdown like this


 <select name="colour" class="browser-default colors">
                <option value="" disabled selected>Choose a Colour</option>
        <option v-for="color in colors" v-on="click:fetchSizes(color.id,$event)">
            @{{color.color}}
        </option>
            </select>


//function to pull size
 fetchSizes: function(color,event) {
                    this.$http.get('/related-sizes?info=' + colour, function(sizes) {
                        this.$set('sizes', sizes);
                    });

                },

So my recommendation is to use the on click event on the dropdown elements. So when user click on the color it should trigger a http call to populate the size dropdownlist. I hope this helps you.

theUnforgiven's avatar

This still doesn't do what I want it still shows the same colour twice rather than once then show the available colours.

theUnforgiven's avatar

Anyone, willing to help out has still haven't found a solution.

theUnforgiven's avatar

I'm still looking for some help on this if someone would be so kind to help out.

Please or to participate in this conversation.