Jump to content

Dynamic page field that depends on value of previous field entry?


darrenc
 Share

Recommended Posts

Is it possible to make a page field, that outputs options based on what a user has selected in a previous page field?

Template data setup

  • house (template)
    • title (text)
    • body (textarea)
    • neighborhood (page field)
  • neighborhood (template)
    • title (text)

Obviously it's set up with lots of neighborhood pages, and if you create a house you get a dropdown of those neighborhoods which you can select. 

What I want to do

  • featured_neighborhood (template)
    • title (text)
    • body (textarea)
    • neighborhood (page field)
    • homes_in_neighborhood (page field)

The goal would be for the user to create a new featured neighborhood, choose the neighborhood reference, and then homes_in_neighborhood would be a selection dynamically created from whatever neighborhood that is.

Is this possible in PW? Thanks in advance!

Link to comment
Share on other sites

You can do this with the undocumented dependent selects feature, which AJAX loads the selectable pages in a Page Reference field based on the value of another Page Reference field in the same page being edited.

Your "homes_in_neighborhood" field would use this as the "Selector string" setting:

neighborhood=page.neighborhood, neighborhood!=''

You can limit by template, parent, etc too if you want.

Based on my previous experience:

  • Both Page Reference fields must use a Select, Select Multiple or AsmSelect inputfield.
  • Dependent selects do not work inside Repeater fields.
  • When changing the "source" Page Reference field you sometimes randomly get an option preselected in the "target" Page Reference field. Not a major problem but just something to keep an eye on to avoid accidentally saving an unintended value.

If these limitations are a problem and you don't mind having to save the page after changing the "neighborhood" field then you can use the "Custom PHP code" option for selectable pages instead.

  • Like 6
  • Thanks 1
Link to comment
Share on other sites

  • 1 year later...
On 16.02.2017 at 10:38 PM, Robin S said:

Dependent selects do not work inside Repeater fields.

@Robin S Do you mean inside a repeater field or if items inside 'parent' Page reference field is repeater items? It seems like it doesn't work in both cases. 

Link to comment
Share on other sites

6 hours ago, Zeka said:

Do you mean inside a repeater field or if items inside 'parent' Page reference field is repeater items?

I mean inside a repeater field. Technically it will work there, but not in a useful way. The issue being that the names of inputfields inside a repeater item have a suffix according to the ID of the repeater page. So you would have to include the suffix in the selector for the dependent Page Reference field and therefore it would only ever work inside a single repeater item.

Should be no problem if the pages that make up the selectable options in a Page Reference field happen to be repeater pages. They are treated the same as any other page inside the field.

Link to comment
Share on other sites

  • 1 year later...

It's a while since this thread has been updated, so I thought I'd check whether there's been any update on anyone finding a way to make dependent selects work dynamically inside repeaters.

I've got dependent selects inside repeaters working ok IF I save each time I make a selection, but that's a bit clunky for an end user. I realise there are issues with the way repeaters generate ids that might make ajax based updates impossible but it would be handy.

I originally rejected using pagetables as there are only every going to be a handful of repeater items for each page, and the modal edit method with pagetable fields, rather than inline editing, along with the need to publish each item is not as intuitive as repeaters. A repeater field would be my preference, although I guess I could go back to using pagetables/pagetable extended, but they're not as convenient for end users as a repeater, apart from the issue around dynamic selects.

Link to comment
Share on other sites

On 11/29/2019 at 8:43 PM, Kiwi Chris said:

It's a while since this thread has been updated, so I thought I'd check whether there's been any update on anyone finding a way to make dependent selects work dynamically inside repeaters.

I had a play around and this hook seems to do the job:

Edit: the hook doesn't work because the selected pages are lost on page save. Maybe related to the discussion in this issue.

$wire->addHookBefore('InputfieldPage::render', function(HookEvent $event) {
	/* @var InputfieldPage $inputfield */
	$inputfield = $event->object;
	$page = $inputfield->hasPage;
	$field = $inputfield->hasField;
	// Return early if inputfield is not in a Repeater page
	if(!$page instanceof RepeaterPage) return;
	$selector = $inputfield->findPagesSelector;
	// Return early if no selector or selector doesn't include a dependency
	if(!$selector || strpos($selector, '=page.') === false) return;
	// Get suffix added to inputfields in this Repeater page
	$suffix = str_replace($field->name, '', $inputfield->name);
	// Add the suffix to dependency inputfield names
	$selector = preg_replace('/(=page.[_a-zA-Z0-9]+)/', "$1{$suffix}", $selector);
	// Replace the original findPagesSelector
	$inputfield->findPagesSelector = $selector;
});

Demo...

Page tree:
2019-11-30_145134.png.b477bb8ecf501314504c6d922a69668a.png

Selector string for Subcategory field:
2019-11-30_145023.png.a1619dabcca96ee9d25d2978f35fd1f9.png

Page Edit:

dependent.gif.d90df113c431bacc4e8ec9429ffe629c.gif

 

Edited by Robin S
Hook doesn't work.
  • Like 5
Link to comment
Share on other sites

1 hour ago, Kiwi Chris said:

That seems to partially work, but it seems to have issues if a custom selector returned by a hook in ready.php, rather than a selector configured in the field properties.

I'm not sure if there's a way around that?

The dependent selects feature has only ever worked with the selector string option as far as I know.

Link to comment
Share on other sites

