Display multiple columns in Select2
Asked Answered
C

8

19

I'm using select2 and I'd like to show a multicolum table as a drop down, so I need the width of the drop down container to have a different (larger) width than the input itself

Is it possible to do that?

moreover I'd like to show a table with several columns. From the movies example, I saw that in the formatResult function you create a new table for each row.

Would it be possible to include every row in the same table, so that every cells has the same width? I would need to set some template to contain the rows or something like that.

What I want to achieve is a small input to show the code of an entity, and a large dropdown to show several more columns

--

here's a related issue on github: https://github.com/ivaynberg/select2/issues/1314

Coquet answered 15/5, 2013 at 3:22 Comment(1)
Currently unsupported. See github.com/ivaynberg/select2/issues/270.Duct
E
9

formatresult didn't work for me ! But templateResult works fine like this with data form PHP in HTML generated (not ajax content).

Here is woorking code for me, I seperate my columns by a pipe char (we could have more than 2 columns) :

html (from PHP) :

<option value="...">
    column 1 text | column 2 text
</option>

javascript (jQuery) :

$('#selectSubstance').select2({
    templateResult: function(data) {
        var r = data.text.split('|');
        var $result = $(
            '<div class="row">' +
                '<div class="col-md-3">' + r[0] + '</div>' +
                '<div class="col-md-9">' + r[1] + '</div>' +
            '</div>'
        );
        return $result;
    }
}); 

EDIT 2021-06-28

If you want the same visual for selected item (closed select), use the same syntax with templateSelection:

For example :

$('#selectProduct').select2({            
    placeholder: 'Choose a product.',
    templateResult: function(data) {
        var r = data.text.split('| ');
        var result = jQuery(
            '<div class="row">' +
                '<div class="col-md-3">' + r[0] + '</div>' +
                '<div class="col-md-9">' + r[1] + '</div>' +
            '</div>'
        );
        return result;
    },
    templateSelection: function(data) {
        var r = data.text.split('| ');
        var result = jQuery(
            '<div class="row">' +
                '<div class="col-md-3">' + r[0] + '</div>' +
                '<div class="col-md-9">' + r[1] + '</div>' +
            '</div>'
        );
        return result;
    }
});
Extirpate answered 13/10, 2016 at 17:26 Comment(0)
S
7

If you use Select2 v3.5, here is a workaround:

function formatResultMulti(data) {
  var city = $(data.element).data('city');
  var classAttr = $(data.element).attr('class');
  var hasClass = typeof classAttr != 'undefined';
  classAttr = hasClass ? ' ' + classAttr : '';

  var $result = $(
    '<div class="row">' +
    '<div class="col-md-6 col-xs-6' + classAttr + '">' + data.text + '</div>' +
    '<div class="col-md-6 col-xs-6' + classAttr + '">' + city + '</div>' +
    '</div>'
  );
  return $result;
}

$(function() {
  $('#multi').select2({
    width: '100%',
    formatResult: formatResultMulti
  });
})
body{
  background-color: #455A64;
}

#multiWrapper {
  width: 300px;
  margin: 25px 0 0 25px;
}

