• Resolved VOST

    (@ovws)


    Hello,

    I am trying to put in place repeatable fields with drop-down lists containing terms from custom taxonomies. It is displayed beneath the editor field in one of my custom post type.

    Here is what I cannot achieve:

    • simply return the html for the drop-down lists (works fine when I try with inputs for example)
    • attribute values to dynamic options

    In the end I got the ‘Add a screening’ (equal to “Add new field”) button but when I click on it nothing happens; none of the fields appear.

    My code is in the function.php file. This is the first part of the code with the drop-down lists which seems to cause me trouble – it was obtained more from tweaks of numerous copy and paste than from my little knowledge of php:

    function print_screening_field($cnt, $p = null) {
    if ($p === null) {
        $a = $b = '';
    } else {
    	    $a = $p['screeningName'];
    	    $b = $p['screeningCountry'];
    
    	    $festivalterms = get_terms('festival', 'hide_empty=0');
    	    $countryterms = get_terms('country', 'hide_empty=0');
    
    	        $html = '';
    	        $html .= '<li>';
    
    		$html .= '<label>Screening: </label>';
    		$html .= '<select>';
    		foreach ($festivalterms as $term) {
    			$html .= '<option>';
    			$html .= $term->name;
    			$html .= '</option>';
    		}
    		$html .= '</select>';
    
    		$html .= '<label>Country: </label>';
    		$html .= '<select>';
    		foreach ($countryterms as $term) {
    			$html .= '<option>';
    			$html .= $term->name;
    			$html .= '</option>';
    		}
    		$html .= '</select>';
        	$html .= '<button class="remove">Remove</button>';
        	$html .= '</li>';
    	}
    	return $html;
    }

    This is the second part of the code; the meta box part with jQuery:

    function screening_details_meta_box(){
    	global $post;
    	$custom = get_post_custom($post->ID);
      	$data = get_post_meta($post->ID,"screeningDetails",true);
    ?>
    
      	<h4>OTHER SCREENINGS</h4>
      	<?php
      	echo '<ul id="data_items">';
      	$d = 0;
        	if (count($data) > 0){
        	    foreach((array)$data as $p ){
        	        if ( isset($p['screeningName']) || isset($p['screeningCountry']) ){
        	            echo print_screening_field($d,$p);
        	            $d = $d +1;
        	        }
        	    }
    	    }
    	echo '</ul>';
       	?>
        <span id="here"></span>
        <button class="add"><?php echo __('Add a screening'); ?></button>
        <script>
        	var $ =jQuery.noConflict();
        	$(document).ready(function() {
        	var count = <?php echo $d; ?>;
        	$(".add").click(function() {
            	count = count + 1;
            		$('#data_items').append('<? echo implode('',explode("\n",print_screening_field('count'))); ?>'.replace(/count/g, count));
            			return false;
            		});
            		$(".remove").live('click', function() {
            			$(this).parent().remove();
            		});
            	});
        </script>
        <style>#data_items {list-style: none;}</style>
        <?php
    }

    As in case I don’t find a solution, I can use the following piece of code with the screening_details_meta_box function (the one that contains the jQuery bit). But it means I am loosing the drop-down lists:

    function print_screening_field($cnt, $p = null) {
    if ($p === null){
        $a = $b = '';
    } else {
        $a = $p['screeningName'];
        $b = $p['screeningCountry'];
    
        $festivalterms = get_terms('festival', 'hide_empty=0');
        $countryterms = get_terms('country', 'hide_empty=0');
    }
    return  <<<HTML
    <li>
        <label>Screening:</label>
        <input type="text" name="screeningDetails[$cnt][screeningName]" value="$a"/>
    
        <label>Country:</label>
        <input type="text" name="screeningDetails[$cnt][screeningCountry]" value="$b"/>
    
        <button class="remove">Remove</button>
    </li>
    HTML
    ;
    }

    I would like to avoid any plugins. If you could simply mention the technical and exact terms describing what I am trying to do, it would be helping me in my research already.

    Thank you

