Skip to content

selectmenu or select2 as field types #141

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

Closed
velis74 opened this issue Nov 7, 2015 · 12 comments
Closed

selectmenu or select2 as field types #141

velis74 opened this issue Nov 7, 2015 · 12 comments
Labels

Comments

@velis74
Copy link

velis74 commented Nov 7, 2015

Trying this grid out to see if it suits my needs. Tried to declare custom fields for select that would handle presentation via jQuery UI's selectmenu or with the excellent Select2 component.

Looking at sample code, I thought to just take the declaration for select field and modify it only such that _createSelect function would return e.g. $result.select2() (besides the obvious renamings, of course).

However, this results in no control being rendered. Is what I'm trying to do even possible?

@tabalinas
Copy link
Owner

I guess the problem is the same as in the ticket #72
These controls don't allow initialization on detached DOM nodes. So to make it work you have to put initialization of the widget in setTimeout (as it's done in the example to the ticket above).

@velis74
Copy link
Author

velis74 commented Nov 7, 2015

Thanks, that suggestion worked immediately. Also thanks for immediate response :)
Can I ask you just one more thing: I update select items in run-time. It seems the widget gets created before I update the list of items because the list remains empty when I open the select. I tried reset and it has no effect.
What's the best way to update items in run-time?

@tabalinas
Copy link
Owner

Please, checkout this SO answer http://stackoverflow.com/questions/19899983/how-to-update-data-in-select2-dropdown-using-ajax
Is it what you are talking about?

@velis74
Copy link
Author

velis74 commented Nov 7, 2015

Not really. The problem is the same if I use built-in select control. The select is built with values that were present when I created the jsgrid, not with ones that were present when I clicked the + icon to add a new row. Basically, if I call $("#mygrid").jsGrid("reset"); I'd like the selects to rebuild with current values in the items array.

@tabalinas
Copy link
Owner

Call render method of the grid http://js-grid.com/docs/#render-promise
Actually, in the latest (not released yet) version there is a method fieldOption([fieldName|fieldIndex], optionName, optionValue) to be able to change an option of a field. So it's gonna be documented and provided in the next release.

@velis74
Copy link
Author

velis74 commented Nov 7, 2015

It would seem that jsgrid makes a deep-copy of the items array. I have just debugged and render does in fact call _createSelect, but the items array is not changed. I suppose I'll need to re-declare fields through option command?

@tabalinas
Copy link
Owner

The options are not deeply extended. But the fields are initialized by config provided. Try to assing fields option again.
Why not load items for select first, and then initialize grid?

@velis74
Copy link
Author

velis74 commented Nov 7, 2015

OK, final, working solution:
I tried to assign fields option + calling render function. It worked - kinda. It grid was already displayed, it worked fine. If it was hidden (the grid is placed within an accordion widget), setting fields + rendering somewhat messed it up - the + sign for adding another row wasn't there any more.

So I tried destroying the widget and creating it again. This works as expected in all scenarios I tried.

Also, to answer your question: I can't build the field options ahead because the grid is basically a detail for another master. Each item in master data set has different options for this particular field. So I have to load them and assign them to the field in the grid.

Thanks again for all the help. This is rare. I think I chose the right grid for my project :)

@velis74 velis74 closed this as completed Nov 7, 2015
@tabalinas
Copy link
Owner

Thank you!

@velis74
Copy link
Author

velis74 commented Nov 7, 2015

If you can find any worth in this: here's what I did for select2. The field also adds another field attribute, called imgField. As with valueField and textField, this one points to the items array and displays the image in the select2 widget.

Well, since the code is pretty much the same all around, perhaps target widget could be made a parameter as well so that multiple field types wouldn't be necessary.

If you find this useful, feel free to include it in your excellent widget :)

Code:

(function(jsGrid, $, undefined) {

  var NumberField = jsGrid.NumberField;

  function Select2Field(config) {
    this.items = [];
    this.selectedIndex = -1;
    this.valueField = "";
    this.textField = "";
    this.imgField = "";

    if (config.valueField && config.items.length)
      this.valueType = typeof config.items[0][config.valueField];
    this.sorter = this.valueType;
    NumberField.call(this, config);
  }

  Select2Field.prototype = new NumberField({
    align: "left",
    valueType: "number",

    itemTemplate: function(value) {
      var items = this.items,
          valueField = this.valueField,
          textField = this.textField,
          imgField = this.imgField,
          resultItem;

      if(valueField) {
        resultItem = $.grep(items, function(item, index) {
          return item[valueField] === value;
        })[0] || {};
      }
      else
        resultItem = items[value];

      var result = (textField ? resultItem[textField] : resultItem);
      return (result === undefined || result === null) ? "" : result;
    },

    filterTemplate: function() {
      if(!this.filtering)
        return "";

      var grid = this._grid,
          $result = this.filterControl = this._createSelect();
      this._applySelect($result, this);

      if(this.autosearch) {
        $result.on("change", function(e) {
          grid.search();
        });
      }

      return $result;
    },

    insertTemplate: function() {
      if(!this.inserting)
        return "";

      var $result = this.insertControl = this._createSelect();
      this._applySelect($result, this);
      return $result;
    },

    editTemplate: function(value) {
      if(!this.editing)
        return this.itemTemplate(value);

      var $result = this.editControl = this._createSelect();
      (value !== undefined) && $result.val(value);
      this._applySelect($result, this);
      return $result;
    },

    filterValue: function() {
      var val = this.filterControl.val();
      return this.valueType === "number" ? parseInt(val || 0, 10) : val;
    },

    insertValue: function() {
      var val = this.insertControl.val();
      return this.valueType === "number" ? parseInt(val || 0, 10) : val;
    },

    editValue: function() {
      var val = this.editControl.val();
      return this.valueType === "number" ? parseInt(val || 0, 10) : val;
    },

    _applySelect: function(item, self)
    {
      setTimeout(function() {
        var selectSiteIcon = function(opt)
        {
          var img = '';
          try {
            img = opt.element.attributes.img.value;
          } catch(e) {}
          if (!opt.id || !img)
            return opt.text;
          var res = $('<span><img src="' + img + '" class="img-flag"/> ' + opt.text + '</span>');
          return res;
        }
        item.select2({
          width: self.width,
          templateResult: selectSiteIcon,
          templateSelection: selectSiteIcon
        });
      });
    },

    _createSelect: function() {
      var $result = $("<select>"),
        valueField = this.valueField,
        textField = this.textField,
        imgField = this.imgField,
        selectedIndex = this.selectedIndex;

      $.each(this.items, function(index, item) {
        var value = valueField ? item[valueField] : index,
            text = textField ? item[textField] : item,
            img = imgField ? item[imgField] : '';

        var $option = $("<option>")
            .attr("value", value)
            .attr("img", img)
            .text(text)
            .appendTo($result);

        $option.prop("selected", (selectedIndex === index));
      });

      return $result;
    }
  });

  jsGrid.fields.select2 = jsGrid.Select2Field = Select2Field;

}(jsGrid, jQuery));

@tabalinas
Copy link
Owner

Perfect! Thank you!

@NewBo1234
Copy link

It's worth noting that implementing the Select2 like this gets around the pervasive problem of materialize nuking selects in the jsgrid.

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

No branches or pull requests

3 participants