Hi,
thanks @Robin S for your hook example, but also (in my case) I had a problem to save values inside repeaters.
I use 3 fields per/row and out of repeaters everything works fine (PW 3.0.146.). The images below shows my page tree and two different situations (after save).
pw.png.994e1c97fc5f3d218cb56426420d5aea.pngok-1.thumb.png.4af6cc59b73d59b8dc03b2f5aca2567f.pngnok-1.thumb.png.0f3f2228c4548e7f0642c0532d1b9375.png

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Here is another hook example ("cascade" case like on images) but before test, please read setup notes:
1) For the first column field (continents / categories) set parent page inside field setup.
2) For subfields (country and city) set template (eg. "blank") inside field setup.
3) Write selector strings for subfields inside hook configuration (ready.php).

category-field.jpg.ef24ce764468ad66dd8b043175359c66.jpg
pw-ok-setup.png.4985c25a95461b356feb2fbfff6ee992.png

 

 

PHP (ready.php)

<?php namespace ProcessWire;

/**
 *  Configuration:
 *  Field names and selectors
 *
 *  For subfields and after save problem
 *  need to write string selectors here, and leave empty
 *  that part inside field setup.
 */

$config->names = array(
    'continent' => '',
    'country' => 'parent=page.continent',
    'city' => 'parent=page.country',
);

$wire->addHookAfter('InputfieldPage::getSelectablePages', function ($event) {

    $inputfield = $event->object;
    $field = $inputfield->hasField;
    $names = $this->config->names;

    if (isset($names["$field->name"])) {
        $selector = $names["$field->name"];
        if (trim($selector) !== "") {
            if (strpos($inputfield->name, '_repeater') !== false) {
                $id = str_replace($field->name . "_repeater", "", $inputfield->name);
                foreach ($names as $n => $s) {
                    if (strpos($selector, $n) !== false) {
                        $repeater_name = $n . '_repeater' . $id;
                        $selector = str_replace($n, $repeater_name, $selector);
                    }
                }
            }
            $inputfield->findPagesSelector($selector);
        }
    }
}
);

Here is screen record:

cascade.thumb.gif.c20276d62ebe44abd58bf517d248ebcb.gif

Regards.

  • Like 3
Link to comment
Share on other sites

22 hours ago, Robin S said:

The dependent selects feature has only ever worked with the selector string option as far as I know.

Sorry, I checked a case outside of a repeater, and you're right. I have dependent selects working, but only after a save. What I had was several page fields outside a repeater that used custom code in ready.php, and a dependent page field inside a repeater, but the containing page was always saved first, so of course the dependent page fields in the repeater worked.

Thanks to your help in another thread:

I can also get dependent selects working inside a repeater if I save after each field is updated.

There doesn't seem to be an option to refresh an individual page field either inside or outside a repeater via an ajax call other than by the selector string option you've mentioned.

 

Link to comment
Share on other sites

7 hours ago, OLSA said:

thanks @Robin S for your hook example, but also (in my case) I had a problem to save values inside repeaters.

You're right - it doesn't work because the selected pages are lost after save. I remember now that there was an issue with how InputfieldPage and FieldtypePage interpret "page.some_field" when it is used to refer to another Page Reference field: https://github.com/processwire/processwire-issues/issues/479

The thing is that PW has to make some consistent evaluation of what "page" is in this circumstance and it can't really know whether you want page to refer to the edited page or the repeater page. I don't have much time right now but I might come back and explore this more later.

Link to comment
Share on other sites

3 hours ago, Robin S said:

You're right - it doesn't work because the selected pages are lost after save. I remember now that there was an issue with how InputfieldPage and FieldtypePage interpret "page.some_field" when it is used to refer to another Page Reference field: https://github.com/processwire/processwire-issues/issues/479

Yes, but in my hook example selected pages are not lost and to get that I need to remove selector string settings to hook.

You can see how that's works in my previous post (just added screnshot).

Link to comment
Share on other sites

  • 3 weeks later...

hello,

i have a strange behaviour in this matter. on one page there are two page reference fields which are dependent on each other. the selector string for this is

template=foo, refClients=page.refClients, sort=title

if i save the page in admin, the selection works, but not via ajax if i stay on the page in admin. as soon as i change source page reference field, it looks like the selector is not working anymore and everything is displayed in target page reference field.

maybe someone has an idea what i can do better.

thanks a lot

 

Link to comment
Share on other sites

  • 2 years later...

Just came across this thread - very useful:

On 2/16/2017 at 8:38 PM, Robin S said:

the undocumented dependent selects feature

Not sure why it is not documented, unless there are risks using it. It really should be documented IMHO.

@OLSA's solution for repeaters was particularly helpful and was (almost) what I needed for my use case.

My case was complicated by the successive pages not being children of the previous selection - the last page reference was a repeater matrix item, not a child. In my case, however, the 'blendFrom' page reference is a repeater stage in the Cider/Juice page, not a child of it, so parent=page.cider{suffix} does not work. My solution was to add (via a before save hook) a field 'parent_page_id' to the repeater matrix item, which is the id of its getForPage, and use the selector "template=repeater_stage, parent_page_id=page.cider" where 'cider' is the field in the previous page selector. Ideally, I would have liked to have used 'parent.name$=page.cider', as that would avoid the use of an extra field,  but although that seems to work fine normally (in Tracy console, for example), it does not seem to work in this context.

Link to comment
Share on other sites

  • 2 months later...

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

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...