Dynamically updating a TinyMCE 4 ListBox

11,413

Solution 1

I found this on the TinyMCE forum and I have confirmed that it works:

tinymce.PluginManager.add('myexample', function(editor, url) {
   var self = this, button;

   function getValues() {
      return editor.settings.myKeyValueList;
   }
   // Add a button that opens a window
   editor.addButton('myexample', {
      type: 'listbox',
      text: 'My Example',
      values: getValues(),
      onselect: function() {
         //insert key
         editor.insertContent(this.value());

         //reset selected value
         this.value(null);
      },
      onPostRender: function() {
         //this is a hack to get button refrence.
         //there may be a better way to do this
         button = this;
      },
   });

   self.refresh = function() {
      //remove existing menu if it is already rendered
      if(button.menu){
         button.menu.remove();
         button.menu = null;
      }

      button.settings.values = button.settings.menu = getValues();
   };
});


Call following code block from ajax success method
//Set new values to myKeyValueList 
tinyMCE.activeEditor.settings.myKeyValueList = [{text: 'newtext', value: 'newvalue'}];
//Call plugin method to reload the dropdown
tinyMCE.activeEditor.plugins.myexample.refresh();

The key here is that you need to do the following:

  1. Get the 'button' reference by taking it from 'this' in the onPostRender method
  2. Update the button.settings.values and button.settings.menu with the values you want
  3. To update the existing list, call button.menu.remove() and button.menu = null

Solution 2

I tried the solution from TinyMCE forum, but I found it buggy. For example, when I tried to alter the first ListBox multiple times, only the first time took effect. Also first change to that box right after dialogue popped up didn't take any effect.

But to the solution:

Do not call button.menu.remove();

Also, the "hack" for getting button reference is quite unnecessary. Your job can be done simply using:

var button = win.find("#button")[0]; 

With these modification, my ListBoxes work just right.

Whole dialogue function:

function ShowDialog() {
  var val;
  win = editor.windowManager.open({
          title: 'title',
          body: {type: 'form', 
          items: [
          {type: 'listbox', 
          name: 'categorybox', 
          text: 'pick one', 
          value: 0,
          label: 'Section: ', 
          values: categories,
          onselect: setValuebox(this.value())        
          },
          {type: 'listbox', 
          name: 'valuebox', 
          text:'pick one', 
          value: '',
          label: 'Page: ', 
          values: pagelist[0],
            onselect: function(e) {
              val = this.value();
            }
          }
          ]
        },
                onsubmit: function(e) {
                    //do whatever
                }
            });

      var valbox = win.find("#valuebox")[0]; 

      function setValuebox(i){
      //feel free to call ajax
      valbox.value(null);              
      valbox.menu = null;
      valbox.settings.menu = pagelist[i]; 
      // you can also set a value from pagelist[i]["values"][0]
      }
  }

categories and pagelist are JSONs generated from DB before TinyMCE load. pagelist[category] = data for ListBox for selected category. category=0 means all.

Hope I helped somebody, because I've been struggling this for hours.

Solution 3

It looks like the tinyMCE version that is included in wordpress 4.3 changed some things, and added a state object that caches the initial menu, so changing the menu is not enough anymore.

One will probably have to update the state object as well. Here is an example of updating the menu with data coming from an ajax request:

editor.addButton('shortcodes', {
        icon: 'icon_shortcodes',
        tooltip: 'Your tooltip',
        type: 'menubutton',
        onPostRender: function() {
            var ctrl = this;
            $.getJSON( ajaxurl , function( menu) {
                // menu is the array containing your menu items
                ctrl.state.data.menu = ctrl.settings.menu = menu;
            });
        }
    });

Solution 4

As far as I can tell, these other approaches are broken in TinyMCE 4.9. After spending most of the day tinkering to fix my own usage of these approaches, this is the working function I've found:

function updateListbox(win, data) { // win is a tinymce.ui.Window
  listbox = win.find('#listbox'); // Substitute your listbox 'name'
  formItem = listbox.parent();

  listbox.remove();
  formItem.append({
    label: 'Dynamic Listbox',
    type: 'listbox',
    name: 'listbox',
    values: data
  });
}
Share:
11,413
spud
Author by

spud

Updated on July 20, 2022

Comments

  • spud
    spud almost 2 years

    I'm trying to modify the TinyMCE 4 "link" plugin to allow users to select content from ListBox elements that are dynamically updated by AJAX requests.

    I'm creating the ListBox elements in advance of editor.windowManager.open(), so they are initially rendered properly. I have an onselect handler that performs the AJAX request, and gets a response in JSON format.

    What I need to do with the JSON response is to have it update another ListBox element, replacing the existing items with the new results.

    I'm baffled, and the documentation is terribly unclear. I don't know if I should replace the entire control, or delete items and then add new ones. I don't know if I need to instantiate a new ListBox control, or render it to HTML, etc.

    Basically, I have access to the original rendered ListBox (name: "module"} with

    win.find('#module');

    I have the new values from the AJAX request:

    var data = tinymce.util.JSON.parse(text).data;

    And I've tried creating a new Control configuration object, like

    newCtrlconfig = {
        type: 'listbox',
        label: 'Class',
        values: data
    };
    

    but I wouldn't know how to render it, much less have it replace the existing one.

    I tried

    var newList = tinymce.ui.Factory.create(newCtrlconfig);

    and then

    newList.renderHtml()

    but even then, the rendered HTML did not contain any markup for the items. And examining these objects is just frustrating: there are "settings", "values", "_values", "items" all of which will happily store my values, but it isn't even clear which of them will work.

    Since it's a ListBox and not a simple SELECT menu, I can't even easily use the DOM to manipulate the values.

    Has anyone conquered the TinyMCE ListBox in 4.x?

  • Jobin
    Jobin about 10 years
    Thanks Moeri Im looking for reset option! //reset selected value this.value(null);
  • ThePengwin
    ThePengwin over 8 years
    The version of tinyMCE this affects is 4.2 thanks for pointing out this change.
  • Vasu Sheoran
    Vasu Sheoran about 8 years
    hey, I am trying to add a list and did as you said, but after myexample.refresh(), i can see 'My Example' button in but it does not have any option ie list is empty. Also i cant see this in menubar and had to put toolbar : 'myexample' to see the button.
  • RestlessWeb
    RestlessWeb almost 8 years
    For a listbox button this ended up being ctrl.state.data.menu = ctrl.settings.values = menu; for me instead of settings.menu. This is for WP 4.5.1 and TinyMCE 4.3.10
  • Tiberiu Petcu
    Tiberiu Petcu about 7 years
    this is still accurate for WP 4.7.2
  • LukaszQr
    LukaszQr about 7 years
    For Tinymce 4.3+ you have to use: button.settings.values = button.state.data.menu = items;
  • Vijay Sharma
    Vijay Sharma almost 6 years
    First please learn how to add custom plugin then only you can understand how to integrate in your code