Jump to content

Dependent Selects


DxJR
 Share

Recommended Posts

So, I've tried looking through here and Can't seem to find it. I'm trying to create a situation in the admin where:

 

I have a page reference InputField. When the user chooses one of those pages, from the dropdown, a new Page reference drop down pops up with that pages children in it. I know how to do the "show only if..." thing, but How did I make it dynamically pull in a pages children? Thanks

Link to comment
Share on other sites

Depending on how complex your needs are, the core does offer some support. Use something like this for the selector value for a page field:

parent=page.otherpagefield

That allows for ajax population of one select based on the selected value of the otherpagefield

  • Like 4
Link to comment
Share on other sites

7 hours ago, adrian said:

Depending on how complex your needs are, the core does offer some support. Use something like this for the selector value for a page field:


parent=page.otherpagefield

That allows for ajax population of one select based on the selected value of the otherpagefield

Is it possible to apply to fields within repeater? It is working great without using repeater, but it is not when the fields are inside a repeater item even after saving the page.

Link to comment
Share on other sites

I needed the same thing so i used Selectize inputfield single for the master select and then a select multiple on the dependent select, then i initialized the 2nd one using Selectize JS;

the JS is not so hard, but you do need to have a custom select multiple that has a data-parent attribute on each option; those are filtered to match the selected option in the 'parent' select. Eventually this will make it into InputfieldSelectize somehow, but for now it's just done by adding JS, courtesy of AdminCustomFiles..

In the screen capture, you will see the left select, which may or may not have child pages; if it does, then the right one shows the children:

5a2b5e47bb562_dependentselects.thumb.gif.2cbb5f9424cf04d13c70fee32dfdad03.gif

 

Spoiler

var initChildSelect = function() {

    // THE "PARENT" FIELD
    var thisID         = $(this).attr('id');
    var repeaterID     = thisID.split('_repeater')[1]; // if in repeater?
    var optID         = parseInt($(this).val()); // the selected option


    // THE "CHILD/DEPENDENT" FIELD
    childWorksSelect = $('select[name^="part_select_repeater'+repeaterID+'"]');
    var allOptions = $(childWorksSelect.children()).clone();
    var validOptions = allOptions.filter(function(){
        return $(this).data('parent') == optID;
    });
    childWorksSelect.html(validOptions);

    var options = {};
    if(!optID) {
        var options = {'placeholder': 'No Work Selected'}
    }

    if(optID && !validOptions.length) {
        var options = {'placeholder': 'No Parts For The Selected Work'}
    }

    childWorksSelect.selectize(options);

    // DEAL WITH CHANGES TO THE PARENT FIELD
    $(this).change(function() {
        var optID = parseInt($(this).val());
        var newOptions = allOptions.filter(function(){
            return $(this).data('parent') == optID;
        });
        childWorksSelect[0].selectize.destroy();

        // Re-init the select
        childWorksSelect = $('select[name^="part_select_repeater'+repeaterID+'"]');
         childWorksSelect.html(newOptions);

         var options = {};

        if(!optID) {
            var options = {'placeholder': 'No Work Selected'}
        }
         if(optID && !newOptions.length) {
             var options = {'placeholder': 'No Parts For The Selected Work'}
         }

        childWorksSelect.selectize(options);
        childWorksSelect[0].selectize.open();
    });

}

$(function(){
    $('select[name^="work_select_single"]').each(initChildSelect);

    $(document).on('reloaded opened repeateradd wiretabclick', '.InputfieldPage', function() {
        $(this).find('select[name^="work_select_single"]').each(initChildSelect);
    });

});

 

 

  • Like 9
Link to comment
Share on other sites

Thanks for your solution @Macrura.

I found that the reason why dynamic function is not working. This is because the class of the select field are amended by the repeater so the jquery is not able to select the DOM correctly. What we should do is to fix it by targeting the correct DOM by changing the selector. I can get it to work with already saved repeater page but not the newly added one. I think a listener is needed to attach the on change event to newly added repeater item when we press Add New.

