Jump to content
a-ok

PageField – limit number of choices

Recommended Posts

Hi folks,

I have a simple PageField set up, which is being used in this instance as a 'Related Articles' area; they can choose (Multiple Pages) which news articles are related then it does something nice front end. The only issue I have is that I'd like to limit the number of articles they can choose (only allowing three, for example) much like you would have in a File field or Image field (Maximum files allowed).

Any thoughts?

Share this post


Link to post
Share on other sites

This greatly depends on the inputfield you're using as it's javascript which would solve this. The only thing you could do from pw aka php is stripping additional pages when saving the page.

Share this post


Link to post
Share on other sites

I am using the Page field. So I'm guessing there's no option? Seems a little like an oversight, do you think (not to sound as mean as it looks!)?

Share this post


Link to post
Share on other sites

I'm talking about the inputfield you choose the page field to use, e.g. Select, ASMSelect, PageListSelect and so on. These all work differently in terms of markup and javascript, so it's understandable that there's no central way to limit the number of selections beyond single / multiple. Single and Multiple pagefields are even mostly using different inputfields. 

Share this post


Link to post
Share on other sites

Ah right, I understand. I am using the ASMSelect for multiple pages. I thought, above all, if you have multiple pages you should at least be able to limit the amount you choose.

Share this post


Link to post
Share on other sites

You can always strip off additional pages using php, but that's certainly not the best user experience. For the ASM Select you could create a javascript, which does disable all options from the select box if a certain number of pages was selected (and reset if not).

Share this post


Link to post
Share on other sites

nice challenge for the evening with a happy end :)

create a javascript file scripts/limitpageselect.js: 

// my field is called "pageselect"
$(window).load(function() {
    if($('#Inputfield_pageselect :selected').length > 4) $('#Inputfield_pageselect').siblings('select').hide();
    $('#Inputfield_pageselect').change(function() {
        if($('#Inputfield_pageselect :selected').length > 4) {
            $(this).siblings('select').hide();
        }
        else {
            $(this).siblings('select').show();
        }
    });
});

and inject this file via hook in /site/ready.php

$wire->addHookAfter("ProcessPageEdit::buildForm", function(HookEvent $event){
    $process = $event->object;
    $page = $process->getPage();

    if($page->template != 'basic-page') return; // set your template name here

    $config = wire('config');
    $config->scripts->append($config->urls->templates . 'scripts/limitpageselect.js');
});

post-2137-0-99193100-1448405819_thumb.gi

  • Like 7

Share this post


Link to post
Share on other sites

I wonder if it's better to wrap the whole hook in such a condition:

if($page->template === 'basic-page') {
    $wire->addHookAfter(...)
}
  • Like 2

Share this post


Link to post
Share on other sites

The $page variable is only available through the HookEvent, so you cannot dynamically add the hook, besides if you already have the exact instance of the class you want to hook.

Share this post


Link to post
Share on other sites

I see, but in ready.php the $page is already determined, isn't it?

This snippet actually works:

// content in ready.php

if ($page->template != 'admin') {
    $wire->addHookAfter("Page::render", function (HookEvent $event) {
        echo <<< HTML
    <script type="text/javascript">alert('Hello world');</script>
HTML;
    });
}

I guess it's because it hooks to Page:: render.

Share this post


Link to post
Share on other sites

$page is of the admin template for everything you visit in the backend. But most of the time you're interested in the page your editing and not the "admin page" your on. 

  • Like 1

Share this post


Link to post
Share on other sites

hi tpr,

this method works in frontend but not in admin! try

echo $page->id;

it will show "10" for all processpageedit pages... /admin/setup/ would be 22

unfortunately there's a bug in my script: when you save a field with 5 pages it will not remove the select-field because its only called on change of the field. the problem is, that the select-field is injected by jquery by the admin so this does not work:

// my field is called "pageselect"
$(function() {

    $('#Inputfield_pageselect').siblings('select').hide(); // added this line

    $('#Inputfield_pageselect').change(function() {
        if($('#Inputfield_pageselect :selected').length > 4) {
            $(this).siblings('select').hide();
        }
        else {
            $(this).siblings('select').show();
        }
    });
});

maybe it would work if the script was loaded at the very last of all scripts?

post-2137-0-12119600-1448442893_thumb.pn

how could i achieve that?

Share this post


Link to post
Share on other sites

Hi BernhardB,

I have a small module that injects a script tag right before </body>. Here's the relevant lines

	public function init() {

		$this->addHookAfter('Page::render', $this, 'addInlineScript'); 

	}

	public function addInlineScript($event) {

		$page = $event->object;

		if ($page->template->name !== "admin" && $page->inlineScript) {

			$event->return = str_replace("</body>", $page->inlineScript . "</body>", $event->return);

		}

	}

You can adjust that to your situation. Hope it helps.

  • Like 1

Share this post


Link to post
Share on other sites

Just use $(document).ready(function(){ … }); around your code, so it's not executed before the whole dom is loaded.

Share this post


Link to post
Share on other sites

@LostKobraKai

Wouldn't you actually need window.onload or $(window).load for that?

