craftcms / cms

Build bespoke content experiences with Craft.
https://craftcms.com
Other
3.28k stars 635 forks source link

Conditionals in field layouts #805

Closed angrybrad closed 3 years ago

angrybrad commented 7 years ago

Created by: Mats Mikkel Rummelhoff (mail@mmikkel.no) on 2015/03/09 00:12:31 +0000 Votes at time of UserVoice import: 355


Coming from Wordpress and ACF, I miss being able to set up simple conditionals for fields.

An example scenario:

A field layout for the section "Albums" includes a true/false called "Compilation". If unchecked, the layout displays a text input field called "Artist name". If checked, the "Artist" field is hidden and replaced by a Table field called "Artists", where the author is able to enter any number of artists featured on the album.

In ACF, all fieldtypes has a "Conditional logic" checkbox. If checked, the field creator is able to build their conditions for showing or hiding the field, based on any selectable (as in checkbox, radio buttons, dropdowns, true/false etc) "sibling" fields in the same Field Group (or Layout, as it were). You can also apply conditionals to fields inside Repeaters (ACF's version of the Table field) and Flexible Content (Matrix) fields, which works the same way – the conditonal builder UI relies on selectable "sibling" fields in the current context.

In the example scenario, the condition for showing the "Artist" field would be [IF "Compilation" IS NOT EQUAL TO "checked"], and for the "Artists" field it would be the opposite.

Conditionals are not super important in Craft, as we have Entry Types, but I can definitely see use cases where one would be more elegant than the other, and I'm often encountering situations where creating a whole separate entry type just to be able to offer a small amount of flexiblity in the layout just seems a bit overkill. If Craft had conditionals, I'd use Entry Types only when the field layout would be more different than similar across various content types in the same section.

To close, one huge difference between Craft and ACF is obviously that fields in Craft are shared between layouts, whereas fields in ACF are tightly coupled to their group. This means that any conditional logic in Craft would probably have to be applied in the Layout Designer (possibly by adding a "Conditional logic" button to the fields' cogwheel/settings button, which could then load up a conditionals builder in a modal or whatever) and not when creating any individual field.

For Matrix fields, however, it would make sense to be able to add conditionals across block types and their fields in a more similar manner to the ACF way.

angrybrad commented 7 years ago

Posted by Mats Mikkel Rummelhoff (mail@mmikkel.no) on 2015/03/09 00:12:31 +0000

Just wanted to let voters know that I've updated the Reasons plugin today, with support for all native element types (Entries, Users, Assets, Categories, Tags and Global Sets), as well as support for element editor modals: https://github.com/mmikkel/Reasons-Craft/

angrybrad commented 7 years ago

Posted by Ben Parizek (ben@barrelstrengthdesign.com) on 2015/03/12 23:19:32 +0000

We would love to add conditional layouts in our Sprout Forms plugin, but we also have the need for conditional layouts in several other Field Layouts. We'd prefer not to re-invent the wheel and love the consistency throughout Craft's interface and would like to stay true to it as much as possible.

I'd love to see Craft implement this in core, at least for simple conditionals. If there is a need for more advanced conditionals, maybe that can be handled by allowing plugin developers to add new conditional options for users to choose from.

angrybrad commented 7 years ago

Posted by Mats Mikkel Rummelhoff (mail@mmikkel.no) on 2015/03/09 00:12:31 +0000

FYI: I wrote a plugin which implements conditionals in field layouts: https://github.com/mmikkel/Reasons-Craft

Considering the popularity of this FR, it's probably not impossible that field layout conditionals will end up in core some day – until then though, I hope Reasons will be a decent patch. Please not that the plugin is in a really early state, and there will be more to come.

I'm also curious about what people think about the template side of things – should field layout conditionals affect templating in some way? Currently, Reasons is strictly about increasing usability in entry forms (same as ACF) and the conditionals are not available to templates in any way. One idea that has been thrown around is to add a "visible" true/false property to the fields. I'm on the fence about whether this is needed or not, though.

angrybrad commented 7 years ago

Posted by Mats Mikkel Rummelhoff (mail@mmikkel.no) on 2015/03/09 00:12:31 +0000

@Daryl The way this works in ACF, the field layout conditionals have no effect on the template side. It's strictly a CP UX effort, where single fields are hidden/displayed on context in order to elevate usability for the editor.

To follow the above example; this would mean that even if the single "Artist" field is hidden in the CP (because the editor has checked the "Compilation" lightswitch), it may contain data and the template will need its own conditionals, yes.

angrybrad commented 7 years ago

Posted by Daryl (craft@codeknight.co.uk) on 2015/03/12 09:36:29 +0000

How would this work in the template? You'd have two sets of conditionals to account for...

angrybrad commented 7 years ago

Posted by Mats Mikkel Rummelhoff (mail@mmikkel.no) on 2015/03/09 00:12:31 +0000

@Arik Jones: Yeah, like I mentioned in the description above, the conditionals would have to be defined in the FieldLayout somehow. As the fields are shared, there's really nowhere else that makes sense.

I actually put together a super quick mockup to illustrate the basic idea (in terms of UI) as outlined in the description. There are problems and the "conditional builder" is a blatant ACF rip-off, but to illustrate:

http://cly.mmikkel.no/image/16381k163G2n/1.jpg http://cly.mmikkel.no/image/2d0B3O2K282q/2.jpg

As for field dependencies, I'm not sure I follow 100% but to me that sounds like a good addition to the proposed conditionals, not really a better/different approach entirely?

angrybrad commented 7 years ago

Posted by Arik Jones (tnypxl00@icloud.com) on 2015/04/06 14:06:26 +0000

Also, you could optionally look for specific values.

angrybrad commented 7 years ago

Posted by Arik Jones (tnypxl00@icloud.com) on 2015/04/06 14:06:26 +0000

A better approach would be field dependencies. Field X depends on non-null values in fields A,C and U. I would think we would probably do this in the Field Layout screens. That way you're not pigeon held by the default field configs.

evanwarner commented 6 years ago

Bonus points for conditionals affecting the required-ness of subsequent fields! e.g. if I flip lightswitch A, fields B and C show up and become required.

theskyfloor commented 6 years ago

Also prob worth checking out the latest ACF release... they redid conditionals and it offers a lot of power and flexibility:

https://www.advancedcustomfields.com/blog/acf-pro-5-7-0-new-architecture-new-features/

khalwat commented 6 years ago

Really hoping this happens.

mortscode commented 6 years ago

Recently forced to do a WP site at work, and while Craft is better in every possible way, the ACF conditionals are a game-changer.

Migrating my site to Craft 3, and it's frustrating to have to pay for LinkIt when all I need is a simple conditional. I guess I could write my own plugin.

Really hoping this happens.

daltonrooney commented 6 years ago

@mortscode Have you tried https://github.com/sebastian-lenz/craft-linkfield ?

mortscode commented 6 years ago

Frick @daltonrooney. MAGIC!!

brandonkelly commented 6 years ago

What do conditionals have to do with LinkIt / Link ?

theskyfloor commented 6 years ago

@brandonkelly I think he is just implying that he could create LinkIt / Link functionality with native fields alone if conditional display was a thing... i.e. Select "Entry" from a link dropdown and show an entry relationship field, select "Facebook" and show a field called Facebook Profile URL, etc.

brandonkelly commented 6 years ago

Right OK, that makes sense.

FWIW we are planning on giving field layouts a major overhaul in 3.2, including conditional fields.

incraigulous commented 6 years ago

AWESOME! @brandonkelly

putyourlightson commented 6 years ago

+1

bennobo commented 6 years ago

+1

dougstjohn commented 6 years ago

+1

RyanAaronGreen commented 6 years ago

+1

theskyfloor commented 6 years ago

Would also love to see options like show only this field or that field depending on structure level. I know I can achieve that kind of granularity with entry types but I want it to be automatic so that my clients dont have to remember or guess what to do. Thanks for the consideration!

shantamg commented 5 years ago

Right OK, that makes sense.

FWIW we are planning on giving field layouts a major overhaul in 3.2, including conditional fields.

Any clues when 3.2 is scheduled for release?

brandonkelly commented 5 years ago

@shantamg Well we need to finish 3.1 first :) That should be done in late December or early January. Guessing 3.2 will be out ~6 months later.

shantamg commented 5 years ago

Gotcha. I have a project that I'm excited to move to Craft 3 and I think this is the last dependency I'm waiting for. :)

