Sürükle ve bırak işleminden sonra widget formunu güncelleme (WP kaydetme hatası)


15

Birkaç ay önce bu konuda bir hata raporu gönderdim ( WordPress trac (Widget Instance Form Güncelleme Hatası) ) ve burada da yazmayı deneyeceğim diye düşündüm. Belki birinin bu konuda benden daha iyi bir çözümü vardır.

Temel olarak sorun, bir widget'ı kenar çubuğuna bırakırsanız, widget'ı elle kaydet düğmesine basana kadar (veya sayfayı yeniden yükleyene kadar) güncellenmez.

Bu, bir form()şey yapmak için (kaydet düğmesine basana kadar) widget örneği kimliğine dayanan işlevdeki tüm kodu kullanılamaz hale getirir . Ajax istekleri, colorpickers gibi jQuery gibi şeyler hemen çalışmayacak, çünkü bu işlevden widget örneğinin henüz başlatılmadığı anlaşılıyor.

Kirli bir düzeltme, livequery gibi bir şey kullanarak kaydet düğmesini otomatik olarak tetiklemek olacaktır :

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

ve widget örneği başlatılmış görünmüyorsa .needfixsınıfı ekleyin form():

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

Bu çözümün bir dezavantajı, kayıtlı çok sayıda widget'ınız varsa, tarayıcının çok fazla CPU yiyeceğidir, çünkü livequery DOM saniyelerini her saniye değiştirir (özellikle bunu test etmedim, bu sadece benim varsayımım :)

Hatayı düzeltmenin daha iyi bir yolu için herhangi bir öneriniz var mı?


Tam bir kaydetme gönderimini tetiklemek yerine, kaydetme düğmesine basmak için gereken kimliği sağlamak, bu kodu ayırmak ve bunun yerine bırakma işleminin sonunda çağırmak için neyin tetiklendiğine bakmak daha anlamlı olmaz mıydı?
hakre

Yanıtlar:


5

Son zamanlarda benzer bir durumla savaştım. Widget'larda Ajax şaka değil! Örnekler arasında çalışmak için bazı çılgın kodlar yazmanız gerekiyor. Canlı sorguya aşina değilim, ancak DOM'u her saniye kontrol ederseniz, sizin için daha az yoğun bir çözüme sahip olabilirim:

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 );
};

Bu işlevi bir seçici veya jQuery nesnesi iletebilirsiniz ve geçerli örneğin örnek kimliğini döndürür. Bu konuda başka bir yol bulamadım. Sadece ben olmadığımı duyduğuma sevindim :)


7

Kendi soruma cevap vermekten hoşlanmıyorum, ancak şimdiye kadar bunun en iyi çözüm olduğunu hissediyorum:

$('#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);

  }

});

Bu, bir widget kaydetme isteği tamamlandıktan hemen sonra (html formunda yanıt yoksa) widget kaydetme ajax isteğini tetikler.

İşlevine eklenmesi gerekir jQuery(document).ready().

Şimdi, javascript işlevlerinizi widget form işlevi tarafından eklenen yeni DOM öğelerine kolayca yeniden eklemek istiyorsanız, bunları yalnızca "saved_widget" etkinliğine bağlayın:

$(document).bind('saved_widget', function(event, widget){
  // For example: $(widget).colorpicker() ....
});

3
JQuery 1.8 itibariyle .ajaxComplete () yönteminin yalnızca belgeye eklenmesi gerektiğini unutmayın. - api.jquery.com/ajaxComplete Bu yüzden snippet'inizin ilk satırında şunlar olmalıdır: $ (document) .ajaxComplete (function (event, XMLHttpRequest, ajaxOptions) {En azından WP 3.6+ için
David Laing

3

Bu son zamanlarda koştu ve geleneksel "widgets.php" arayüzünde herhangi bir javascript başlatmanın mevcut widget'lar için (div'de olanlar #widgets-right) doğrudan ve dolaylı olarak widget-addedyeni eklenen widget'lar için olay yoluyla çalıştırılması gerektiği görülmektedir ; oysa "custom.php" arayüzünde mevcut ve yeni tüm widget'lar widget-addedetkinliğe gönderilir, böylece sadece orada başlatılabilir. Buna dayanarak, WP_Widgetbir işlev geçersiz kılınarak bir widget'ın formuna javascript başlatmayı eklemeyi kolaylaştıran sınıfın bir uzantısı aşağıdadır form_javascript_init():

class WPSE_JS_Widget extends WP_Widget { // For widgets using javascript in form().
    var $js_ns = 'wpse'; // Javscript namespace.
    var $js_init_func = ''; // Name of javascript init function to call. Initialized in constructor.
    var $is_customizer = false; // Whether in customizer or not. Set on 'load-customize.php' action (if any).

    public function __construct( $id_base, $name, $widget_options = array(), $control_options = array(), $js_ns = '' ) {
        parent::__construct( $id_base, $name, $widget_options, $control_options );
        if ( $js_ns ) {
            $this->js_ns = $js_ns;
        }
        $this->js_init_func = $this->js_ns . '.' . $this->id_base . '_init';
        add_action( 'load-widgets.php', array( $this, 'load_widgets_php' ) );
        add_action( 'load-customize.php', array( $this, 'load_customize_php' ) );
    }

