Dynamic (JavaScript based) displayCond for TCA fields

84 Views Asked by At

TYPO3 has the property displayCond in TCA to define "one or more condition rules for whether to display the field or not."

In some cases this condition does not change while the form is open (e.g. REC:NEW or HIDE_FOR_NON_ADMINS), but in some conditions it depends on the value of another field (e.g. REQ) which may change while editing the field. In this case, it is usually necessary to add a 'onChange' => 'reload', to the other field. However, this makes the editing klunky and sluggish, because the record is effectively saved and the form generated via PHP and loaded again. Also a confirmation dialog is displayed and must be confirmed.

enter image description here

Example is pages.canonical_link which depends on no_index (EXT:seo must be displayed).

If a field depends on more then one fields, the form may have to be reloaded multiple times.

I am looking for a solution to reduce the reloading of the forms, most likely via JavaScript.

I have already implemented something like this in one of my extensions where a custom renderType was created anyway. In this case a JavaScript part took care of the conditional rendering of the fields.

However, this not well maintainable in the long term and I am looking for a more generic solution.

I am wondering if anyone has implemented something like this and how you would go about about creating such a solution and making it maintainable long-term.

1

There are 1 best solutions below

0
On

The "easiest" way to achieve this, is probably by adjusting the part of the backend that removes the fields from the rendering, when a displayCond evaluates to false, and instead output them hidden.

The place to overwrite/replace would probably be \TYPO3\CMS\Backend\Form\FormDataProvider\EvaluateDisplayConditions, specifically the evaluateConditions method, that does the actual unset.

You can probably "X-Class" the class, or replace it directly in the FormDataGroup. For the latter see $GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['formDataGroup'] in the Configuration backend module. I think the default one for regular records is tcaDatabaseRecord (might be wrong though).

After that add the displayCond to the output somewhere, which is probably a bit harder, as there are several places where the wrapping "divs" are generated, for example \TYPO3\CMS\Backend\Form\Container\PaletteAndSingleContainer. Alternatively you could try adding it for every field type, but that's probably even harder than just adding it to the container.

I think to overwrite the Containers (or Elements) you'd need x-classing. Not sure if there is a better way.

On the JavaScript side you'd probably need to implement the displayCond parsing and evaluation yourself. The PHP version can be seen in the EvaluateDisplayConditions class I mentioned above, so if you're lucky, you might be able to just translate it.

How to add JavaScript to the FormEngine I'm not entirely sure though. Usually you can add it for individual Elements, e.g. like in the SelectMultipleSideBySideElement.php Element it looks like this:

$resultArray['javaScriptModules'][] = JavaScriptModuleInstruction::create(
    '@typo3/backend/form-engine/element/select-multiple-side-by-side-element.js'
)->instance($selectedOptionsFieldId, $availableOptionsFieldId);

But we don't have the resultArray in FormDataProviders, but we do have it in the Container classes (e.g. \TYPO3\CMS\Backend\Form\Container\TabsContainer), so that might be a viable place to inject your JavaScript if you overwrite some of those anyway.

But there might be a better more "global" place to inject it. I'm not sure.

Some notes:

All of the above requires you to output the fields hidden by displayConds, but you should be aware, that if you output them, they will also be submitted - even if they're hidden - which would be different with the regular displayCond behaviour.

So you might want to temporarily remove the "name" attribute on any input, whenever you hide a field, so it retains the previous behaviour.

Edit added: There might also be issues with form validation when the fields are only hidden. e.g. a field might be hidden by displayCond, but has the "required" eval set. Which means you might end up having the form validator complaining about empty fields, that you can't see, if you just hide them.

I don't think rendering-but-hiding the fields is a security issue, as displayConds are typically not intended for limiting the permission of fields to editors or something like that. But I wouldn't bet on it not being used somewhere like that.

Also, there is a good chance that any backend changes like that will break with the next TYPO3 major release. I speak from experience :D


As said, the above is the "easiest" way I believe.

Alternatives would require rewriting the whole FormEngine mechanics, like completely rendering the Form on the client side (maybe with a JavaScript framework), instead of the PHP side as it's currently done.

But that would be quite a huge undertaking, and maybe more something to suggest to the TYPO3 core team, though while they might find it interesting or even a good idea (debatable), I doubt anyone would want to or even have the time to spent implementing that.


I hope this helps you somewhat.

Greetings

Nitori