1

I decided to override one widget there and decided to add some custom settings for it.

The appearance of the widget is controlled by JavaScript function (I use slick slider there, which is suppose to be configurable through the widget settings)

So here is the widget class: (as example from here http://www.wpbeginner.com/wp-tutorials/how-to-create-a-custom-wordpress-widget/)

 <?php

// Creating the widget
class wpb_widget extends WP_Widget
{

    function __construct()
    {
        parent::__construct(
        // Base ID of your widget
        'wpb_widget',

        // Widget name will appear in UI
        __('WPBeginner Widget', 'wpb_widget_domain'),

        // Widget description
        array(
        'description' => __( 'Sample widget based on WPBeginner Tutorial', 'wpb_widget_domain' ),
        'customize_selective_refresh' => true,
        )
            );


             // Enqueue style if widget is active (appears in a sidebar) or if in Customizer preview.
        if ( is_active_widget( false, false, $this->id_base ) || is_customize_preview() ) {
            add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
        }
    }

// Creating widget front-end

    public function widget($args, $instance)
    {
        $title = apply_filters( 'widget_title', $instance['title'] );

        // before and after widget arguments are defined by themes
        echo $args['before_widget'];
        if (! empty( $title )) {
            echo $args['before_title'] . $title . $args['after_title'];
        }

        // This is where you run the code and display the output
        echo __( 'Hello, World!', 'wpb_widget_domain' );
        echo("<div id='customId'>TEST</div>");
        echo $args['after_widget'];

    }

// Widget Backend 
    public function form($instance)
    {
        if (isset( $instance[ 'title' ] )) {
            $title = $instance[ 'title' ];
        } else {
            $title = __( 'New title', 'wpb_widget_domain' );
        }
        // Widget admin form
        ?>
    <p>
        <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?></label> 
        <input class="widefat" 
            id="<?php echo $this->get_field_id( 'title' ); ?>" 
            name="<?php echo $this->get_field_name( 'title' ); ?>" 
            type="text" value="<?php echo esc_attr( $title ); ?>" 
        />
    </p>
<?php

}

public function enqueue_scripts() {

    $randomParameter =  random_int(1, 100);

    $dataToBePassed = array(
        'someParameter' => $randomParameter
    );

    wp_enqueue_script(
        'customjs',
        get_template_directory_uri() . '/js/customwidget.js',
        array( 'jquery','customize-preview' ),
        '',
        true
    );

    wp_localize_script('customjs', 'settings', $dataToBePassed);
}


    // Updating widget replacing old instances with new
    public function update($new_instance, $old_instance)
    {
        $instance = array();
        $instance['title'] = ( ! empty( $new_instance['title'] ) ) ? strip_tags( $new_instance['title'] ) : '';
        return $instance;
    }
} // Class wpb_widget ends here

here is example of binded js:

(function ($) {
    console.log("custom widget js init");

    var configureCustomWidget = function(){
        var me = this,
            conf = settings || {someParameter:5};

        $('#customId').html("<h1>"+conf.someParameter+"</h1>");
    }

    configureCustomWidget();

    // Short-circuit selective refresh events if not in customizer preview or pre-4.5.
    if ('undefined' === typeof wp || !wp.customize || !wp.customize.selectiveRefresh) {
        return;
    }

    // Re-load widgets when a partial is rendered.
    wp.customize.selectiveRefresh.bind('partial-content-rendered', function (placement) {
        debugger;
    });

    // Refresh a moved partial containing 
    wp.customize.selectiveRefresh.bind('partial-content-moved', function (placement) {
        debugger;
    });

})(jQuery);

On preview everything is fine with that, but the problem is that even though I done as it recommended here it just didn't work out because of at the moment of when script initialized for some reason wp.customize.selectiveRefresh is undefined.

Like here: enter image description here

So any ideas of how I can toggle binded widget JavaScript on setting change?

P.S.

It's just a simplified example, there will be an additional options and I want to change, let say just text fields, named 'customId'. And on change I want it to make changes in HTML, like changing some style stuff or etc.

UPD:

(story)

First of all, I am creating the theme for a WordPress site.

This theme should contain some widgets.

One of the widgets is supposed to be extended from woocommerce product category lists. But the default product category list got the output just like a simple bulleted list with description and photo.

So, to make it fancier I decided to make it look like a slider. I decided to use one little JavaScript library for that, named Slick

And the thing is, it really flexible and could be configured, as example, like this:

$('.myCustomSlider').slick({
  slidesToShow: 3,
  slidesToScroll: 1,
  autoplay: true
});

And so, I decided to add to my widget options like you can see in the JavaScript object here, so Slides to show, Slides to scroll etc.

I was thinking that on theme customization screen it would be nice, when the user changes those options value and could see the how changes happen.

(implementation)

To not make this question really wide - full code is here

So, first of all, in my theme, I overrode the default product category widget, changes were simple - just added custom settings into already exists & called slider script at the hook:

 public function __construct()
    {
        $extraSettings = array(
          'slidesCount'  => array(
              'type'  => 'number',
              'step'  => 1,
              'min'   => 1,
              'max'   => '',
              'std'   => 3,
              'label' => __( 'Slides count per page', 'woocommerce' ),
          )
        );

        parent::__construct();

        $this->settings = array_merge($this->settings, $extraSettings);

         // To call script for the slider.
         if ( is_active_widget( false, false, $this->id_base ) || is_customize_preview() ) {
            add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
        }
    }

also, changed <ul> to <div class="product-categories slider"> like that

echo '<div class="product-categories slider">';

wp_list_categories( apply_filters( 'woocommerce_product_categories_widget_args', $list_args ) );

echo '</div>';

I also overrode the widget's default walker (but just made changes in outputting html stuff) like <li> to <div class='slide'>

And then there is my custom JavaScript with bind to events mentioned here.

It works one when loaded. But when I change something it's just no any reaction there. And the reason is the same as I described above -wp.customize.selectiveRefresh is not defined.

But the main question is - how to update client part after my custom/overridden widget settings are updated?

Hope it made some things clear :)