Viewing 4 replies - 1 through 4 (of 4 total)
  • Moderator bcworkz

    (@bcworkz)

    Sorry, I’m not quite sure what you’re actually trying to do. I get the sense that you might not have the full process in mind in your attempts. Perhaps if you had a better model in mind you will have better success. The WP documentation team is currently working on a plugin developer’s handbook. It’s still a work in progress, but the chapter on Meta Boxes is complete. As it happens the example provided is a drop down list. If you follow the example, it should be easy to add as many more fields as needed.

    Sorry I can’t provide more specific help. I hope the reference gets you pointed in the right direction. In way of disclosure, I’m involved with the documentation team. We would appreciate any feedback you might have about this article or anything else you find in the handbook. Either reply here or register on “Make” and leave a comment below the page.

    Thread Starter VOST

    (@ovws)

    Hello bcworkz,

    thank you for your link. What I am trying to do is slightly more complex as I wish to have several drop-down lists (so several values) embedded in one repeatable field. Visually it would be something like such:

    [repeatable]
    – Drop-down: Screenings’ names
    – Drop-down: Screenings’ countries
    -remove button
    [/repeatable]
    [Add a screening button]

    So far I succeeded to return the drop-down lists (which was my first problem) but I still cannot find a way to attach values to the selected options.

    Here is my code so far (what changes, compared to the first code in my first post, is that I closed my condition “$p === null” before I set my $festivalterms and $countryterms variables):

    function print_screening_field($cnt, $p = null) {
    if ($p === null) {
    
        $a = $b = '';
    
    } else {
    
        $a = $p['screeningName'];
        $b = $p['screeningCountry'];
    
    }
    
        $festivalterms = get_terms('festival', 'hide_empty=0');
        $festivalnames = wp_get_object_terms($post->ID, 'festival');
        $countryterms = get_terms('country', 'hide_empty=0');
        $countrynames = wp_get_object_terms($post->ID, 'country');
    
    	$html = '';
            $html .= '<li>';
    
    	$html .= '<label>Festival: </label>';
    	$html .= '<select name="screeningName" id="screeningName">';
    	$html .= '<option value="">Select screening</option>';
    	foreach ($festivalterms as $term) {
    		$html .= '<option value="' . $a . '" ' . selected($a, '' . $term->name . '') . '>' . $term->name . '</option>';
    	}
    	$html .= '</select>';
    
    	$html .= '<label>Country: </label>';
    	$html .= '<select name="screeningCountry" id="screeningCountry">';
    	$html .= '<option value="">Select country</option>';
    	foreach ($countryterms as $term) {
    		$html .= '<option value="' . $b . '" ' . selected($b, '' . $term->name . '') . '>' . $term->name . '</option>';
    	}
    	$html .= '</select>';
    
    	$html .= '<button class="remove">Remove</button>';
    	$html .= '</li>';
    
    return $html;
    }

    Moderator bcworkz

    (@bcworkz)

    Sorry, I’m still not sure where you’re going with this, but specifically, to add values to each option from the values in the terms array, you cannot use $a and $b as they appear to be undefined and do not reflect the terms array. You need to use the temporary value specified in the foreach statement, in your case $term, something like this inside the foreach loop:
    $html .= '<option value="' . $term->name . '" >' . $term->name . '</option>';

    This of course does not preselect a previously selected value. How to do this depends on how the previously selected value is stored. If the previously selected value were contained in $festival you could do something like this:
    $html .= '<option value="' . $term->name . '" '.($festival==$term->name?'selected':'').'>' . $term->name . '</option>';

    Thread Starter VOST

    (@ovws)

    Thanks bcworkz in trying to help me out; it took me some times but I eventually found something.
    What follows is not a long-term solution. If you wish to keep in your database the fact that you assign terms of custom taxonomies to a certain post using repeatable fields, then, I suppose, it evolves doing a bit a SQL.
    Still, I am posting my solution as it may be of use for someone.
    The trick, compared to my previous code, was to add a count variable (here $cnt) to the name attribute of my select element:
    Before:
    $html .= '<select name="screeningName" id="screeningName">';
    After:
    $html .= '<select name="screeningDetails[' . $cnt . '][screeningName]">';
    Semantically, the repeatable fields are tr elements of a table, with the remove button within each row. In order to make it work (the remove button inside each row) the jquery code needs to be changed:

    $(".remove").live('click', function() {
            //$(this).parent().remove(); // When the button is outside the repeatable element
            $(this).parents('[class*="repeatableField"]').remove(); // When the button is inside the repeatable element
    });

    So, here we go, follows the corrected code (I also pastebin-ed it for anyone to improve it, https://pastebin.com/CxWaiqQk):
    Drop-down list:

    function print_screening_field($cnt, $p = null) {
    if ($p === null) {
        $a = $b = $c = '';
    } else {
    	$a = $p['screeningName']; // Change variable name
    	$b = $p['screeningMonth']; // Change variable name
    	$c = $p['screeningYear']; // Change variable name
    }
    	$festivalTerms = get_terms('festival', 'hide_empty=0'); // Change variable name and taxonomy name
    	$festivalNames = wp_get_object_terms($post->ID, 'festival'); // Change variable name and taxonomy name
    	$monthTerms = get_terms('month', 'hide_empty=0'); // Change variable name and taxonomy name
    	$monthNames = wp_get_object_terms($post->ID, 'month'); // Change variable name and taxonomy name
    	$yearTerms = get_terms('year', 'hide_empty=0'); // Change variable name and taxonomy name
    	$yearNames = wp_get_object_terms($post->ID, 'year'); // Change variable name and taxonomy name
    
    	$html .= '<tr id="'. $cnt .'" class="repeatableField form-field">'; // The class name here is important regarding the jquery's part of the code
        	$html .= '<td><label>#'.$cnt.'</label></td>'; // Here the $cnt variable is used just to visually see how many fields we have created
    
        	$html .= '<td>';
    			$html .= '<select name="screeningDetails[' . $cnt . '][screeningName]" id="' . $cnt . '">'; // to name the select makes it work, adding also the $cnt variable to it
    				$html .= '<option value="';
    					if (!count( $festivalNames )) {
    						$html .= 'selected">';
    					}
    				$html .= 'Select a festival</option>';
    				foreach ($festivalTerms as $festivalTerm) {
    				$html .= '<option value="' . $festivalTerm->name . '" '.($a==$festivalTerm->name?'selected':'').'>' . $festivalTerm->name . '</option>';
    				}
    			$html .= '</select>';
    		$html .= '</td>';
    
        	$html .= '<td>';
    			$html .= '<select name="screeningDetails[' . $cnt . '][screeningMonth]" id="' . $cnt . '">'; // to name the select makes it work, adding also the $cnt variable to it
    				$html .= '<option value="';
    					if (!count( $monthNames )) {
    						$html .= 'selected">';
    					}
    				$html .= 'Select a month</option>';
    				foreach ($monthTerms as $monthTerm) {
    				$html .= '<option value="' . $monthTerm->name . '" '.($b==$monthTerm->name?'selected':'').' id="' . $cnt . '">' . $monthTerm->name . '</option>';
    				}
    			$html .= '</select>';
    		$html .= '</td>';
    
        	$html .= '<td>';
    			$html .= '<select name="screeningDetails[' . $cnt . '][screeningYear]" id="' . $cnt . '">'; // to name the select makes it work, adding also the $cnt variable to it
    				$html .= '<option value="';
    					if (!count( $yearNames )) {
    						$html .= 'selected">';
    					}
    				$html .= 'Select a year</option>';
    				foreach ($yearTerms as $yearTerm) {
    					$html .= '<option value="' . $yearTerm->name . '" '.($c==$yearTerm->name?'selected':'').'>' . $yearTerm->name . '</option>';
    				}
    			$html .= '</select>';
    		$html .= '</td>';	
    
    		$html .= '<td>';
    			$html .= '<button class="remove">Remove #' . $cnt . '</button>';
    		$html .= '</td>';
    
    	$html .= '</tr>';
    
    return $html;
    }

    Display on the admin side:

    function screening_details_meta_box(){
    	global $post;
    	$custom = get_post_custom($post->ID);
      	$screeningDetailsData = get_post_meta($post->ID,"screeningDetails",true);
    ?>
      	<h4 style=" text-transform: uppercase;">Other screenings</h4>
      	<table id="data_items" class="form-table" border-collapse="collapse">
    		<caption style="display: none;">List of screenings other than the premiere screenings</caption>
    
    		<thead>
    			<tr class="form-field">
    				<th>Number</th>
    				<th>Name</th>
    				<th>Month</th>
    				<th>Year</th>
    				<th></th>
    			</tr>
    		</thead>
    
    		<tbody>
      		<?php
     	 	$c = 0;
        		if (count($screeningDetailsData) > 0){
        		    foreach((array)$screeningDetailsData as $p ){
        		        if ( isset($p['screeningName']) || isset($p['screeningMonth']) || isset($p['screeningYear']) ){
        		            echo print_screening_field($c,$p); // uses the fonction name
        		            $c = $c +1;
        		        }
        		    }
    	    	}
    		?>
    		</tbody>
    	</table>
        <span id="here"></span>
        <button class="add"><?php echo __('Add a screening'); ?></button>
        <script>
    		var $ =jQuery.noConflict();
    		$(document).ready(function() {
    		var count = <?php echo $c; ?>;
        	$(".add").click(function() {
    			count = count + 1;
            		$('#data_items').append('<? echo implode('',explode("\n",print_screening_field('count'))); ?>'.replace(/count/g, count));
            			return false;
            		});
            		$(".remove").live('click', function() {
            			//$(this).parent().remove(); // When the button is outside the repeatable element
            			$(this).parents('[class*="repeatableField"]').remove(); // When the button is inside the repeatable element
            		});
            	});
        </script>
        <style>
        	table#data_items { margin-bottom: 20px; }
        	tr[class*="repeatableField"]:nth-child(odd) { background-color: #EEEEEE; }
        </style>
        <?php
    }

    Save the data:

    function save_meta_box($post_id){
    	global $post;
    
    	if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE )
    	return $post_id;
    	if (isset($_POST['screeningDetails'])){ $screeningDetailsData = $_POST['screeningDetails']; update_post_meta($post_id,'screeningDetails',$screeningDetailsData); } else { delete_post_meta($post_id,'screeningDetails'); }
    }
    add_action('save_post', 'save_meta_box');

Viewing 4 replies - 1 through 4 (of 4 total)
  • The topic ‘Repeatable fields with dropdown lists of taxonomies' terms’ is closed to new replies.