Image uploader widget is not previewing selected image

I’ve been trying to develop an image widget plugin for WordPress, and I’ve almost successfully done that thanks to the following code:

<?php
/*
Plugin Name: Title Page Widget (PB)
Plugin URI: http://www.example.com
Description: Creates a full-screen title page for a post (designed to used with Site Origin Page Builder)
Author: Me
Version: 1.0
Author URI: http://www.example.com
*/

// Block direct requests
if ( !defined('ABSPATH') )
    die('-1');

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


/**
 * Adds Title_Page_Widget widget.
 */
class Title_Page_Widget extends WP_Widget {

    /**
     * Register widget with WordPress.
     */
    function __construct() {
        parent::__construct(
            'Title_Page_Widget', // Base ID
            __('Title Page Widget (PB)', 'text_domain'), // Name
            array('description' => __( 'Creates a full-screen title page - designed for use with Site Origin's Page Builder plugin', 'text_domain' ),) // Args
        );

        add_action( 'sidebar_admin_setup', array( $this, 'admin_setup' ) );

    }

    function admin_setup(){

        wp_enqueue_media();
        wp_register_script('tpw-admin-js', plugins_url('tpw_admin.js', __FILE__), array( 'jquery', 'media-upload', 'media-views' ) );
        wp_enqueue_script('tpw-admin-js');
        wp_enqueue_style('tpw-admin', plugins_url('tpw_admin.css', __FILE__) );

    }       

    /**
     * Back-end widget form.
     *
     * @see WP_Widget::form()
     *
     * @param array $instance Previously saved values from database.
     */
    public function form( $instance ) {


        $title_text = ( isset( $instance['title_text'] ) ) ? $instance['title_text'] : '';
        $title_image = ( isset( $instance['title_image'] ) ) ? $instance['title_image'] : '';


    ?>  

        <div class="titlepage_widget">

            <h3>Title</h3>
            <p>
                <div class="widget_input">
                    <label for="<?php echo $this->get_field_id( 'title_text' ); ?>"><?php _e( 'Text :' ); ?></label>    
                    <input class="title_text" id="<?php echo $this->get_field_id( 'title_text' ); ?>" name="<?php echo $this->get_field_name( 'title_text' ); ?>" value="<?php echo $title_text ?>" type="text"><br/>
                </div>
                <div class="widget_input">
                    <label for="<?php echo $this->get_field_id( 'title_image' ); ?>"><?php _e( 'Image :' ); ?></label>  
                    <input class="title_image" id="<?php echo $this->get_field_id( 'title_image' ); ?>" name="<?php echo $this->get_field_name( 'title_image' ); ?>" value="<?php echo $title_image ?>" type="text"><button id="title_image_button" class="button" onclick="image_button_click('Choose Title Image','Select Image','image','title_image_preview','<?php echo $this->get_field_id( 'title_image' );  ?>');">Select Image</button>            
                </div>
                <div id="title_image_preview" class="preview_placholder">
                <?php 
                    if ($title_image!='') echo '<img src="' . $title_image . '">';
                ?>
                </div>
            </p>    

        </div>

        <?php 
    }

    /**
     * Sanitize widget form values as they are saved.
     *
     * @see WP_Widget::update()
     *
     * @param array $new_instance Values just sent to be saved.
     * @param array $old_instance Previously saved values from database.
     *
     * @return array Updated safe values to be saved.
     */
    public function update( $new_instance, $old_instance ) {

        $instance = array();
        $instance['title_text'] = ( ! empty( $new_instance['title_text'] ) ) ? strip_tags( $new_instance['title_text'] ) : '';
        $instance['title_image'] = ( ! empty( $new_instance['title_image'] ) ) ? strip_tags( $new_instance['title_image'] ) : '';
        return $instance;
    }

} // class My_Widget

And the following is the jQuery which accompanies the previous code:

Read More
var custom_uploader;

function image_button_click(dialog_title, button_text, library_type, preview_id, control_id) {

    event.preventDefault();

    //If the uploader object has already been created, reopen the dialog
    //if (custom_uploader) {
     //   custom_uploader.open();
    //    return;
    //}

    //Extend the wp.media object
    custom_uploader = wp.media.frames.file_frame = wp.media({
        title: dialog_title,
        button: {
            text: button_text
        },
        library : { type : library_type },            
        multiple: false
    });

    //When a file is selected, grab the URL and set it as the text field's value
    custom_uploader.on('select', function() {

        attachment = custom_uploader.state().get('selection').first().toJSON();
        jQuery('#' + control_id).val(attachment.url);

        var html = '';

        if (library_type=='image') {
            html = '<img src="' + attachment.url + '">';
        }

        if (library_type=='video') {
            html = '<video autoplay loop><source src="' + attachment.url + '" type="video/' + get_extension( attachment.url ) + '" /></video>';
        }

        jQuery('#' + preview_id).empty();
        jQuery('#' + preview_id).append(html);
    });

    //Open the uploader dialog
    custom_uploader.open();

}

function get_extension( url ){

    return url.substr((url.lastIndexOf('.') + 1));

}

You can can find the whole thing right here.

The problem is that the selected image is only showed after I click on the Save button, and it would be great if it would be shown in the preview field right after the image is selected, without having to click on the Save button, just like the Featured Image widget does in the Post Edit screen.

Any ideas? 🙂

Related posts

1 comment

  1. First thing I suggest is to rewrite the whole thing a bit better. You’re writing widgets, and say you want multiple of them, you’ll have multiple same id’s which isn’t a good thing.

    So create clear and understandable classes, that you can point to on your click events.

    I’ve made something that works, but it can be enhanced greatly (which I trust is something you’ll do :D).

    I’ve added plugin header so that I can install it, and I’ve had to register widget so that I can see it in my backend as well. I’ve added remove image button that will remove image and clear the input field on click.

    php part:

    <?php
    /*
    Plugin Name: Title_Page_Widget
    Plugin URI:
    Description: Title_Page_Widget
    Version: 1.0.0
    Author:
    Author
    License: GPL
    */
    
    
    /**
     * Adds Title_Page_Widget widget.
     */
    class Title_Page_Widget extends WP_Widget {
    
        /**
         * Register widget with WordPress.
         */
        function __construct() {
            parent::__construct(
                'Title_Page_Widget', // Base ID
                __('Title Page Widget (PB)', 'text_domain'), // Name
                array('description' => __( 'Creates a full-screen title page - designed for use with Site Origin's Page Builder plugin', 'text_domain' ),) // Args
            );
    
            add_action( 'sidebar_admin_setup', array( $this, 'admin_setup' ) );
    
        }
    
        function admin_setup(){
    
            wp_enqueue_media();
            wp_register_script('tpw-admin-js', plugins_url('tpw_admin.js', __FILE__), array( 'jquery', 'media-upload', 'media-views' ) );
            wp_enqueue_script('tpw-admin-js');
            wp_enqueue_style('tpw-admin', plugins_url('tpw_admin.css', __FILE__) );
    
        }
    
        /**
         * Back-end widget form.
         *
         * @see WP_Widget::form()
         *
         * @param array $instance Previously saved values from database.
         */
        public function form( $instance ) {
    
    
            $title_text = ( isset( $instance['title_text'] ) ) ? $instance['title_text'] : '';
            $title_image = ( isset( $instance['title_image'] ) ) ? $instance['title_image'] : '';
    
    
        ?>
    
            <div class="titlepage_widget">
    
                <h3>Title</h3>
                <p>
                    <div class="widget_input">
                        <label for="<?php echo $this->get_field_id( 'title_text' ); ?>"><?php _e( 'Text :' ); ?></label>
                        <input class="title_text" id="<?php echo $this->get_field_id( 'title_text' ); ?>" name="<?php echo $this->get_field_name( 'title_text' ); ?>" value="<?php echo $title_text ?>" type="text"><br/>
                    </div>
                    <div class="widget_input">
                        <label for="<?php echo $this->get_field_id( 'title_image' ); ?>"><?php _e( 'Image :' ); ?></label>
                        <input class="title_image" id="<?php echo $this->get_field_id( 'title_image' ); ?>" name="<?php echo $this->get_field_name( 'title_image' ); ?>" value="<?php echo $title_image ?>" type="text"><button id="title_image_button" class="button" onclick="image_button_click('Choose Title Image','Select Image','image','title_image_preview','<?php echo $this->get_field_id( 'title_image' );  ?>');">Select Image</button>
                        <div class="button remove_image_button" >Remove Image</div>
                    </div>
                    <div id="title_image_preview" class="preview_placholder">
                    <?php
                        if ($title_image!='') echo '<img style="width:100%" src="' . $title_image . '">';
                    ?>
                    </div>
                </p>
    
            </div>
    
            <?php
        }
    
        /**
         * Sanitize widget form values as they are saved.
         *
         * @see WP_Widget::update()
         *
         * @param array $new_instance Values just sent to be saved.
         * @param array $old_instance Previously saved values from database.
         *
         * @return array Updated safe values to be saved.
         */
        public function update( $new_instance, $old_instance ) {
    
            $instance = array();
            $instance['title_text'] = ( ! empty( $new_instance['title_text'] ) ) ? strip_tags( $new_instance['title_text'] ) : '';
            $instance['title_image'] = ( ! empty( $new_instance['title_image'] ) ) ? strip_tags( $new_instance['title_image'] ) : '';
            return $instance;
        }
    
    } // class My_Widget
    
    function twp_widget(){
        register_widget('Title_Page_Widget');
    }
    
    add_action('widgets_init', 'twp_widget');
    

    JS part:

    var custom_uploader;
    
    function image_button_click(dialog_title, button_text, library_type, preview_id, control_id) {
    
        event.preventDefault();
    
        //If the uploader object has already been created, reopen the dialog
        //if (custom_uploader) {
         //   custom_uploader.open();
        //    return;
        //}
    
        //Extend the wp.media object
        custom_uploader = wp.media.frames.file_frame = wp.media({
            title: dialog_title,
            button: {
                text: button_text
            },
            library : { type : library_type },
            multiple: false
        });
    
        //When a file is selected, grab the URL and set it as the text field's value
        custom_uploader.on('select', function() {
    
            attachment = custom_uploader.state().get('selection').first().toJSON();
            jQuery('#' + control_id).val(attachment.url);
    
            var html = '';
    
            if (library_type=='image') {
                html = '<img src="' + attachment.url + '">';
            }
    
            if (library_type=='video') {
                html = '<video autoplay loop><source src="' + attachment.url + '" type="video/' + get_extension( attachment.url ) + '" /></video>';
            }
    
            var uploaded_image = jQuery('.uploaded_image');
    
            if (uploaded_image.length) {
                uploaded_image.remove();
            }
    
            jQuery('.title_image').before('<img class="uploaded_image" src="'+attachment.url+'" style="width:100%";>');
    
            jQuery('#' + preview_id).empty();
            jQuery('#' + preview_id).append(html);
        });
    
        //Open the uploader dialog
        custom_uploader.open();
    
    }
    
    jQuery(document).on('click','.remove_image_button', function(e){
        e.preventDefault();
        jQuery('.uploaded_image').remove();
        jQuery('#title_image_preview img').remove();
        jQuery('.title_image').val('');
    });
    
    function get_extension( url ){
    
        return url.substr((url.lastIndexOf('.') + 1));
    
    }
    

    Here I’ve added:

        var uploaded_image = jQuery('.uploaded_image');
    
        if (uploaded_image.length) {
            uploaded_image.remove();
        }
    
        jQuery('.title_image').before('<img class="uploaded_image" src="'+attachment.url+'" style="width:100%";>');
    

    and for remove button:

    jQuery(document).on('click','.remove_image_button', function(e){
        e.preventDefault();
        jQuery('.uploaded_image').remove();
        jQuery('#title_image_preview img').remove();
        jQuery('.title_image').val('');
    });
    

    I’ve tested it and it works, but as I’ve said, it needs work. You’ll need to see from which widget the click originated, and where to remove the image, and so on. I suspect that if you have multiple of them, once you’ve click on the remove, you’d remove all the images. As I’ve said, needs more work.

    Hope this gets you in the right direction.

Comments are closed.