1

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

Appearence of the widged is controlled by javascript function (I use slick slider there, which is suppose to be configurable throught 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 https://make.wordpress.org/core/2016/03/22/implementing-selective-refresh-support-for-widgets/

it's just not worked 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 simplified exapmle, 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 chaning some style stuff or etc

UPD:

(story)

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

This theme should contain some widgets.

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

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

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

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

And so, I decided to add to my widget options like you can see in js 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 overrided 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 overrided widget's default walker (but just made changes in outputting html stuff) like <li> to <div class='slide'>

And then there is my custom js 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/overrided widget settings are updated?

Hope it made some things clear :)

  • 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 – Mark Kaplun Oct 9 '17 at 8:46
  • @MarkKaplun oh, okay, will update it – Danil Gholtsman Oct 9 '17 at 10:28
  • @MarkKaplun took more time than I tought i would to describe it – Danil Gholtsman Oct 9 '17 at 11:30
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".

  • 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 – Danil Gholtsman Oct 9 '17 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. – Mark Kaplun Oct 9 '17 at 12:59
  • hmm, interesting, +1 for now, will try to use it – Danil Gholtsman Oct 9 '17 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 – Danil Gholtsman Oct 9 '17 at 13:19
  • poor practice? in what regard? – Mark Kaplun Oct 9 '17 at 13:21

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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