Support » Developing with WordPress » Conditional meta box logic

  • Hello,

    I’m trying to create conditional logic for custom meta boxes on admin screen only and without plugin. This is a situation: I have checkboxes, which upon selection will show corresponding DIV which contain input field. It is basic show/hide, nothing complicated.

    Meta box fields are working properly saving and displaying values. Conditional logic is managed using javascript, it’s working.

    Problem is: When I show DIV via checkbox and click on update post, DIV doesn’t stay visible. This means I didn’t save logic properly.

    My checkboxes and inputs are generated via foreach loop, and here is my issue. I’m using PHP switch to determine each type of field, and can’t find solution to save which DIV is visible and to stay visible.

    I appreciate any help or any reference link suggestion. Thanks

    This is my meta box class:

    class Artikal_Meta_Box {
    	private $screens = array(
    		'artikli',
    	);
    	private $fields = array(
    		array(
    			'id' => 'check-artikal-braon',
    			'label' => 'Braon',
    			'type' => 'checkbox',
    			'value' => 'color-field-artikal-braon',
    		),
    		array(
    			'id' => 'check-artikal-crna',
    			'label' => 'Crna',
    			'type' => 'checkbox',
    			'value' => 'color-field-artikal-crna',
    		),		
    		array(
    			'id' => 'check-artikal-zuta',
    			'label' => 'Žuta',
    			'type' => 'checkbox',
    			'value' => 'color-field-artikal-zuta',
    		),
    		array(
    			'id' => 'check-artikal-tirkiz',
    			'label' => 'Tirkiz',
    			'type' => 'checkbox',
    			'value' => 'color-field-artikal-tirkiz',
    		),
    		
    		array(
    			'id' => 'artikal-braon',
    			'label' => 'Braon',
    			'name' => 'braon',
    			'type' => 'text',
    		),
    		array(
    			'id' => 'artikal-crna',
    			'label' => 'Crna',
    			'name' => 'crna',
    			'type' => 'text',			
    		),
    		array(
    			'id' => 'artikal-zuta',
    			'label' => 'Žuta',
    			'name' => 'zuta',
    			'type' => 'text',
    		),
    		array(
    			'id' => 'artikal-tirkiz',
    			'label' => 'Tirkiz',
    			'name' => 'tirkiz',
    			'type' => 'text',
    		),
    		
    		array(
    			'id' => 'artikal-suma',
    			'label' => 'Stanje',
    			'name' => 'artikal-suma',
    			'type' => 'textarea',			
    		),
    		
    	);
    
    	/**
    	 * Class construct method. Adds actions to their respective WordPress hooks.
    	 */
    	public function __construct() {
    		add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
    		add_action( 'save_post', array( $this, 'save_post' ) );
    	}
    
    	/**
    	 * Hooks into WordPress' add_meta_boxes function.
    	 * Goes through screens (post types) and adds the meta box.
    	 */
    	public function add_meta_boxes() {
    		foreach ( $this->screens as $screen ) {
    			add_meta_box(
    				'artikal-info',
    				__( 'Artikal', 'artikal-info' ),
    				array( $this, 'add_meta_box_callback' ),
    				$screen,
    				'normal',
    				'high'
    			);
    		}
    	}
    
    	/**
    	 * Generates the HTML for the meta box
    	 * 
    	 * @param object $post WordPress post object
    	 */
    	public function add_meta_box_callback( $post ) {
    		wp_nonce_field( 'artikal_info_data', 'artikal_info_nonce' );
    		$this->generate_fields( $post );
    	}
    
    	/**
    	 * Generates the field's HTML for the meta box.
    	 */
    	public function generate_fields( $post ) {
    		$output = '<div class="artikal-all-fields" style="background-color:lightblue;">';
    
    		foreach ( $this->fields as $field ) {	
    			
    			$cc = $field['value'];
    			$label = '<label for="' . $field['id'] . '">' . $field['label'] . '</label>';
    			$db_value = get_post_meta( $post->ID, 'artikal_info_' . $field['id'], true );
    			
    
    			switch ( $field['type'] ) {
    				case 'checkbox':
    				$input = sprintf(
    					'<input %s id="%s" class="artikli-checkbox" name="%s" type="%s" value="'. $cc .'">',
    					$db_value === $cc ? 'checked="checked"' : '',
    					$field['id'],
    					$field['id'],
    					$field['type']
    				);
    				break;
    				case 'textarea':
    				$input = sprintf(
    					'<textarea id="%s" name="%s" rows="1">%s</textarea>',
    					$field['id'],
    					$field['id'],
    					$db_value
    				);
    				break;
    				default:
    					$input = sprintf(
    						'<input class="components-text-control__input" id="%s" name="%s" type="%s" value="%s">',
    						$field['id'],
    						$field['id'],
    						$field['type'],
    						$db_value
    					);
    			}
    			
    			switch ( $field['type'] ) {
    				case 'checkbox':						
    					$output .= '<div class="'. $cc .'">';					
    					$output .= $input;
    					$output .= $label;
    					$output .= '</div>';					
    				break;
    				default :
    					$output .= '<div id="color-field-'. $field['id'] .'" class="artikal-field-wrap '. $none .'" >';
    					$output .= '<span class="minus button"></span>';
    					$output .= $label;					
    					$output .= $input;
    					$output .= '<span class="add button"></span>';
    					$output .= '</div>';
    			}	
    			
    			
    		}
    		
    		
    		
    		$output .= '</div>';
    		echo $output;
    	}
    
    	/**
    	 * Hooks into WordPress' save_post function
    	 */
    	public function save_post( $post_id ) {
    		if ( ! isset( $_POST['artikal_info_nonce'] ) )
    			return $post_id;
    
    		$nonce = $_POST['artikal_info_nonce'];
    		if ( !wp_verify_nonce( $nonce, 'artikal_info_data' ) )
    			return $post_id;
    
    		if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
    			return $post_id;
    
    		foreach ( $this->fields as $field ) {
    			if ( isset( $_POST[ $field['id'] ] ) ) {
    				switch ( $field['type'] ) {
    					case 'email':
    						$_POST[ $field['id'] ] = sanitize_email( $_POST[ $field['id'] ] );
    						break;
    					case 'text':
    						$_POST[ $field['id'] ] = sanitize_text_field( $_POST[ $field['id'] ] );
    						break;
    				}
    				update_post_meta( $post_id, 'artikal_info_' . $field['id'], $_POST[ $field['id'] ] );
    			} else if ( $field['type'] === 'checkbox' ) {
    				update_post_meta( $post_id, 'artikal_info_' . $field['id'], '0' );
    			}
    		}
    	}
    }
    new Artikal_Meta_Box;

    This is my javascript snippet

    $('.artikli-checkbox').change(function() {
    		var checkbox = $(this);
    		if( checkbox.is(':checked') ) {
    			$( '#' + checkbox.val() ).show();
    		} else {
    			$( '#' + checkbox.val() ).hide();
    		}
    	});
