Is dynamic forms/entries possible in Widget?

Thank you Dave Romsey and Mark Kaplun for pointing out about not using HTML ID in WordPress Widget. I finally find a solution for my need.

Solution:

We need to add a preset variables into all of the 3 functions (widget, form, update). In my codes, they are called “spotlight_image_link1, spotlight_image_link2, etc”. Then in the “form function” give their id using php code. Please read my code below for more information.

What can my solution do?

Basically, it is not fully dynamic because we need to predefine all the spotlights beforehand. In my case, I only predefined two spotlights to make the code simple to read.

Here what it looks like:
enter image description here

Logic behind the scene:

  • whenever the “Click me!” button is clicked, a spotlight will be added.
  • There is a tracker array called “tracker” that keep track of which predefined spotlights are already used. When all spotlights are used, disable the “Click me!” button.
  • When the “delete spotlight” is clicked, remove that specific spotlight, and re-enable the “Click me!” button. Note that the name of each delete button is append with number. This lets us know which spotlight to be deleted.
  • The spotlight is saved only if and only if the input is not empty. That condition is in the “while” loop of the “form” function.

Limitation:

As of now, whenever, we clicked “Save”, the spotlight order is rearranged from 1 to n. It’s not super user-friendly, but I plan to use Ajax to sync the front-end to the back-end. I am not sure how just yet.

Source code:

  • As of WordPress 4.7.2, it works without any error.
  • Put all the source code into “repeat.php” file in your plugin directory. If you don’t know how, please google it.

repeat.php

    <?php
