Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x-editables with select2 #431

Open
phorisc opened this issue Nov 5, 2013 · 22 comments
Open

x-editables with select2 #431

phorisc opened this issue Nov 5, 2013 · 22 comments

Comments

@phorisc
Copy link

phorisc commented Nov 5, 2013

I am using select2 with x-editables. Select2 allows me to modify the query field and i can append a new item to select in the drop down if it doesnt currently exist. Though when i select it and hit the check button the x-editable field remains with the "Empty" text that it originally had but in the back end it knows the right value. Is there somthing i need to do to make it so it updates the "Empty" text to the text of what i selected in the select2 drop down?

@phorisc
Copy link
Author

phorisc commented Nov 5, 2013

screen shot 2013-11-05 at 2 24 27 pm
screen shot 2013-11-05 at 2 18 23 pm
screen shot 2013-11-05 at 2 18 32 pm
screen shot 2013-11-05 at 2 24 13 pm

Thought maybe some pictures might help explain what im seeing.

@vitalets
Copy link
Owner

vitalets commented Nov 9, 2013

hi @phorisc
could you share the code how you init editable field?

@phorisc
Copy link
Author

phorisc commented Nov 11, 2013

select2: {
    minimumInputLength: 1,
    source: window.choices,
    query: function (query) {
        var data = window.choices;
        data.results.push({id: 0, text: query.term});        
        query.callback(data);
    }
}

So I have since found a workaround to get through this by using just the select2 in a bootstrap popup. But it would be nice if this did work. Not sure if its somthing im doing wrong or if its a bug. The code for doing this kind of thing is found on the select2 github documentation.
screen shot 2013-11-11 at 10 48 52 am

@cmartin81
Copy link

I need this function aswell

@ruuter
Copy link

ruuter commented Apr 24, 2014

Same or similar problem here. After saving new value with select2, editable text node sets to 'Empty'. Success function returns correct value and server-side is also updated, but text node switch to 'Empty' every time.
I even tried to manually set value with:

success: function(response, newValue){
    console.log($(this).html());
    $(this).editable('setValue', newValue);
    console.log($(this).html());
}

First console log call returns text value of previous select and second returns Empty.
Actually, calling setValue at any point, behaves the same, even called from, console.

@ruuter
Copy link

ruuter commented Apr 28, 2014

I solved my problem with 'display' function. Hope it helps someone.
( data is pre-loaded with custom ajax function. Left it out here for the sake of clarity. )

display: function (value)
{
    if (!value) {
        $(this).empty();
        return;
    }
    var match = jQuery.grep(data, function( o ){ return ( o.id == value ); });
    if(!match[0]){
        return;
    }
    var html = match[0].text;
    $(this).html(html);
},

Now text node updates after saving value with select2.

Second version, with support to multiple values:

display: function (value)
{
    var html='';
    if(!value){
        $(this).empty();
        return;
    }
    if($.isArray(value)){
        if(!value.length){
            $(this).empty();
            return;
        }
        $(value).each(function(i,e){
            var match = jQuery.grep(data, function( o ){ return ( o.id == e ); });
            if(match[0]){
                html += match[0].text;
                if(i+1 < value.length) html+=",";
            }
        });
    }else{
        var match = jQuery.grep(data, function( o ){ return ( o.id == value ); });
        if(!match[0]) return;
        html = match[0].text;
    }
    $(this).html(html);
},

@Ninodevo
Copy link

Hi...I cant get it to work. I'm using xeditable for yii...has someone an example?

@renierdbruyn
Copy link

@Ninodevo check here: http://x-editable.demopage.ru/

@AndrewEastwood
Copy link

doesn't work for me as well. Once I get any option selected and by clicking the save button I get 'Empty' text again.

@AndrewEastwood
Copy link

on the example page http://vitalets.github.io/x-editable/demo-bs3.html the selected value is being sent to server but what about the case when I don't do that and just display the selected?

@IvoPereira
Copy link