.def-cursor {
  cursor: default;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/3.5.0/select2.min.css" rel="stylesheet"/>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/3.5.0/select2.min.js"></script>
<div id='multiWrapper'>
  <select id="multi">
    <optgroup class='def-cursor' label='Country' data-city='City'>
      <option data-city="Athens" id="1" selected>Greece</option>
      <option data-city="Rome" "id="2 ">Italy</option>
  <option data-city="Paris " "id="3">France</option>
    </optgroup>
  </select>
</div>
Solothurn answered 23/1, 2018 at 8:36 Comment(1)
How would one encode that in a json packet above so that option group gets created.Dehumanize
J
6

I wanted to give my solution which will hopefully help others.

Using Select2 v4 tableish styling and multicolumn search. This uses local JSON data.

Quick overview of how this works:

There are 2 functions. The matcher function and the formatSelect.

The matcher function is used for the query. It will check each word of the query to see if there is a match in any of the rows. Currently set to return any row that matches atleaast 1 word.

The formatSelect function is then run once the matcher has returned all matching rows (all rows if no query). formatSelect adds header columns on the first row of data if its the first row being rendered.

The table in this case uses bootstrap column widths for structure.

The code:

var firstEmptySelect = true;

function formatSelect(result) {
    if (!result.id) {
        if (firstEmptySelect) {
            console.log('showing row');
            firstEmptySelect = false;
            return '<div class="row">' +
                    '<div class="col-xs-3"><b>Client</b></div>' +
                    '<div class="col-xs-3"><b>Account</b></div>' +
                    '<div class="col-xs-3"><b>Deal</b></div>' +
                    '</div>';
        } else {
            console.log('skipping row');
            return false;
        }
        console.log('result');
        console.log(result);
    }
    return '<div class="row">' +
                 '<div class="col-xs-3">' + result.client_name + '</div>' +
                 '<div class="col-xs-3">' + result.account_name + '</div>' +
                 '<div class="col-xs-3">' + result.deal_name + '</div>' +
                 '</div>';      
}

function matcher(query, option) {
    firstEmptySelect = true;
    if (!query.term) {
        return option;
    }
    var has = true;
    var words = query.term.toUpperCase().split(" ");
    for (var i =0; i < words.length; i++){
        var word = words[i];
        has = has && (option.text.toUpperCase().indexOf(word) >= 0); 
    }
    if (has) return option;
    return false;
}

$('#selectAccountDeal').select2({
    data: {!! $deals !!},
    width: '100%',
    templateResult: formatSelect,
    templateSelection: formatSelect,
    escapeMarkup: function(m) { return m; },
    matcher: matcher
})

My example JSON data

[{
    "text": "JL - o yea - Testing this deal",
    "id": 4,
    "client_name": "JL",
    "account_name": "o yea",
    "deal_name": "Testing this deal"
}, {
    "text": "JL - test - fj;askld fj",
    "id": 3,
    "client_name": "JL",
    "account_name": "test",
    "deal_name": "fj;askld fj"
}]

HTML for select element

<select id="selectAccountDeal" name="account_deal_id" placeholder="Select your Deal" required>
  <option></option>
</select>

Example of the select2 dropdown

select2 table view

Janiuszck answered 8/6, 2018 at 5:9 Comment(1)
Way to go, it's a good solution, but when I type in text-box to search, it returns one record less than it should return, for instance when I type 'Tech' it should return 4 rows according to the given data, but it returns one row as header and three more rows as result, so the first row is missing. As I figured out there is two return in formatSelect(), once it creates the header, one record of result is omitted. In fact the header occupies space of the first row. How do you solve it?Oleson
M
4

I haven't had any success yet with having a fixed header row above the columns to act as a typical table header row, but in terms of actually getting the columns out there, I referred to this feature request on Github where user trebuchetty alludes to using bootstrap's grid styles like col-md-6 (bootstrap versions >= 3).

I tried the following in the select2 options and it seems to give good results:

formatResult: function(result) {
    return '<div class="row">' +
           '<div class="col-md-6">' + result.name + '</div>' +
           '<div class="col-md-6">' + result.manager + '</div>' +
           '</div>';
},

result in the above is a reference to an element in the array of items displayed in the dropdown

Mackie answered 30/1, 2014 at 14:1 Comment(2)
can you please make a jsfiddle or something like that? I don't get any change to output after using formatResult in version 4.0 +Latanya
formatresult didn't work for me ! But templateResult works fine, so I posted my solution.Extirpate
P
2

Here is my solutions. My select2 version is 4.0.3.

The final presentation look like: select2-dropdown-table.

First add the header when select2 dropdown open:

    var headerIsAppend = false;
    $('#stock').on('select2:open', function (e) {
        if (!headerIsAppend) {
            html = '<table class="table table-bordered" style="margin-top: 5px;margin-bottom: 0px;">\
                <tbody>\
                <tr>\
                    <td width="20%"><b>药品名称</b></td>\
                    <td width="10%"><b>成本价</b></td>\
                    <td width="20%"><b>供应商</b></td>\
                    <td width="10%"><b>批号</b></td>\
                    <td width="20%"><b>有效期</b></td>\
                    <td width="20%"><b>库存数量</b></td>\
                </tr >\
                </tbody>\
                </table>';
            $('.select2-search').append(html);
            $('.select2-results').addClass('stock');
            headerIsAppend = true;
        }
    });

Then

templateResult: function (repo) {
            if (repo.medicine) {
                var html = '<table class="table table-bordered" style="margin-bottom: 0px;">\
                <tbody>\
                <tr>\
                    <td width="20%">' + repo.medicine.name + '</td>\
                    <td width="10%">' + repo.costPrice + '</td>\
                    <td width="20%">' + repo.vendor + '</td>\
                    <td width="10%">' + repo.batchNum + '</td>\
                    <td width="20%">' + repo.expiredDate + '</td>\
                    <td width="20%">' + repo.quantity + '</td>\
                </tr >\
                </tbody>\
                </table>';
                return $(html);
            }
        },

The rest work need to do is adjust the css:

.stock .select2-results__option {
  padding: 0px 4px;
}

That's all.

Provencal answered 5/9, 2018 at 2:58 Comment(1)
Yes this the most eazy way. ThanksLatish
D
0

refer this it may helpful, here a component to make columns in select2 like structure https://github.com/suriyagp/Grid-Select

Donavon answered 10/11, 2014 at 13:15 Comment(0)
S
0

My example (based on Meloman's answer) can maybe help someone else :

// HTML
 <select 
    class="form-control select2" 
    data-col0htmldebut="<div class='col-md-6'>" 
    data-col0htmlfin="</div>" 
    data-col1htmldebut="<div class='col-md-2'>" 
    data-col1htmlfin="</div>" 
    data-col2htmldebut="<div class='col-md-4'>" 
    data-col2htmlfin="</div>">
          <option value="">Select...</option>
          <option value="-1">Text with no column</option>
          <option value="1">Column1__|__Col2__|__Col3</option>
          <option value="2">Column1__|__Col2__|__Col3</option>
 </select>

$("select.select2").select2({
    templateResult: function (data) {
        if (data.element == null) return data.text;


        /**************  Just one column handler **************/

        // __|__ text seperator between each col find ?
        var arrTexteOption = data.text.split('__|__');
        if (arrTexteOption.length <= 1) return data.text;


        /**************  Multi column handler **************/

        // Get select object
        var objSelect = $("#" + data.element.parentNode.id);

        // 4 column handled here
        var arrStyleColDébut = [];
        var arrStyleColFin = [];

        for (var i = 0; i < 4; i++)
        {
            if (objSelect.attr('data-col' + i + 'htmldebut'))   arrStyleColDébut.push(objSelect.data("col" + i + "htmldebut"));
            if (objSelect.attr('data-col' + i + 'htmlfin'))     arrStyleColFin.push(objSelect.data("col" + i + "htmlfin"));
        }

        if (arrTexteOption.length != arrStyleColDébut.length) return data.text;

        var $htmlResult = '<div class="row">';
        for (var i = 0; i < arrTexteOption.length; i++)
        {
            $htmlResult += arrStyleColDébut[i] + arrTexteOption[i] + arrStyleColFin[i];
        }
        $htmlResult += "</div>";

        return $($htmlResult);
    },
    templateSelection: function (data) {
        // Selection must display only the first col.
        return data.text.split('__|__')[0];
    }
});
Sassenach answered 14/2, 2018 at 10:13 Comment(0)
I
0

I using SELECT2 V4. Thank to BrinkDaDrink idea. I did some modification and simplified the code. below is my sample:

        var firstEmptySelect = true; // Indicate header was create

        function s2FormatResult(item) {

            if (!item.id) {
                // trigger when remote query
                firstEmptySelect = true; // reset
                return item.text;
            }

            var $container; // This is custom templete container.

            // Create header
            if (firstEmptySelect) {

                firstEmptySelect = false;

                $container = $(
                    '<div class="row">' +
                    '<div class="col-xs-3"><b>Product ID</b></div>' +
                    '<div class="col-xs-3"><b>Product Name</b></div>' +
                    '</div>' +
                    '<div class="row">' +
                    '<div class="col-xs-3">' + item.id + '</div>' +
                    '<div class="col-xs-3">' + item.text + '</div>' +
                    '</div>'
                );

            }
            else {
                $container = $('<div class="row">' +
                    '<div class="col-xs-3">' + item.id + '</div>' +
                    '<div class="col-xs-3">' + item.text + '</div>' +
                    '</div>');
            }


          return $container
        }

It's look like this.

Iridic answered 4/11, 2019 at 8:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.