Viewing 6 replies - 1 through 6 (of 6 total)
  • Moderator bcworkz

    (@bcworkz)

    You are trying to show/hide based on an element ID attribute, but your checkbox related divs have no ID attribute. If you change the div’s class to id your JS ought to work.

    Thread Starter kostadino

    (@kostadino)

    Hi @bcworkz,

    Thanks for a reply. My JS is working just fine, show/hide is working. The problem is in Save.

    When checkbox is checked, DIV is show, but it doesn’t stay visible when clicking on “Update post” button and saving the post.

    Moderator bcworkz

    (@bcworkz)

    Oooo! I see now, in the save_post callback, when a check box is marked, the first if isset condition matches, but there is no switch case for check boxes there. Setting the related meta value to 0 when there is no related data in $_POST is fine (the else if condition), but you’re not handling the condition when the checkbox is marked.

    Thread Starter kostadino

    (@kostadino)

    Hi @bcworkz,

    you’re not handling the condition when the checkbox is marked.

    Straight to the center.

    I’m not handling condition because I don’t know how in case of dynamically created fields.

    When checkbox is marked, I need to find a way to apply its state to DIV and save it, right now it’s a bit riddle for me how to accomplish that.

    Moderator bcworkz

    (@bcworkz)

    I’m quite a bit off with the checkbox handling, sorry. However, you should add a case to sanitize the value before saving, even though it is not a variable field. A bad actor could still inject malicious code through that field if it’s not sanitized.

    Isn’t the concept that the div element is always there, it’s just hidden or not hidden based on checkbox status?

    There’s no conditional code that I see to set the CSS style based on checkbox status. I suspect the default CSS is to hide. There’s nothing on initial document load to unhide it when the box is checked. The jQuery you have only runs when the box changes status, so it’s of no help on document load.

    You need script to go through every checkbox field on document load and if it’s marked, call .show() for the field’s related div.

    There are many different approaches you could take.

    I suggest lets name ID and value of checkboxes the same, then create an empty state array and pass the value of checked element to this array and save it as post meta hooked to save_post action, then get back the values with get_post_meta hooked to the_post action and finally with ajax pass the state array/object or saved post meta to jQuery and show or hide the <div>.

    This way you have checked elements in the db and available in JS for each post.

    I hope this helps.

Viewing 6 replies - 1 through 6 (of 6 total)
  • You must be logged in to reply to this topic.