3
  • there are so many missing pieces here, I have no idea what is that you are asking. please edit the question, focus on the problem in the start of it, what is it you are trying to do and failing, than give the background. What is that JS, how is it loaded, for what and when are some of the things I could not understand now Oct 9, 2017 at 8:46
  • @MarkKaplun oh, okay, will update it Oct 9, 2017 at 10:28
  • @MarkKaplun took more time than I tought i would to describe it Oct 9, 2017 at 11:30

1 Answer 1

1

As a role of thumb, you should not extend widgets that you do not control unless their author specifically gave guidance on how to do that. Extending is somewhat easier on the PHP side, but it is hard(er) to extend UI with JS if facilities were not put in place in advance.

A safer (at least from ease of development perspective) way is to "fork" the widget code. Take the code, make whatever modification you need to it, and call the widget a new name, or depending on your needs you can override the "official" name (unregister the original, and register yours under the same handle).

This will mean that with each new version of WC you will need to make sure that you do not need to update your code to reflect a change in the "original" widget, but most likely you would have needed to do that even if you were just "extending".

11
  • well, the thing is, they not really use any js in that. I'd like to fork but even with simple example (added first in the question) I can't make it to stick with my custom js Oct 9, 2017 at 12:50
  • For it to work you will need to know the IDs of the elements, you will need to handle the widget_update and widget_added (IIRC the names) events the customizer raises when widget are add/updated to maybe re-register your handlers. It is doable, it is not very well documented. Your specific problem comes from not initializing your JS and the right time (i.e. probably too early) or trying to use it on the widgets admin window. widget admin window will require some code that is at least somewhat different than the customizer code as things operate a little differently there. Oct 9, 2017 at 12:59
  • hmm, interesting, +1 for now, will try to use it Oct 9, 2017 at 13:19
  • btw is it even good practice? I mean, how normally ppl implement stuff like that? I never worked with wordpress before and got poor php experience Oct 9, 2017 at 13:19
  • poor practice? in what regard? Oct 9, 2017 at 13:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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