    // Called on 'load-widgets.php' action added in constructor.
    public function load_widgets_php() {
        add_action( 'in_widget_form', array( $this, 'form_maybe_call_javascript_init' ) );
        add_action( 'admin_print_scripts', array( $this, 'admin_print_scripts' ), PHP_INT_MAX );
    }

    // Called on 'load-customize.php' action added in constructor.
    public function load_customize_php() {
        $this->is_customizer = true;
        // Don't add 'in_widget_form' action as customizer sends 'widget-added' event to existing widgets too.
        add_action( 'admin_print_scripts', array( $this, 'admin_print_scripts' ), PHP_INT_MAX );
    }

    // Form javascript initialization code here. "widget" and "widget_id" available.
    public function form_javascript_init() {
    }

    // Called on 'in_widget_form' action (ie directly after form()) when in traditional widgets interface.
    // Run init directly unless we're newly added.
    public function form_maybe_call_javascript_init( $callee_this ) {
        if ( $this === $callee_this && '__i__' !== $this->number ) {
            ?>
            <script type="text/javascript">
            jQuery(function ($) {
                <?php echo $this->js_init_func; ?>(null, $('#widgets-right [id$="<?php echo $this->id; ?>"]'));
            });
            </script>
            <?php
        }
    }

    // Called on 'admin_print_scripts' action added in constructor.
    public function admin_print_scripts() {
        ?>
        <script type="text/javascript">
        var <?php echo $this->js_ns; ?> = <?php echo $this->js_ns; ?> || {}; // Our namespace.
        jQuery(function ($) {
            <?php echo $this->js_init_func; ?> = function (e, widget) {
                var widget_id = widget.attr('id');
                if (widget_id.search(/^widget-[0-9]+_<?php echo $this->id_base; ?>-[0-9]+$/) === -1) { // Check it's our widget.
                    return;
                }
                <?php $this->form_javascript_init(); ?>
            };
            $(document).on('widget-added', <?php echo $this->js_init_func; ?>); // Call init on widget add.
        });
        </script>
        <?php
    }
}

Bunu kullanarak örnek bir test widget'ı:

class WPSE_Test_Widget extends WPSE_JS_Widget {
    var $defaults; // Form defaults. Initialized in constructor.

    function __construct() {
        parent::__construct( 'wpse_test_widget', __( 'WPSE: Test Widget' ), array( 'description' => __( 'Test init of javascript.' ) ) );
        $this->defaults = array(
            'one' => false,
            'two' => false,
            'color' => '#123456',
        );
        add_action( 'admin_enqueue_scripts', function ( $hook_suffix ) {
            if ( ! in_array( $hook_suffix, array( 'widgets.php', 'customize.php' ) ) ) return;
            wp_enqueue_script( 'wp-color-picker' ); wp_enqueue_style( 'wp-color-picker' );
        } );
    }

    function widget( $args, $instance ) {
        extract( $args );
        extract( wp_parse_args( $instance, $this->defaults ) );

        echo $before_widget, '<p style="color:', $color, ';">', $two ? 'Two' : ( $one ? 'One' : 'None' ), '</p>', $after_widget;
    }

    function update( $new_instance, $old_instance ) {
        $new_instance['one'] = isset( $new_instance['one'] ) ? 1 : 0;
        $new_instance['two'] = isset( $new_instance['two'] ) ? 1 : 0;
        return $new_instance;
    }

    function form( $instance ) {
        extract( wp_parse_args( $instance, $this->defaults ) );
        ?>
        <div class="wpse_test">
            <p class="one">
                <input class="checkbox" type="checkbox" <?php checked( $one ); disabled( $two ); ?> id="<?php echo $this->get_field_id( 'one' ); ?>" name="<?php echo $this->get_field_name( 'one' ); ?>" />
                <label for="<?php echo $this->get_field_id( 'one' ); ?>"><?php _e( 'One?' ); ?></label>
            </p>
            <p class="two">
                <input class="checkbox" type="checkbox" <?php checked( $two ); disabled( $one ); ?> id="<?php echo $this->get_field_id( 'two' ); ?>" name="<?php echo $this->get_field_name( 'two' ); ?>" />
                <label for="<?php echo $this->get_field_id( 'two' ); ?>"><?php _e( 'Two?' ); ?></label>
            </p>
            <p class="color">
                <input type="text" value="<?php echo htmlspecialchars( $color ); ?>" id="<?php echo $this->get_field_id( 'color' ); ?>" name="<?php echo $this->get_field_name( 'color' ); ?>" />
            </p>
        </div>
        <?php
    }

    // Form javascript initialization code here. "widget" and "widget_id" available.
    function form_javascript_init() {
        ?>
            $('.one input', widget).change(function (event) { $('.two input', widget).prop('disabled', this.checked); });
            $('.two input', widget).change(function (event) { $('.one input', widget).prop('disabled', this.checked); });
            $('.color input', widget).wpColorPicker({
                <?php if ( $this->is_customizer ) ?> change: _.throttle( function () { $(this).trigger('change'); }, 1000, {leading: false} )
            });
        <?php
    }
}

add_action( 'widgets_init', function () {
    register_widget( 'WPSE_Test_Widget' );
} );

2

Wordpress 3.9'da size yardımcı olabilecek bir şey olduğunu düşünüyorum. Bu var widget güncellenmiş geri arama. Şöyle kullanın (kahve):

$(document).on 'widget-updated', (event, widget) ->
    doWhatINeed() if widget[0].id.match(/my_widget_name/)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.