+1 on this. How could we achieve dynamic select2 data with the possibility of adding new items in X-Editable?

@rjsmith
Copy link

rjsmith commented Jan 26, 2015

Hi,
I have just spent a few hours figuring out why xEditable kept showing 'Empty' when I was using type='select2'. I think I have finally figured out what you have to do to make it work, by looking through the code here.

I think the reason is the code in this section in the value2html function:

       value2html: function(value, element) {
           var text = '', data,
               that = this;

           if(this.options.select2.tags) { //in tags mode just assign value
              data = value; 
              //data = $.fn.editableutils.itemsByValue(value, this.options.select2.tags, this.idFunc);
           } else if(this.sourceData) {
              data = $.fn.editableutils.itemsByValue(value, this.sourceData, this.idFunc); 
           } else {
              //can not get list of possible values 
              //(e.g. autotext for select2 with ajax source)
           }

           //data may be array (when multiple values allowed)
           if($.isArray(data)) {
               //collect selected data and show with separator
               text = [];
               $.each(data, function(k, v){
                   text.push(v && typeof v === 'object' ? that.formatSelection(v) : v);
               });
           } else if(data) {
               text = that.formatSelection(data);
           }

           text = $.isArray(text) ? text.join(this.options.viewseparator) : text;

           //$(element).text(text);
           Constructor.superclass.value2html.call(this, text, element); 

Basically, if the select2 is not in 'tags' mode, you have to provide a sourceData to set the data variable, which is then used to set the text. The only way to ensure that the sourceData is set, is to specify a source xEditable option, as can be seen from this code section here:

        if(!options.select2.tags && options.source) {
            var source = options.source;
            //if source is function, call it (once!)
            if ($.isFunction(options.source)) {
                source = options.source.call(options.scope);
            }               

            if (typeof source === 'string') {
                options.select2.ajax = options.select2.ajax || {};
                //some default ajax params
                if(!options.select2.ajax.data) {
                    options.select2.ajax.data = function(term) {return { query:term };};
                }
                if(!options.select2.ajax.results) {
                    options.select2.ajax.results = function(data) { return {results:data };};
                }
                options.select2.ajax.url = source;
            } else {
                //check format and convert x-editable format to select2 format (if needed)
                this.sourceData = this.convertSource(source);
                options.select2.data = this.sourceData;
            }
        } 

What it took me hours to figure out was that my original code was not setting the source xEditable option, but setting the select2 data option directly in the select2 property. As soon as I replaced the select2.data option with the source option instead, xEditable displayed the text of the selected entry as expected.

I think that either the xEditable select2 documentation section could be altered to make this much clearer, or consider enhancing the xEditable select2 class constructor method so that it will set the sourceData directly from the select2 data option if it has been set instead of the xEditable source.

Hope this helps anyone encountering the same issue as I did, might save you a wasted evening :-)

@Miges
Copy link

Miges commented Feb 16, 2015

Thanks! You saved me the rest of a wasted evening!

@simplenotezy
Copy link

Thanks, but can you provide with a JSFiddle or some code?

@ihelmer07
Copy link

@rjsmith - any chance at all of providing a fiddle or some code with this working? You'd save my bacon!

@rjsmith
Copy link

rjsmith commented Oct 14, 2017

I am using xEditable within a Meteor.js application via this Meteor lightweight wrapper package. Here is one example of a Meteor Blaze template that wraps a select2 xEditable widget. As I described above, the key here is to pass a 'source' attribute to xEditable.

<template name='xEditableSelectRequirementComponent'>
  {{> xEditable type="select2" success=onSuccess mode="inline" placeholder="Select a Requirement" select2=getSelect2Options source=getSource value=initialValue}}
</template>
Template.xEditableSelectRequirementComponent.created = function () {
  var tmpl = this;

  // Get contents of select2 list and store it in template instance
  tmpl.select2Source = select2Source(tmpl.data);

};

