Example of using select2 library instead of select or autocomplete

Example of using select2 library instead of select or autocomplete

asleasle Posts: 122Questions: 31Answers: 0

I have a need for a select field that is both autocomplete and multiple select and I understand that the select2 library is very good. It seems I an better oontrol the height of the select also (?) with selec2. But I can't find any good examples of setting this up, only this:
https://editor.datatables.net/plug-ins/field-type/editor.select2

I am trying to set this up and I have a test page. When I choose "New" I can not type anything, I can only select from the list. I am trying to accomplish autocomplete also on the field When I edit a record i get the value.
Here is a test page
If I could look at some working solution with select2 and Datatables it would help me.

Replies

  • kthorngrenkthorngren Posts: 22,132Questions: 26Answers: 5,097

    For some reason the input for this field doesn't look like a select2 input:

            {
                label: 'Tiltakstype:',
                name: 'publikasjon.tiltakstype',
                type: 'select2',
                opts: {
                    placeholder: "Velg eller skriv tiltakstype...",
                    tags: true,
                    allowClear: true,
                    width: 'resolve'
                }
            },
    

    Looks like you might be using an old version for the Editor's select2 plugin. Try the latest version from here.

    Kevin

  • allanallan Posts: 64,690Questions: 1Answers: 10,697 Site admin

    To add to that, there isn't currently an online demo for Select2 with Editor I'm afraid. I've focused instead on the built in tags and autocomplete field types which can do nearly everything required for a Select2 like field and are fully integrated into Editor and the backend libraries.

    Allan

  • rf1234rf1234 Posts: 3,138Questions: 91Answers: 433

    Hi asle,

    if you use the plug-in version that Kevin recommends everything should work. Here is the link again:
    https://editor.datatables.net/plug-ins/field-type/editor.select2

    I use exactly those versions for Select2:

    I copied your settings into my code:

    editor
        .on('opened', function (e, mode, action) {   
            if ( action === "remove" || action === "edit" ) {
                return true; //we can stop here for edit and remove
            }
            //must be reinitialized: not working otherwise!!
             $( this.field("report.report_type_id").input() ).select2("destroy").select2({
                placeholder: lang === 'de' ? 'Bitte wählen Sie einen Berichtstyp' : 
                                             'Please select one report type',
                tags: true,
                allowClear: true,
                width: 'resolve'
                });
    

    For some reason I need to reinitialize my select2 field on "opened" - otherwise: not working. But maybe that's caused by dynamically adding the optgroups in a fairly unorthodox way?!

    I tried the code above. And it gives you the search field etc. I don't see any effect of "tags", "allowClear" and "width" but I think it works well.

  • asleasle Posts: 122Questions: 31Answers: 0

    Thanks, the page you referred to is from 2018. Has there not been any work done on the plugin since then?
    On the homepage for select2 there is a newer version 4.1.0-rc.0 that I installed. I also downloaded the editor.select2.js

    I have got the select2 almost working but
    - I can not edit a previous value. I have to click the "x" and empty the value and write it again from scratch.
    - I can not select several options.

    Here is my code:

    {
                label: 'Tiltakstype:',
                name: 'publikasjon.tiltakstype',
                type: 'select2',
                opts: {
                    placeholder: {
                         id: '',
                        text:'Velg eller skriv tiltakstype...'
                    },
                    tags: true,
                    allowClear: true,
                }
            },
    

    I see Allan suggests that the functions are covered in tags and autocomplete which is builtin in datatables. Is it better then to drop select2 and try to get my solution to work with autocomplete?

  • rf1234rf1234 Posts: 3,138Questions: 91Answers: 433
    edited July 10

    https://editor.datatables.net/examples/dropdown/tags-multiple.html

    This looks like a pretty good fit for your requirements, doesn't it? I like it :smile: And will probably use it for multi-select the next time.

  • kthorngrenkthorngren Posts: 22,132Questions: 26Answers: 5,097

    I can not edit a previous value. I have to click the "x" and empty the value and write it again from scratch.

    Not sure why this is happening. I can't open you test case from your first post. Says its under maintenance.

    I can not select several options.

    You need to add the multiple option to the opts property. See the Select2 options docs for more details.

    Here is a simple example:
    https://live.datatables.net/guwafemu/634/edit

    Note that when multiple is set that field will send an array of one or more selected options to the server.

    Kevin

  • asleasle Posts: 122Questions: 31Answers: 0

    Thanks. I have looked at the tags-multiple example and it is very much what I am after. The problem is that I have over 2000 records with entries in the field I am working with data saved like "option1, option2, option3" and the datatables tags example uses JOIN with the tags in a separate table. I am not sure how I would convert all the existing fields with the tag system. With tags I can not add new values like I can do with autocomplete. Is it possible to add new tags with the tags approach? The best solution would be TAGS with the same ability as AUTOCOMPLETE to add new tags on the fly.

    So I have tried autocomplete but a problem I have is when I use autocomplete and long select lists, the select list goes outside the page. Sorry, the demo is up again (https://dev.mfu.no/code/admin-test.html - demo/demo ) where you can create a new post.
    I have several autocomplete fields, the field "Forfatter" collects distinct content on the field but it can be more than 50 lines. The select goes outside the page. I used CSS but dont know how I can target every autocomplete dropdown so that the top one is 300px (or 70%) height and the bottom is 150px (or 30%) height. If you create a new post you see that "Fylke" and "Kommune" should have been shorter height. Also the select2 field goes outside the page. I have used this CSS:

    .dte-dropdown-list {
        max-height: 300px;
        overflow-y: auto;
    }
    
  • kthorngrenkthorngren Posts: 22,132Questions: 26Answers: 5,097
    edited July 11

    The test case is asking for a login.

    I tried this with the tags:

    div.dte-dropdown-list {
        max-height: 100px;
        overflow-y: auto;
    }
    

    In this test case and it seems to work with the Datatables tags type:
    https://live.datatables.net/bomavase/1/edit

    However since the table list of options is a Datatable it would be nice if it used the datatable config property. I tried it but it doesn't seem the tags looks for the config property. Maybe @allan will comment on whether this is feasible or not.

    Select2 should just use standard CSS for the select2 library. This SO thread has some suggestions. I tried this:

    span.select2-results {
      max-height: 100px;
      overflow-y: auto;
    }
    

    In this test case:
    https://live.datatables.net/guwafemu/635/edit

    Kevin

  • asleasle Posts: 122Questions: 31Answers: 0

    Thanks again for your input! I wrote the username/pass for the demo page in my reply (demo/demo) but maybe it was not clear enough. I looked also at the datatables field config but not sure how to use it. If you look at the demo again you see when you create a new record that the dropdowns are all the same height. So I am not sure how to target each dropdown separately:

    /* this targets every dropdown but I want 
     a different value for dropdown1, dropdown2 etc */
    div.dte-dropdown-list {
        max-height: 100px;
        overflow-y: auto;
    }
    

    Suddenly the select2 is not expanding outside my page (today) but the I have max-height set to max-height:300px and that is good for the field "Forfatter" but too long for "Fylke" and "Kommune". So how to target each of these dropdowns with their own height?

    @kthorngren I added multiple: true to the field 'publikasjon.tiltakstype' and I can add multiple but I can not save. I get this error:

    <b>Fatal error</b>:  Uncaught Error: Call to undefined method DataTables\Editor\Options::id() in /home/1/m/mfu/www/code/js/Editor-PHP/controllers/admin-mfu-test.php:116
    Stack trace:
    #0 {main}
      thrown in <b>/home/1/m/mfu/www/code/js/Editor-PHP/controllers/admin-mfu-test.php</b> on line <b>116</b><br />
    

    the code it refers to is this in the controller php:

    Field::inst('publikasjon.tiltakstype')
    ->options(
        Options::inst()
         ->table('publikasjon')
         ->id('tiltakstype')   // line 116
         ->text('tiltakstype')
         ->order('tiltakstype ASC')
         ->where(function ($q) {
             $q->where(function ($r) {
                 $r->where('tiltakstype', null, '!=');
                 $r->and_where('tiltakstype', '', '!=');
             });
         })
    ),
    

    I like the builtin tags and autocomplete so I wonder if is is possible to use multiple items like the tags example but without a mysql JOIN where the field content is saved directly as "option1, option2, option3" instead of "1,2,3" which is joined on another table with the values.

    My fields with one value work fine with autocomplete since I can also edit the input. It seems like in the example that it is not possible to add a new value to the value list with select2 but it should behave like autocomplete like now when I fetch DISTINCT values (?)
    For my multiple field I would if possible avoid a JOIN and only save as the text since I have over 2000 records with (comma-separated) multiple values. And I may just be overcomplicating things as I tend to do!

  • kthorngrenkthorngren Posts: 22,132Questions: 26Answers: 5,097

    For the tags field I don't see a way specify a CSS selector to apply styling. The only option I can think of is to programmatically apply the CSS when the for is open using the open event. For example:

      editor.on('open', function () {
        // Set the CSS when the Position field label is clicked
        $('#DTE_Field_position > div > div.dte-tag-label').on('click', function () {
          // Small delay to allow browser time to show form
          setTimeout(() => {
            // Apply CSS to dropdown list
            $('div.dte-dropdown-list').css({
              'max-height': '100px',
              'overflow-y': 'auto'
            });
          }, 0);
        });
      });
    

    Updated test case:
    https://live.datatables.net/bomavase/2/edit

    The key is #DTE_Field_position which contains the e-fields.name value.

    You can probably do something similar with Select2. You will need to inspect the form elements to determine the proper selectors.

    @allan or someone else familiar with the server side libraries will need to help with your questions there.

    Kevin

  • rf1234rf1234 Posts: 3,138Questions: 91Answers: 433
    edited July 12

    My fields with one value work fine with autocomplete since I can also edit the input. It seems like in the example that it is not possible to add a new value to the value list with select2 but it should behave like autocomplete like now when I fetch DISTINCT values (?)

    I have a use case for adding new values on the fly. I do that using a "selectize" field (yet another field type plug-in)
    https://editor.datatables.net/plug-ins/field-type/editor.selectize

    The JS looks like this:

    fields: [ {
            label: lang === 'de' ? 'Kontrahent:' : 'Counterparty:',
            name:  "contract.gov_manual_creditor_id", //render creditor name
            type: "selectize", 
            opts: {
                create: true,
                createFilter: function(val) {
                  return ( isNaN(val) || val.indexOf('.') > -1 ) ? val : false;
                },
                maxItems: 1,
                openOnFocus: false,
                allowEmptyOption: false,
                placeholder: lang === 'de' ? 
                    "Bitte Kontrahenten wählen oder hinzufügen" : 
                    "Please select a Counterparty or add one",                
                render: {
                    option_create: function(data, escape) {
                        var add = lang === 'de' ? "Neu: " : "Add ";      
                        return '<div class="create">' + add + '<strong>'
                               + escape(data.input) + '</strong>&hellip;</div>';
                    }
                  }
                },
        },  {
    

    You can add new values but they may not be integer numbers (because then I can't distinguish the new value from existing ids). If you enter a new value it is preceeded by "Neu" or "Add" (depending on user language).

    That is all fairly easy. It becomes more difficult when it comes to saving this on the server.

    When a new value is entered (i.e. something that isn't an integer) I need to add this to the available options which are saved in a database table and save the id of the newly created record as a foreign key in "contract.gov_manual_creditor_id".

    So here is the PHP code.

    First the field instance which looks fairly normal. It will even try to write text to the gov_manual_creditor_id field in case of a new value entered by the user which doesn't seem to matter ... At least it hasn't caused any issues so far since the respective id field is nullable. And as you will see this value gets updated with a valid value on "writeCreate" and "writeEdit".

    Field::inst( 'contract.gov_manual_creditor_id' )
        ->validator( 'Validate::notEmpty', array('message' => $msg[0]) )
        ->options( Options::inst()
            ->table('gov_manual_creditor')
            ->value( 'id' )
            ->label( array('name', 'govdept_id') )    
            ->render( function ( $row ) { 
                if ($row['govdept_id'] > 0) {
                    return 'Interne Abteilung: ' . $row["name"];
                }
                return $row["name"];
            } )
            ->order( 'govdept_id desc, name asc' )
            //where clause MUST be a closure function in Options!!!
            ->where( function($q) {
                $q ->where('gov_id', $_SESSION['gov_id']);
                $q ->where( function($r) {
                    $r ->where('govdept_id', $_SESSION['govdept_id'], '!=');
                    $r ->or_where('govdept_id', null);
                });
            } )
        ),
    

    Then on "writeCreate" and also on "writeEdit" I insert the new "manual_creditor"_record when a new value is created by the user and update the "contract" record with the id of the newly created "manual creditor" record.

    ->on( 'writeCreate', function ( $editor, $id, $values ) {
        $manualId = $values['contract']['gov_manual_creditor_id'];
        if ( ( ! is_numeric( $manualId ) ) || //we don't have a valid id => new manual counterparty entered on the fly
             ( count( explode('.', $manualId) ) > 1 ) ) { //strpos didn't work with a period!!
            $result = $editor->db()->insert( 
                            'gov_manual_creditor', array (
                            'gov_id'        => $_SESSION['gov_id'],
                            'name'          => $manualId,
                            'counterparty'  => $values['contract']['counterparty'],
                            'updater_id'    => $_SESSION['orig_id'],
                            'creator_id'    => $_SESSION['orig_id']                    
                            ), array ( 'id' ) );
            $editor->db()->raw()
               ->bind( ':fk', $result->insertId() )
               ->bind( ':id', $id )
               ->exec( 'UPDATE contract   
                           SET gov_manual_creditor_id = :fk, 
                               further_approvals_creditor = 0, 
                               further_approvals_govdept  = 0 
                         WHERE id = :id' );
        }
    } )
    
Sign In or Register to comment.