baryla commented 5 years ago

@brandonkelly - still on track for this to be in 3.2? :)

brandonkelly commented 5 years ago

Not exactly… we started planning it and realized that to do it right we’re going to need to make a few other changes as well, so we’re going to do those as a 4.0 release instead. (We’ll get a 3.2 and other 3.x releases out before 4.0 though.)

baryla commented 5 years ago

@brandonkelly ah that's a shame. When I get some free time I'll have a crack at creating this.

nikolowry commented 5 years ago

Until this feature is implemented, I found an easy-ish way to achieve this manually was to utilize the following plugins with the window.MutationObserver API:


Edit: Fix broken link and grammar

shantamg commented 5 years ago

@nikolowry That sounds promising. Can you show us what you did to achieve conditional fields? By the way, the second link is missing an 's' at the end (should be https://github.com/doublesecretagency/craft-cpcss)

nikolowry commented 5 years ago

@shantamg I could only find a decent example on an older Craft instance (v3.0.0-RC6) -- the following example should work in newer versions, but some selectors might need updating.

This older instance also doesn't have anything like Field Manager installed (this should be baked into core IMO), so the best I can do with short notice is give a quick overview of the field and take screenshots of the backend config.

Field Overview

Type: Matrix
Handle: nav
Block Types:
    Block:
        Block Type Name: Item
        Block Type Handle: navItem
        Block Type Fields:
            Field:
                Field Name: Type
                Field Handle: itemType
                Field Required: True
                Field Type: Dropdown
                Dropdown Options:
                    Option:
                        Label: Internal
                        Value: internal 
                        Default: True
                    Option:
                        Label: External
                        Value: external 
                        Default: False
            Field:
                Field Name: Internal Link
                Field Handle: itemIternalLink
                Field Required: False
                Field Type: Entries
                Entries Options:
                    Sources: All
                    Limit: 1
            Field:
                Field Name: Title
                Field Handle: itemExternalTitle
                Field Required: False
                Field Type: Plain Text
            Field:
                Field Name: External Link
                Field Handle: itemExternalLink
                Field Required: False
                Field Type: [Match Input](https://github.com/marionnewlevant/craft-match_input), but Url is fine
                Match Input Options:
                    Input Mask RegEx Pattern: /(?:(?:https*?|ftp|file):\/\/|www\.|ftp\.)(?:\([-\w\d+&@#\/%=~_|$?!:,.]*\)|[-\w\d+&@#\/%=~_|$?!:,.])*(?:\([-\w\d+&@#\/%=~_|$?!:,.]*\)|[\w\d+&@#\/%=~_|$])/
                    Error Message: Invalid url
                    Placeholder Text: Url

Screenshot: Screenshot from 2019-04-09 21-19-02

Javascript

(function () {
    // Toggle Internal/External Link visibility in Navigation Matrix
    var fieldNavMatrixController = function () {
        // Only Modern Browsers
        if (!('from' in Array))
            return;

        var target = document
            .querySelector('#fields-nav-field .blocks');

        var setDataItemType = function (node, val) {
            if (!node)
                return;

            val = val.match(/(internal|external)/)
                ? val
                : 'internal';

            node.setAttribute('data-item-type', val);
        };

        var setFieldClass = function (node) {
            var isItemLink = (node.id || '')
                .match(/item(Internal|External)(Link|Title)/);

            if (isItemLink)
                node.classList.add(isItemLink[0]);
        };

        // On Init, update pre-existing fields' data-attr & add listeners
        Array.from(target.querySelectorAll('.fields'))
            .forEach(function (node) {
                if (!node.querySelector('select'))
                    return;

                setDataItemType(node, node
                    .querySelector('select option[selected]')
                    .getAttribute('value'));

                node.querySelector('select').onchange = function (e) {
                    setDataItemType(node, e.target.value)
                };
            });

        // On init, set pre-existing fields' classes
        Array.from(target.querySelectorAll('.field'))
            .forEach(function (node) {
                setFieldClass(node);
            });

        // Create an observer instance
        new window.MutationObserver(function (mutations) {
            mutations.forEach(function (mutation) {
                if (!(mutation.addedNodes || []).length)
                    return;

                Array.from(mutation.addedNodes)
                    .forEach(function (node) {
                        if (!node
                                || !('querySelector' in node)
                                || !node.dataset
                                || node.dataset.type !== 'navItem')
                            return;

                        var fields = node.querySelector('.fields');
                        var select = node.querySelector('select');

                        if (!fields || !select)
                            return;

                        setDataItemType(fields, select.value);

                        node.querySelector('select').onchange = function (e) {
                            setDataItemType(fields, e.target.value);
                        };

                        Array.from(node.querySelectorAll('.field'))
                            .forEach(function (node) {
                                setFieldClass(node);
                            });
                    });
              });
        })
        .observe(target, {
            childList: true,
            subtree: true
        });
    }

    if (document.querySelector('[name="fields[nav]"]'))
        fieldNavMatrixController()
})();

CSS

#fields-nav-field .fields > .field.itemInternalLink,
#fields-nav-field .fields > .field.itemExternalLink,
#fields-nav-field .fields > .field.itemExternalTitle {
    display:none;
}

#fields-nav-field .fields:not([data-item-type]) > .field.itemInternalLink,
#fields-nav-field .fields[data-item-type="internal"] > .field.itemInternalLink,
#fields-nav-field .fields[data-item-type="external"] > .field.itemExternalLink,
#fields-nav-field .fields[data-item-type="external"] > .field.itemExternalTitle {
    display:block;
}

Twig Example Usage

<nav>
    <ul>
        {% for item in header.nav.all() %}
            {% set itemType = item.itemType %}

            {% set field = item[itemType == 'internal'
                ? 'itemInternalLink'
                : 'itemExternalLink'] %}

            {% if field is defined and field|length %}
                {% set itemHref = itemType == 'internal'
                    ? field[0].url|raw
                    : field|raw %}
                {% set itemTitle = itemType == 'internal'
                    ? field[0].title
                    : item.itemExternalTitle ?: '' %} 
                {% if itemHref|length and itemTitle|length %}
                    <li>
                        <a
                            href="{{ itemHref }}"
                            {% if itemType == 'external' %}
                                target="_blank"
                                rel="noopener"
                                title="Opens in New Tab"
                            {% endif %}
                        >{{ itemTitle }}</a>
                    </li>
                {% endif %}
            {% endif %}
        {% endfor %}
    </ul>
</nav>

Screen Capture (Click image or direct link) craft-psuedo-conditionals_thumb_video

Direct Link: https://neeks.me/cloud/index.php/s/EWS4JMS3WERRJBa

zzseba78 commented 5 years ago

I miss Reasons!

dougstjohn commented 5 years ago

The lack of conditionals was the largest pain point in my latest build as I do large ContentBuilder matrix fields that are used across many EntryTypes. I resorted to creating tabs with the MatrixMate plugin to try to convey to the user they should only set data in one of the first 3 tabs.

matrix-tabs

daltonrooney commented 5 years ago

@dougstjohn I had to do the exact same thing on my most recent project, and I used MatrixMate. Great plugin. You can also do things like hide matrix blocks on a per-section basis (as in #4252)

I saw there's a new plugin called Conditions in the plugin store that brings back the Reasons functionality, but when I installed it, it started outputting admin styles on my front-end which really messed up the site. I think they're working on that issue, will take a look again in a few weeks.

zzseba78 commented 5 years ago

This is solved? Is there a way to use conditional fields in Craft 3? ( like we did in v2 with reasons?). Forget about plugin called Conditions, it´s useless.

baryla commented 5 years ago

@zzseba78, nope it's not yet available in 3.2. It's most likely going to be a 4.0 according to @brandonkelly

brandonkelly commented 5 years ago

It certainly will :)

zzseba78 commented 5 years ago

oh what a pity... this is a very useful feature! Thanks for your response.

nitech commented 5 years ago

Yes, this will solve a whole lot of challenges.

jlcharette commented 4 years ago

BTW, original Reasons now available for Craft 3.

https://plugins.craftcms.com/reasons

jakepm commented 4 years ago

@brandonkelly I was about to add a feature request, for something related to this, but a quick search shows that this idea is a better solution - with a slight addition.

It would also be really useful if the visibility of fields could also be dependent on the Site loading the corresponding entry.

I recently found myself wanting to show/hide certain fields for a Single entry, based on the site/language. The initial idea I had was to allow a different field layouts for a Single entry, per Site. However, if fields could be shown/hidden for specific Sites, right in the field layout editor... that would also work. Especially for Global fields, which aren't always needed across Sites.

For full context, we have a website in three different languages. All content across the site is editable, but the structural content for the Japan site is vastly reduced. As such, most of the fields are not needed and we didn't want the site owner to be confused. We therefore created a second Single entry instead - but from an organisational and SEO point of view, they're both the same page. This created knock-on issues for the way we were generating hreflang tags in the HTML. Same goes for the global fields; there are many unwanted/irrelevant fields when editing globals for the Japan site which we can't currently do anything about.

missmatsuko commented 3 years ago

It would be useful to have conditional fields or some way of making one of different fields required.

For example, requiring either an alt text or a checkbox stating no alt text is needed.

JshGrn commented 3 years ago

So.... what is going on with this now... is it coming... or is it deemed not required?

I just don't see how there isn't a viable solution to this? Example here:

Matrix field Buttons

Button Name - text Button Source - select (value toggles visibility and required check of one of the field below, others are hidden) Link - text, shows only if source === link Asset - asset, shows only if source === asset Entry - entry, shows only if source === entry

How has Craft got here without that native functionality? Has anyone had any actual viable solutions to this?

daltonrooney commented 3 years ago

@JshGrn For this example, I use Typed Link Field: https://plugins.craftcms.com/typedlinkfield.

For other types of content where I would typically use a conditional field, I often use a Matrix. It's not quite as flexible but usually gets the job done and is a bit more predictable on the templating side.

brandonkelly commented 3 years ago

It’s currently planned for v4. In the meantime you can use Reasons.

JshGrn commented 3 years ago

It’s currently planned for v4. In the meantime you can use Reasons.

Not supported within a Matrix field, I am trying to recreate ACF for my clients and without this it is very hard to get them to pay for a CMS. 👎 Is there a roadmap for v4 that I can see what sort of date we are aiming at? I thought I read somewhere v4 is not planned and that features will just come to new 3.x versions instead?

brandonkelly commented 3 years ago

We’re working on publishing a roadmap; will update this when it’s out there. Timing-wise we’ll have more to say in a couple months at our Dot All conference.

theskyfloor commented 3 years ago

Considering this issue first started as a feature request in 2015, I wouldn't hold my breath @JshGrn. That being said you can probably get around needing real conditionals several different ways. The plugin @daltonrooney recommended is one we use and it works well and there is another field called LinkIt which also works super well. Both of those plugins let you define custom link text and select links from a long list of sources and for most circumstances the single field can easily power your entire button code. If you have other needs for conditionals we would all be happy to brainstorm solutions!

JshGrn commented 3 years ago

@theskyfloor Yep, not holding breath... looks like a good plugin. I am yet to try it as I ended up doing it in a Neo field, although I think I will give that plugin a go as Neo is a bit heavy for my purpose.

It is bonkers that 6 years on its still not something added, I don't understand why its not a priority as people will be finding it hard to migrate from ACF without it.... with this... more people would migrate and therefore 1) more people would be actively developing within Craft and 2) more people would be paying licences when more people use Craft...

Thanks for the solutions all, hope it also helps someone else looking for conditionals on the same topic.... Will eagerly keep ears and eyes open for Dot All.