Template.xEditableSelectRequirementComponent.helpers({

   // Provides xEditable 'source' attribute which provides data list for
   // select2 and also enables xEditable to display correct text.
   // Displaying text does NOT work if you specify the select2 'data' option directly!
   // See: https://github.com/vitalets/x-editable/blob/master/src/inputs/select2/select2.js#L171
   getSource: function () {
      var tmpl = Template.instance();
      return tmpl.select2Source;
   },

   getSelect2Options: function () {
    var self = this,
    tmpl = Template.instance()
    ;
    return {
      placeholder: 'Select a Requirement',
      allowClear: false,
      width: '100%',
      formatResult: formatRequirementOptions,
      formatSelection: formatRequirementSelection,
      initSelection: function (element, callback) {
        var initRequirement = _.find(tmpl.select2Source, function(item) {
          return item.id === element.val();
        });
        if (initRequirement) {
          callback(initRequirement);
        } else {
          callback();
        }
      }
     }
  },

  onSuccess: function () {
    var self = this,
    tmpl = Template.instance();
    // Provides xEditable success callback
    return function(res, requirementId) {
      if (_.isFunction(self.onSelectedRequirement)) {
        self.onSelectedRequirement(requirementId);        
      }
    }
  },

  initialValue: function () {
    var self = this;
    return self.requirement._id;
  }

});

/**
 * Helpers for creating requirement - lookup select2 lists
 */
function formatRequirementOptions(requirement) {
  var _html = "<div><span>" + _.escape(requirement.text) + "</span>" +
    "<span class='label label-info'>" + 
    Plangy.Utils.camelCaseToTitleCase(requirement.type)+ "</div>";
  return _html;
}

function formatRequirementSelection(requirement) {
var _html = 
  _.escape(requirement.text);
return _html;
}

function select2Source(dataContext) {
  // Fetch list of requirements
  var _requirements = dataContext.requirementsCursor.fetch();
  // Transform into select2 - friendly data array
  var _data = _.map(_requirements, function (requirement) {
    return {
      id:   requirement._id,
      text: requirement.tag,
      type: requirement.type
    };
  });
  return _data;
}

@ihelmer07
Copy link

ihelmer07 commented Oct 14, 2017

Thank you very much for that - except I'm not using meteor, Could you post your final rendered version of your js (if that's possible?) I don't know much about meteor and I'm failing to understand what your JS would look like after template processing.

Also - what version of select2 and xeditable are you using?

Here is my fully rendered JS - maybe you can see what I can't:

    $('#example .eo_role a').editable( {
        params: function(params) {  //params already contain `name`, `value` and `pk`
          var data = {};
          data[params.name] = params.value;
          return data;
        },
        source: 'http://localhost:8000/api/eo_role/select_two_data/',
        //have to set tpl to correct label or else it won't register what type
        tpl: '<select></select>',
        //set ajax to put
        ajaxOptions: {
            type: 'put'
            },
            
            
        select2: {
            //cacheData doesn't seem to work but i'm keeping it
            cacheDatasource:true,
            width: '150px',
            id: function(pk) {
                return pk.id;
            },
             //this doesn't seem to work
            source: 'http://localhost:8000/api/eo_role/select_two_data/',
            formatSelection: function (item) {
                return item.text;
            },
            ajax: {
                url: 'http://localhost:8000/api/eo_role/select_two_data/',
                //this doesn't seem to work either
                source: 'http://localhost:8000/api/eo_role/select_two_data/',
                dataType: "json",
                type: 'GET',
                processResults: function(item) {return item;},
                },

            },
   
 
        formatSelection: function (item) {
            return item.text;
        },
        formatResult: function (item) {
            return item.text;
        },
        templateResult: function (item) {
            return item.text;
        },
        templateSelection : function (item) {
            return item.text;
        },  

    }); 

@rjsmith
Copy link

rjsmith commented Oct 14, 2017