EDIT: The newly added repeater item surprisingly fires the js again. It is working fine now. A small bug is that the old repeater items will fire more than once if there are more than one newly added item.

Below is the amended JS file for InputfieldPage in quick

Spoiler

function initInputfieldPage($this) {
	
	$this.find("p.InputfieldPageAddButton a").click(function() {
		var $input = $(this).parent('p').next('.InputfieldPageAddItems');
		if($input.is(":visible")) $input.slideUp('fast').find(":input").val('');
		else $input.slideDown('fast').parents('.ui-widget-content').slice(0,1).effect('highlight', {}, 500)
		return false;
	});

	// support for dependent selects
	$this.find(".findPagesSelector").each(function() {
		var $repeater = $this.parents('.InputfieldRepeaterItem');
		var $t = $(this);
		var selector = $t.val();
		// if there is no "=page." present in the selector, then this can't be a dependent select
		if(selector.indexOf('=page.') == -1) return;
		var labelFieldName = $t.attr('data-label');
		var formatName = $t.attr('data-formatname');
		if(!labelFieldName.length) $labelFieldName = 'name';
		// if it doesn't contain a dynamic request from the page, then stop now

		var $wrap = $t.parents(".InputfieldPage");
		var $select = $('select#' + $wrap.attr('id').replace(/^wrap_/, ''));

		if($select.length < 1) return;

		var parts = selector.match(/(=page.[_a-zA-Z0-9]+)/g);

		for(var n = 0; n < parts.length; n++) {

			var part = parts[n];
			var name = part.replace('=page.', '');
			var $inputfield = $repeater.length === 0 ? $('#Inputfield_' + name) : $('#Inputfield_' + name + "_repeater" + $repeater.data('page'));
			if($inputfield.length < 1) return;

			// monitor changes to the dependency field
			$inputfield.off('change');
			$inputfield.on('change', function() {
				var s = selector;
				var v = $inputfield.val();
				if(v == null) {
					// no values selected
					$select.children().remove();
					$select.change();
					return;
				}
				v = v.toString();
				v = v.replace(/,/g, '|'); // if multi-value field, convert commas to pipes
				s = s.replace(part, '=' + v);
				s = s.replace(/,\s*/g, '&');
				if(s.indexOf('_LPID')) s = s.replace(/_LPID[0-9]+/g, '');
				var url = ProcessWire.config.urls.admin + 'page/search/for?' + s + '&limit=999&get=' + labelFieldName;
				if(formatName.length) url += '&format_name=' + formatName;
				$.getJSON(url, {}, function(data) {
					//$select.children().remove();
					$select.children().addClass('option-tbd'); // mark existing options as to-be-deleted
					for(n = 0; n < data.matches.length; n++) {
						var page = data.matches[n];
						// first see if we can find the existing option already present
						var $option = $select.children("[value=" + page.id + "]");
						// if that option isn't already there, then make a new one
						var selected = false;
						if($option.size() > 0) selected = $option.is(":checked");
						$option.remove();
						var label = '';
						if(formatName.length) label = page[formatName];
						if(!label.length) label = page[labelFieldName];
						if(!label.length) label = page.name;
						var $option = $("<option value='" + page.id + "'>" + label + "</option>");
						if(selected) $option.attr('selected', 'selected');
						// add the <option> to the <select>
						$select.append($option);
					}
					$blankOption = $("<option value=''></option>");
					$select.prepend($blankOption);
					$select.children(".option-tbd").remove();
					$select.change();
				});
			});
		}
	});
}


$(document).ready(function() {
	$(".InputfieldPage").each(function() {
		initInputfieldPage($(this));
	});
	$(document).on("reloaded", ".InputfieldPage", function() {
		initInputfieldPage($(this));
	});
}); 

 

EDIT: The bug is fixed. I have submited a pull request. Hope we could see this feature in the core soon!

  • Like 4
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...