/**
 * Plugin Name: Repeat Field
 *
 */

 class repeat_field_widget extends WP_Widget {

    /**
     * Sets up the widgets name etc
     */
    public function __construct() {
        $widget_ops = array(
            'classname' => 'repeat_field_widget',
            'description' => 'Repeat Field for Name input'
        );
        parent::__construct( 'repeat_field_widget', 'Repeat Field', $widget_ops );
    $tracker1;
    }

    /**
     * Outputs the content of the widget
     *
     * @param array $args
     * @param array $instance
     */
    public function widget( $args, $instance ) {
        // outputs the content of the widget
    $spotlight_add_row = ! empty( $instance['spotlight_add_row'] ) ? $instance['spotlight_add_row'] : '';
    $spotlight_row_appender = ! empty( $instance['spotlight_row_appender'] ) ? $instance['spotlight_row_appender'] : '';

    /**============== Spotlight 1 =========================*/
    $spotlight_image_link1 = ! empty( $instance['spotlight_image_link1'] ) ? $instance['spotlight_image_link1'] : '';
    $spotlight_add_image1 = ! empty( $instance['spotlight_add_image1'] ) ? $instance['spotlight_add_image1'] : '';
    $spotlight_image_preview1 = ! empty( $instance['spotlight_image_preview1'] ) ? $instance['spotlight_image_preview1'] : '';
    $spotlight_name1 = ! empty( $instance['spotlight_name1'] ) ? $instance['spotlight_name1'] : '';
    $spotlight_description1 = ! empty( $instance['spotlight_description1'] ) ? $instance['spotlight_description1'] : '';
    $spotlight_link1 = ! empty( $instance['spotlight_link1'] ) ? $instance['spotlight_link1'] : '';

    /**============== Spotlight 2 =========================*/
    $spotlight_image_link2 = ! empty( $instance['spotlight_image_link2'] ) ? $instance['spotlight_image_link2'] : '';
    $spotlight_add_image2 = ! empty( $instance['spotlight_add_image2'] ) ? $instance['spotlight_add_image2'] : '';
    $spotlight_image_preview2 = ! empty( $instance['spotlight_image_preview2'] ) ? $instance['spotlight_image_preview2'] : '';
    $spotlight_name2 = ! empty( $instance['spotlight_name2'] ) ? $instance['spotlight_name2'] : '';
    $spotlight_description2 = ! empty( $instance['spotlight_description2'] ) ? $instance['spotlight_description2'] : '';
    $spotlight_link2 = ! empty( $instance['spotlight_link2'] ) ? $instance['spotlight_link2'] : '';
    }

    /**
     * Outputs the options form on admin
     *
     * @param array $instance The widget options
     */
    public function form( $instance ) {
        // outputs the options form on admin
    $instance = wp_parse_args( (array) $instance, array( 'spotlight_add_row' => '', 'spotlight_row_appender' => '', 'spotlight_image_link1' => '', 'spotlight_add_image1' => '', 'spotlight_image_preview1' => '', 'spotlight_name1' => '', 'spotlight_description1' => '', 'spotlight_link1' => '',
    'spotlight_image_link2' => '', 'spotlight_add_image2' => '', 'spotlight_image_preview2' => '', 'spotlight_name2' => '', 'spotlight_description2' => '', 'spotlight_link2' => '' ));

    //Create Add and delete button
    $spotlight_add_row = $instance['spotlight_add_row'];
    $spotlight_row_appender = $instance['spotlight_row_appender'];

    /**================== Spotlight 1 ==============*/
    $spotlight_image_link1 = $instance['spotlight_image_link1'];
    $spotlight_add_image1 = $instance['spotlight_add_image1'];
    $spotlight_image_preview1 = $instance['spotlight_image_preview1'];
    $spotlight_name1 = $instance['spotlight_name1'];
    $spotlight_description1 = $instance['spotlight_description1'];
    $spotlight_link1 = $instance['spotlight_link1'];

    /**================== Spotlight 2 ==============*/
    $spotlight_image_link2 = $instance['spotlight_image_link2'];
    $spotlight_add_image2 = $instance['spotlight_add_image2'];
    $spotlight_image_preview2 = $instance['spotlight_image_preview2'];
    $spotlight_name2 = $instance['spotlight_name2'];
    $spotlight_description2 = $instance['spotlight_description2'];
    $spotlight_link2 = $instance['spotlight_link2'];

    $starter = 1; //Store which number to continue adding spotlight.
    $num = 1;
    $max_spotlight = 2; //number of spotlight allowed.
    static $tracker = array(0,0); //setup a tracker for each spotlight, zero mean none active.

    while($num <= $max_spotlight){
      $tempImage="spotlight_image_link" . $num;

       if ($$tempImage != ''){
        $starter++;
        $tracker[$num - 1] = 1;
?>
        <!-- Image input -->
        <div>
        <p class="spotlight-para">Spotlight <?php echo $num; ?></p>
        <p> <?php $tempImage="spotlight_image_link" . $num;  $tempDeleteName="deletespotlight_". $num;?> <!-- store the combined name. -->
            <label for="<?php echo esc_attr( $this->get_field_id( $tempImage ) ); ?>"><?php esc_attr_e( 'Image\'s link:', 'text_domain' ); ?></label>
            <input
                   class="widefat"
                   id="<?php echo $this->get_field_id($tempImage); ?>"
                   name="<?php echo $this->get_field_name($tempImage); ?>"
                   type="text"
                   value="<?php echo esc_attr($$tempImage); ?>"
                   />
           <input style="float:right;" id="delete-spotlight" name="<?php echo $tempDeleteName; ?>" type="button" value="Delete Spotlight" class="button"/>
           <br />
        </p>
        </div>
<?php
      }
     $num++;
    }
    $id_prefix = $this->get_field_id(''); //get the widget prefix id.
?>
    <span id="<?php echo $this->get_field_id('spotlight_row_appender'); ?>"> </span>
    <div>
      <br />
      <input
            class="button"
            type="button"
            id="<?php echo $this->get_field_id('spotlight_add_row'); ?>"
            value="Click Me!"
            onclick="repeater.uploader('<?php echo $this->id;?>', '<?php echo $id_prefix;?>'); return false;"
            />
    </div>

    <script>
    jQuery(document).ready(function($){
      var tracker = <?php echo json_encode($tracker); ?>;
      var c1 = <?php echo json_encode($starter - 1); ?>;//index of the array.

      //disbale add button when reaches max spotlight.
      if(tracker.every(x => x > 0)){
        $('#' + '<?php echo $id_prefix; ?>' + 'spotlight_add_row').attr("disabled",true);
      }

      repeater = {
          //TRY to mass Number into this function........
          uploader :function(widget_id, widget_id_string){
            //Find the non active element
            var i;
            for (i = 0; i < <?php echo $max_spotlight; ?>; i++){
              if ( tracker[i] == 0){
                c1 = i;
                break;
              }
            }
            c1++;
            //alert(c1);



            $("#" + widget_id_string + "spotlight_row_appender").append('<div> <p class="spotlight-para">Spotlight '+c1+'</p><p> <label for="<?php echo esc_attr( $this->get_field_id( "spotlight_image_link'+c1+'")); ?>"><?php esc_attr_e( 'Image\'s link:', 'text_domain' ); ?></label>  <input  class="widefat" id="<?php echo $this->get_field_id("spotlight_image_link'+c1+'"); ?>"  name="<?php echo $this->get_field_name("spotlight_image_link'+c1+'"); ?>" type="text" value="" />  <input style="float:right;"id="delete-spotlight" name="deletespotlight_'+c1+'" type="button" value="Delete Spotlight" class="button"/><br /> </p></div>');
            //check element as active
            tracker[c1-1] = 1;

            //if all element is > 0, disable the deleteButton.
            if(tracker.every(x => x > 0)){
              $('#' + '<?php echo $id_prefix; ?>' + 'spotlight_add_row').attr("disabled",true);
            }
            //alert(c1);
            return false;
          }
      };

      $(document).on('click', '#delete-spotlight', function() {

        $(this).parent().parent().remove(); //remove the field.
        $('#' + '<?php echo $id_prefix; ?>' + 'spotlight_add_row').removeAttr("disabled"); //reset add button.

        //Get the name, and parse to get the ID.
        var deleteButtonName = this.name;
        var stringDeleteButton = deleteButtonName.split("_").pop();
        var deleteButtonID = parseInt(stringDeleteButton);

        tracker[deleteButtonID-1] = 0; //reset element
        //alert(tracker);
      });

    });
    </script>
  <?php
    }

    /**
     * Processing widget options on save
     *
     * @param array $new_instance The new options
     * @param array $old_instance The previous options
     */
    public function update( $new_instance, $old_instance ) {
        // processes widget options to be saved
    $instance = $old_instance;
    $instance['spotlight_add_row'] = sanitize_text_field($new_instance['spotlight_add_row']);
    $instance['spotlight_row_appender'] = sanitize_text_field($new_instance['spotlight_row_appender']);

    $increment = 1;
    while ( $increment <= 2 ){
      //increment variables
      $increment_image_link = 'spotlight_image_link' . $increment;
      $increment_add_image="spotlight_add_image" . $increment;
      $increment_image_preview = 'spotlight_image_preview' . $increment;
      $increment_description = 'spotlight_description' . $increment;
      $increment_name="spotlight_name" . $increment;
      $increment_link = 'spotlight_link' . $increment;

      $instance[$increment_image_link] = sanitize_text_field( $new_instance[$increment_image_link] );
      $instance[$increment_add_image] = sanitize_text_field( $new_instance[$increment_add_image] );
      $instance[$increment_image_preview] = sanitize_text_field( $new_instance[$increment_image_preview]);
      $instance[$increment_name] = sanitize_text_field( $new_instance[$increment_name] );
      $instance[$increment_description] = sanitize_text_field( $new_instance[$increment_description] );
      $instance[$increment_link] = sanitize_text_field( $new_instance[$increment_link] );

      $increment++;
    }
    $starter = 1;
    $num = 1;

    return $instance;
    }
}


function register_repeat_field_widget() {
    register_widget( 'repeat_field_widget' );
}
add_action( 'widgets_init', 'register_repeat_field_widget' );

Quick Note:

I know that this is not the most clean, secure, and best code, but I am still learning. I hope that helps anyone who faces the same issues as me.

Leave a Comment