$(document).ready fires when the DOM is ready and does not wait for all resources like scripts/images.

See here and here.

  • Like 1

Share this post


Link to post
Share on other sites

thank you!

i was already using document.ready (short syntax) - gebeer was right, both injecting the JS via str_replace(</body> and loading the script via window.onload worked!

i updated the code above so it should be working now.

Share this post


Link to post
Share on other sites

Some improvements in the JS:

$(window).load(function () {

    var fieldName = 'tags_recipe',  // add field name here
        limit = 5,  // set limit here
        asmSelector = '#Inputfield_' + fieldName;

    $(asmSelector).on('change limitItems', function () {
        var obj = $(this).parent().find('.asmSelect');
        $(this).find(':selected').length >= limit ? obj.attr('disabled', 1) : obj.removeAttr('disabled');
    });

    $(asmSelector).trigger('limitItems');
});

What would be nice is to add the "limit" in the backend to the asmSelect using a "data-limit" attribute and then let JavaScript do its part on all asmSelects which have data-limit set.

  • Like 5

Share this post


Link to post
Share on other sites

@tpr - have you considered adding this functionality to AOS?

Or maybe a @Robin S "limit" special module :)

  • Like 2

Share this post


Link to post
Share on other sites

I haven't yet but I'll give a try. 

  • Like 4

Share this post


Link to post
Share on other sites

It's done, will be added to the next aos update. I've borrowed some code from @Robin S modules (thanks!) as I haven't added config inputs to fields so far.

In fact I totally forgot about this asmSelect snippet of mine, thanks for the reminder :)

 

limitasmselect01.png

limitasmselect.gif

  • Like 5

Share this post


Link to post
Share on other sites
23 minutes ago, tpr said:

It's done, will be added to the next aos update.

Awesome!

Do you reckon you could do something similar for Page Autocomplete? :)

Share this post


Link to post
Share on other sites
6 hours ago, Robin S said:

Do you reckon you could do something similar for Page Autocomplete?

I saw this follow-up question coming, 😀 I think that's also doable, will check that later. I reckon the same question is about to emerge for the rest of the PageField types, isn't it? 😀

  • Like 1
  • Haha 1

Share this post


Link to post
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

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By EyeDentify
      I have been experimenting with the new $page->meta() method and find it useful.

      Once i figured out that the data i "save" with it is tied to the page where i called the method from.

      So this is not obvious at least not for me in the documentation:
      https://processwire.com/api/ref/page/meta/
       
      So i just wanted to share that revelation with the community so you don´t get as confused as i was.

      Happy Coding Everyone.
    • By stanoliver
      In the new page-meta-method (https://processwire.com/blog/posts/pw-3.0.133/#comments) there is an example how to ouput "world". 
      Could may someone give me a snippet of code so that the output is one of the colors (red, green, blue).
    • By VeiJari
      Hello forum. I'm trying to figure out how to detect if current form has errors after saving it in pw admin page. This is so that our event location isn't added in database if we detect errors. But I can't seem to find a correct way to detect errors? I only found the wire()->errors() and it always triggers even if I don't have any errors. 
      How can I detect errors in a form?
      By error I mean $page->error('this is the red error');
    • By VeiJari
      Hello forum!
      I've yet again stumbled on a head-scratching situation. We have enabled the option on our articles template and events template that it skips the title adding part and goes straight to the form. This is what our customer wants. So when you add a new article or event it automatically names it temporary to "article-0000000" and same with event. Now the problem is that obviously after saving the form we want to change to page url or "name" to the title, like it's normally. 
      Now here's the code for the hook:
      wire()->addHookBefore("Pages::saved(template=tapahtuma|artikkeli)", function($hook) {
        $page = $hook->arguments(0);
        $newUrl = wire()->sanitizer->pageName($page->title); // give it a name used in the url for the page
        wire()->log->message($page->name);
        $page->setAndSave('name', $newUrl);
      });
      I get the correct page and the name and path changes when I log them, but when I try to save it. It just loads and then I get: 
      Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 262144 bytes) This happens in sanitizer.php
      and then another error: Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 262144 bytes) in Unknown on line 0
       
      What is happening? Am I not suppose to use sanitizer in this way? When we made a temporary page object in out other hook, the sanitizer worked perfectly.
      Thanks for the help!
    • By VeiJari
      Hello, our customer doesn't like the fact that they have to first add a title and then add the other info in the form. Therefore, we've enabled the way to skip this by making a temporary file before saving.
      But the problem is that by using module schedulepages in this piece of code in the module:  
      if (!$page->isNew() && $page->publishable() && $page->isChanged('status') && $page->is(Page::statusUnpublished) && $page->publish_from) {
                  $this->session->error($this->_("“Publish From Date” field was cleared to prevent the page from being unintentionally re-published on the next Lazy Cron run."));
                  $page->publish_from = null;
                  $page->save('publish_from');
              }
      it fires even with the temporary file and therefore after first save it resets the publish_from field. Is there a way to check if template is temporary, or something along those lines?
      One solution is just to ask the customer to first save the form after adding a title, but I don't see that as a good solution.
×
×
  • Create New...