I'm using xEditable bootstrap v1.5.1, with select2 v3.5.1
It's not really possible for me to show the compiled javascript, its all tied up with the Meteor framework. You have not said what the problem is you want to resolve. I can only suggest you do what I did ... put breakpoints in the xEditable code in your browser and step through it until you figure out what the root cause of whatever your issue is. In my code, I provide the xEditable 'source' attribute which is assigned to the select2 'data' property .. in my code the 'source' is a JSON array, not an ajax url. You might want to simplify your code and try a dummy array source first, to focus on xEditable itself instead of the extra complexity of your ajax call too. Best wishes!

@ihelmer07
Copy link

Sorry - the problem is that using select2 dropdown, after successful selection the cell displays 'Empty' exactly like the initial post. The server is updated via ajax call successfully however it just doesn't appear so due to it showing 'empty' in the cell after selecting. You have to reload the page (and pull from the server again) in order to get it to show the correct value.

I'll see what I can do to add breakpoints in the select2 code, I may revert back to 3.5.1, I am using 4.4 currently.

@rjsmith
Copy link

rjsmith commented Oct 14, 2017 via email

@ihelmer07
Copy link

ihelmer07 commented Oct 14, 2017

I figured out that like you - datasource isn't being set during this function call:

    $.extend(Constructor.prototype, {
        render: function() {
            this.setClass();

            //can not apply select2 here as it calls initSelection 
            //over input that does not have correct value yet.
            //apply select2 only in value2input
            //this.$input.select2(this.options.select2);

            //when data is loaded via ajax, we need to know when it's done to populate listData
            if(this.isRemote) {
                //listen to loaded event to populate data
                this.$input.on('select2-loaded', $.proxy(function(e) {
// ****** THIS IS NEVER setting this.sourceData for some reason. List still populates however.
                    this.sourceData = e.items.results;
                }, this));
            }

            //trigger resize of editableform to re-position container in multi-valued mode
            if(this.isMultiple) {
               this.$input.on('change', function() {
                   $(this).closest('form').parent().triggerHandler('resize');
               });
            }
       },

Then because this.sourceData isn't set, the next value2html fails out, having an empty array.

       value2html: function(value, element) {
           var text = '', data,
               that = this;

           if(this.options.select2.tags) { //in tags mode just assign value
              data = value; 
              //data = $.fn.editableutils.itemsByValue(value, this.options.select2.tags, this.idFunc);
           } else if(this.sourceData) {
              data = $.fn.editableutils.itemsByValue(value, this.sourceData, this.idFunc); 
           } else {
              //****THIS IS WHERE WE END UP, meaning data never gets set meaning it displays empty.
              // If I knew how to translate an ID value to a text we'd be in business here, but I don't
              //***********************************************************
              //can not get list of possible values 
              //(e.g. autotext for select2 with ajax source)
           }

           //data may be array (when multiple values allowed)
           if($.isArray(data)) {
               //collect selected data and show with separator
               text = [];
               $.each(data, function(k, v){
                   text.push(v && typeof v === 'object' ? that.formatSelection(v) : v);
               });
           } else if(data) {

//**************************
///this goes to the xeditable formatSelection (which you need to specify), however there is no ability to 
take the value given and use the Json object to convert it to text.
//************************

               text = that.formatSelection(data);
           }

           text = $.isArray(text) ? text.join(this.options.viewseparator) : text;

           //$(element).text(text);
           Constructor.superclass.value2html.call(this, text, element); 
       },

I could force the array into the 'tags' case using tags:true to set the data variable, enabling the rest of the logic to go through - however then it goes to the editable.formatSelection function, which needs to convert the value (id) into the text of the item, which it doesn't because it is being pass the string, not the string and objects array.

I can't seem to find a way to get access to the json objects array within that function.

@omarkasem
Copy link

omarkasem commented Dec 16, 2021

Here's what worked for me with select2 (v4.0.13) and x-editable (v1.5.1)

$(editable.input.$input).on('select2:select', function (e) {
var data = e.params.data;
editable.options.display = function(value,source){
$(this).html(data.text);
};
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests