Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I've posted a bug-report about this a few months ago (on WordPress trac (Widget Instance Form Update Bug)) and I thought I'd try writing about it here too. Maybe someone has a better solution to this issue than me.

Basically the problem is that if you drop a widget into a sidebar, the widget form doesn't get updated until you manually press save (or reload the page).

This makes unusable all the code from the form() function that relies on the widget instance ID to do something (until you press the save button). Any stuff like ajax requests, jQuery things like colorpickers and so on will not work right away, because from that function it would appear that the widget instance has not yet been initialized.

A dirty fix would be to trigger the save button automatically using something like livequery:

$("#widgets-right .needfix").livequery(function(){
  var widget = $(this).closest('div.widget');
  wpWidgets.save(widget, 0, 1, 0);
  return false;
});

and add the .needfix class in form() if the widget instance doesn't look initialized:

 <div <?php if(!is_numeric($this->number)): ?>class="needfix"<?php endif; ?>
   ...
 </div>

One drawback of this solution is that if you have lots of widgets registered, the browser will eat lots of CPU, because livequery checks for DOM changes every second (tho I didn't specifically test this, it's just my assumption :)

Any suggestions for a better way to fix the bug?

share|improve this question
    
Instead of triggering a full save submit, wouldn't it make more sense to look inside what pressing the save button triggers to provide the needed ID, separate that code and call it at the end of the dropping operation instead? –  hakre Feb 6 '11 at 17:12

2 Answers 2

up vote 3 down vote accepted

I did battle with a similar situation recently. Ajax in widgets is no joke! Need to write some pretty crazy code to get things to work across instances. I'm not familiar with live query, but if you say it checks the DOM every second, I might have a less intense solution for you:

var get_widget_id = function ( selector ) {
    var selector, widget_id = false;
    var id_attr = $( selector ).closest( 'form' ).find( 'input[name="widget-id"]' ).val();
    if ( typeof( id_attr ) != 'undefined' ) {
        var parts = id_attr.split( '-' );
        widget_id = parts[parts.length-1];
    }
    return parseInt( widget_id );
};

You can pass this function a selector or jQuery object and it will return the instance ID of the current instance. I could find no other way around this issue. Glad to hear I'm not the only one :)

share|improve this answer

I don't like answering my own question, but I feel this is the best solution so far:

$('#widgets-right').ajaxComplete(function(event, XMLHttpRequest, ajaxOptions){

  // determine which ajax request is this (we're after "save-widget")
  var request = {}, pairs = ajaxOptions.data.split('&'), i, split, widget;

  for(i in pairs){
    split = pairs[i].split('=');
    request[decodeURIComponent(split[0])] = decodeURIComponent(split[1]);
  }

  // only proceed if this was a widget-save request
  if(request.action && (request.action === 'save-widget')){

    // locate the widget block
    widget = $('input.widget-id[value="' + request['widget-id'] + '"]').parents('.widget');

    // trigger manual save, if this was the save request 
    // and if we didn't get the form html response (the wp bug)
    if(!XMLHttpRequest.responseText)
      wpWidgets.save(widget, 0, 1, 0);

    // we got an response, this could be either our request above,
    // or a correct widget-save call, so fire an event on which we can hook our js
    else
      $(document).trigger('saved_widget', widget);

  }

});

This will fire the widget-save ajax request, just after a widget-save request has completed (if there was no response with the form html).

It needs to be added in the jQuery(document).ready() function.

Now, if you want to easily re-attach your javascript functions to the new DOM elements added by the widget form function simply bind them to the "saved_widget" event:

$(document).bind('saved_widget', function(event, widget){
  // For example: $(widget).colorpicker() ....
});
share|improve this answer
2  
Note that As of jQuery 1.8, the .ajaxComplete() method should only be attached to document. - api.jquery.com/ajaxComplete Thus the first line of your snippet should read: $( document ).ajaxComplete(function(event, XMLHttpRequest, ajaxOptions){ At least for WP 3.6+ –  David Laing Aug 25 '13 at 13:39

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.