Display Logic Module for SilverStripe 3

28 Jan

 

Hello, SilverStripers!

Have you ever had a page type or DataObject that has fields that should only be displayed if other form fields are set a certain way? Imagine an object that can take either an internal link (TreeDropdownField) or an external link (TextField). If one is filled out, the other should not display.

The Display Logic module allows you to create a very verbose set of logic that, when evaluated, determines whether the field should show or hide. And it $looks->absolutely(“Beautiful”)->thanksTo(“chainable methods”)!

Best of all, it works with Bootstrap Forms!

Example Usage

$products->displayIf("HasProducts")->isChecked();

$sizes->hideUnless("ProductType")->isEqualTo("t-shirt")
      ->andIf("Price")->isGreaterThan(10);

$payment->hideIf("Price")->isEqualTo(0);

$shipping->displayIf("ProductType")->isEqualTo("furniture")
           ->andIf()
              ->group()
                ->orIf("RushShipping")->isChecked()
                ->orIf("ShippingAddress")->isNotEmpty();
              ->end();

 More Information

Please checkout the Github page for more information, including documentation.

36 Responses to “Display Logic Module for SilverStripe 3”

  1. Simon 28. Jan, 2013 at 4:45 pm #

    Are you using Entwine here to trigger things?
    Also, is there a specific reason for using the Field::create() method?
    I like the new FieldType(); method more, personally. It might not be the best method, but the create-method is cluttery to me.

    • Will 28. Jan, 2013 at 5:55 pm #

      Field::create() hooks into the new Dependency injection framework in 3.0 allowing greater customization of those fields.

      • unclecheese 28. Jan, 2013 at 5:57 pm #

        Yup. What he said. Also it hooks into Object::useCustomClass(), and if you’re in PHP < 5.4, it makes the instantiation chainable.

      • Will 28. Jan, 2013 at 5:58 pm #

        Also nice work Aaron! Liking the fluent api use. So this supports front end forms as well as CMS forms?

        • unclecheese 28. Jan, 2013 at 6:15 pm #

          Thanks, Will. Yes. It’s controller agnostic.

      • Simon 29. Jan, 2013 at 6:18 am #

        Ah, ok, then it definitely makes sense. I’m going to rewrite some of my backend I guess.
        Hadn’t realized/read that.
        I guess cluttery looking functionality over preferred cleanliness :)

  2. Jose A. 29. Jan, 2013 at 4:43 am #

    Aaron, wonderful work! You’re a crack. :)

  3. Matthew Balaam 29. Jan, 2013 at 8:04 am #

    Another very useful module. Thanks!

    I am trying out the following code in conjunction with your Bootstrap Forms module:

    OptionsetField::create(‘Options’,_t(‘Content.Options’, ‘option?’))
    ->setSource(array(‘Option1′, ‘Option2′)),

    EmailField::create(‘Option1′,_t(‘Content.Option1′, ‘Option1′))
    ->displayIf(‘Options’)->isEqualTo(‘Option1′)->end(),

    TextField::create(‘Option2′,_t(‘Content.Option2′, ‘Option2′))
    ->displayIf(‘Options’)->isEqualTo(‘Option2′)->end(),

    This then produces the following code:

    – All my regular scripts then appear here, followed by bootstrap_forms.js, jquery.entwine-dist.js and display_logic.js
    – Then this comes next: ($(“#Options”).evaluateEqualTo(“Option1″))

    ….

    • Matthew Balaam 29. Jan, 2013 at 8:06 am #

      That seems to have stripped my code. Should be:

      [div id="Option1" etc.]
      [label etc.]
      [div class="controls"]
      [input name="Option1" etc.]
      [/div]

      – All my regular scripts then appear here, followed by bootstrap_forms.js, jquery.entwine-dist.js and display_logic.js
      – Then this comes next: [script type="text/template" class="display-logic-eval"]($(“#Options”).evaluateEqualTo(“Option1″))[/script]
      [/div]

      [div id="Option2" etc.]

      ….

      • unclecheese 29. Jan, 2013 at 8:41 am #

        Yeah, that’s a really annoying feature of the Requirements system. If you have Requirements::$write_js_to_body enabled, it looks for the first script tag in the body and appends everything after that, rather than just putting everything before the closing body tag. One fix is to set Requirements::set_write_js_to_body(false);, but not everyone likes to do that.

        In a perfect world, it makes the most sense for the parseable logic string to be in a script tag, because it’s naturally hidden, and it is, well, script. But I suppose we could change that to a div and apply a display none to it with CSS. I’m not crazy about it, but I do see the limitation, and I’d much rather a practical solution than a conceptually perfect one.

        • Matthew Balaam 29. Jan, 2013 at 9:35 am #

          Thanks, it makes sense now, I’ve had these issues with requirements before, I wish it was more flexible.

          • unclecheese 29. Jan, 2013 at 9:50 am #

            Can you try changing the script tag to a div (and removing the type attribute) from all the templates that come with the module and let me know if that works?

            Requirements is a can of worms. There needs to be a much more strict dependency management system in place, but there are many ways to do it, and none of them perfect. One of the biggest flaws I see in Requirements right now (for which I don’t have a fix) is that dependencies are called by path, not by name. It’s easy to end up with three or four different jQuery sources in your document!

  4. Matthew Balaam 29. Jan, 2013 at 10:07 am #

    I‘m not sure if this is exactly what you asked for but I changed to:

    This shows all options. Clicking any of the linked radio buttons then toggles all options.

  5. Matthew Balaam 29. Jan, 2013 at 10:07 am #

    Sorry, forgot about tags. [div class="display-logic-eval"][/div]

    • unclecheese 29. Jan, 2013 at 11:00 am #

      Hi, Matthew,

      You haven’t set the source of your OptionsetField properly. Be sure to pass key/value pairs rather than a list, because as you have it, the value you should be checking for is “0″ or “1″..

  6. Andrew Houle 29. Jan, 2013 at 2:18 pm #

    So much cleaner then the hacked up logic I’ve used in the past. Can’t wait to try this!

  7. Andrew Houle 30. Jan, 2013 at 9:39 am #

    Is there a way to listen for if a has_one image has been added? My first attempt was $photooption->displayIf(“Photo”)->isNotEmpty();

    • unclecheese 01. Feb, 2013 at 2:13 pm #

      No support for upload fields. They don’t fire change events. Maybe we can get Zauberfisch to add one

  8. Lx 02. Feb, 2013 at 7:39 am #

    Wow, that looks really cool.
    The rules remind me of our netefx validator.
    But with this chaining they are much smarter.
    So maybe you can reuse this to build a validator of it.

    Regards
    Lx

  9. guci0 06. Feb, 2013 at 10:14 am #

    Miodzio :)

  10. nicolaas 12. Feb, 2013 at 4:20 am #

    love it.

  11. francisco arenas 17. Feb, 2013 at 7:18 pm #

    do i hear silversmith addon??? :D

  12. CW 04. Mar, 2013 at 2:35 am #

    Can anyone verify that their default “Insert Link” popup works with this module?

    Cos when I installed this, the js that handles the default “Insert Link” popup doesn’t seem to fire and everything just displays.

    Is there a clash somewhere?

    • unclecheese 04. Mar, 2013 at 10:24 am #

      Yeah, I just confirmed it. It’s not a JS issue as far as I can tell. If you delete /display_logic/templates/Includes/FormField_holder.ss it works. (But you lose the display logic features).

      This one’s a bit of a mystery right now.

    • unclecheese 10. Mar, 2013 at 2:49 pm #

      This bug is fixed.

      • CW 10. Mar, 2013 at 10:46 pm #

        Thanks unclecheese!

  13. Jamie 22. Jul, 2013 at 4:22 pm #

    Super awesome module!

    Silverstripe has needed this! Does the display-logic module work on FieldGroup and, or TableField elements? Any advice would be greatly appreciated!! Maybe i just did it wrong, but i can’t seem to get them to work with those.

    Thanks for all your contributions!

    • unclecheese 22. Jul, 2013 at 5:23 pm #

      Hi, Jamie,

      If you’re using TableFields, then you’re on SilverStripe 2, and this module is only for SilverStripe 3.

      • Jamie 22. Jul, 2013 at 5:31 pm #

        Thanks for the quick reply, it says it’s still supported in 3. [http://api.silverstripe.org/3.0/class-TableField.html]. Also what about FieldGroup? It would seem like that should work, no? Like you group all your elements in a FieldGroup then show / hide it based on input…

        Am i missing something?

        • unclecheese 23. Jul, 2013 at 7:35 pm #

          TableField should absolutely not be used in SS3. It’s still in the codebase it’s heavily deprecated. Use GridField.

          FieldGroup should work fine. If it doesn’t, it just needs a template.

          • Jamie 24. Jul, 2013 at 11:20 am #

            Thank you very much, I will make this change now. Your advice, timely responses and contributions are very appreciated.

          • Jeff Whitfield 25. Jul, 2013 at 1:25 am #

            I’m trying to get FieldGroup to work as well. Seems that the template for a FieldGroup is actually CompositeField_holder.ss. Having a hellava time trying to figure out what’s necessary to make it hide and unhide. If this could be added to the module it would really enhance the crap out of it. Imagine being able to group a bunch of fields together with a HeaderField and then hide and unhide them based on a drop-down.

            One of the applications I’m using this for is a ContentBlock feature that allows for dropping in multiple content blocks onto a page. The model includes fields for each type of layout. So, for a two-column layout, I allow for two headings, two images, and two content blocks. I put each set into a FieldGroup, which works quite well. However, what would be nice is to hide the second column FieldGroup from view if the Layout dropdown isn’t set to “2-Column”. Pretty simple but just having trouble figuring it out.

  14. Shrike 12. Aug, 2013 at 11:30 am #

    Is it possible to use display logic on Tabs ($fields->findOrMakeTab…)? Any example, how to do it if it’s possible already? Great module!

  15. Chris 23. Mar, 2014 at 7:14 pm #

    How would you use it within a checkboxsetfield? I would have assumed that

    ->displayIf(“foo”)->hasCheckedOption(’5′)->end()

    For example would only show if the checkboxsetfield named foo had the option named ’5′ checked?

    • unclecheese 23. Mar, 2014 at 7:18 pm #

      Yes, that’s correct. ->hasCheckedOption() on a CheckboxSetField takes an argument of either the value of the checkboxfield or its label.

      ->displayIf(“Categories”)->hasCheckedOption(123)
      or..
      ->displayIf(“Categories”)->hasCheckedOption(“Some category”);

      • Chris 23. Mar, 2014 at 8:02 pm #

        Hmm.. When using the following code on the latest SS version, the CommentAboutFruit displays when either Apple or Banana is checked.

        CheckboxSetField::create(‘Fruits’, ‘Fruit list’)
        ->setSource(array(
        ’1′ => ‘Apple’,
        ’2′ => ‘Banana’
        )),

        TextareaField::create(‘CommentAboutFruit’, ‘Enter comment’)->displayIf(“Fruits”)
        ->hasCheckedOption(‘Apple’)->end(),

Leave a Reply