TYPO3 / Fluid

Fluid template rendering engine - Standalone version
GNU Lesser General Public License v3.0
152 stars 93 forks source link

Template parser issue when using curly braces for inline js or css #808

Open stefanr opened 1 year ago

stefanr commented 1 year ago

Sometimes when using curly braces for inline javascript or css styles inside a fluid template the parser breaks and does not further resolve variables from this point up until some other view helper is used via xml syntax or the scope (section, partial, ...) is closed.

I didn't look into the implementation in detail so it currently seems a little "random" at which point the parser stops working.

Example:

<f:variable name="test" value="foo" />
<div
  x-data="{
  test: null,
  init() {
    test = 'foo';
  }
  }"
>
  {test}
</div>

This correctly (or let's say as expected), gives: foo

This on the other hand (focus on the indentation):

<f:variable name="test" value="foo" />
<div
    x-data="{
      test: null,
      init() {
        test = 'foo';
      }
    }"
>
  {test}
</div>

... renders as: {test}

Placing a view helper before the variable output seems to fix wrong parser state:

<f:variable name="test" value="foo" />
<div
    x-data="{
      test: null,
      init() {
        test = 'foo';
      }
    }"
>
  <f:variable name="void" value="" />
  {test}
</div>

... renders again: foo

Any ideas? This issue is kind of a problem as one might not expect to destroy the following template code by changing some attributes that also render completely fine in the frontend.

Wrapping the problematic blocks like so x-data="<f:format.raw>{ ... }</f:format.raw>" is the current work around to fix this issue.

sbuerk commented 1 year ago

@stefanr Thank you for the issue report. However, I would say, this is not a bug. It is how it is worked and documentented.

{} are fluid specific chars which marks the start end end of variables or inline code. So they have a special meaning.

You are trying to mix this some how. There are multiple ways to mitigate this, which are already documentend and explained, at least for the TYPO3 world here:

https://docs.typo3.org/m/typo3/guide-extbasefluid/main/en-us/Fluid/ThingsToKnow/JsAndInline.html

Granted, the context describes it for inline javascript, but this also counts for "json" or "JavaScript object like" markup creation.

It's a casual issue if you deal with kind of a parsed language, that special meaning chars needs to be escaped in the one or other way.

Not tested, but I guess following "should" work.

<f:variable name="test" value="foo" />
<f:format.cdata>
<div
  x-data="{
    test: null,
    init() {
      test = 'foo';
    }
  }"
>
</f:format.cdata>
  {test}
</div>

You could also wrapp the { and } of your "markup" code which should not be parsed into the format.raw viewhelper (also described in the link documentation page above).

Another solution is to create a custom viewhelper which creates the "data attribute" content and the structure instead of dealing wiht the definition in the markup if possible. Otherwise, I guess the cdata approav is the "best" way to do it as it avoids a lot of char based wrappings/escapings.

radmiraal commented 1 year ago

@sbuerk while agreeing on the {} special meaning I still think there's something going on. It seems like there's a randomness factor in this issue. I have a template with 1 x-data attribute, an x-bind:class attribute and those 2 attributes:

x-on:keyup.tab="<f:format.raw>Alpine.$data($el).__open()</f:format.raw>"
x-on:mouseup="<f:format.raw>Alpine.$data($el).__open()</f:format.raw>"

The following happens:

None of the attribute values wrapped: Simple inline var {foo} is not replace and rendered as {foo}

Only x-data wrapped, or x-data and the x-on:keyup.tab wrap: Same result

x-data and both of the attributes above wrapped: All works fine!

x-data wrapped and both attributes fully removed: Broken again

x-data wrapped and the x-class:bind wrapped, the attributes without wrapping: Also works fine

So although the issue does not occur when all json is wrapped it still feels like the bug appears quite randomly depending on the structure / formatting of the template... also seen it in other templates where just changing indent prevented the issue from occur... So maybe this actually deserves some love...

terminal8-af commented 7 months ago

Another issue I've noticed is when adding a comment right after the opening brackets of a function such as here:

{f:security.nonce()}
<script>
    let map;
    function initMap() {
        // Map
        map = new Map(document.getElementById("map"), {
            zoom: 14,
        });
    }
    initMap();
</script>

This causes {f:security.nonce()} not to be parsed. However, upon removing the //Map comment the viewhelper is correctly parsed.

s2b commented 7 months ago

@AlexanderT8 In this case, you can probably wrap your inline script in a CDATA section.

I would also say that you should extract longer scripts to a separate file and create some kind of API to provide data from the DOM (like JSON in a data attribute).

radmiraal commented 7 months ago

@s2b I feel like there's loads of arguments against facing this is an actual bug... Of course this can be done in an API and longer scripts can be separated, but the parsing of Fluid simply breaks... Just focus on that fact...

I myself worked around the issue with workarounds like you suggest. But that doesn't mean the parsing ain't broken... I always loved Fluid and have an actual background in contribution but I decided to drop it because it simply doesn't work in this usecase at all. Unfortunately there're better alternatives that fit the usecase with for example alpinejs better. Which probably ain't a bad thing as we've to pick the right tool for the job of course. But still I would've loved to stick to Fluid...

s2b commented 7 months ago

@radmiraal Don't get me wrong, I acknowledge that this is a problem. I also know about the alternatives to Fluid and how they handle this. I just wanted to show the workarounds once again to "solve" the problem for the example given.

Currently, curly braces are an integral part of the Fluid syntax, and I fear that it won't be easy to modify/extend this without it being a breaking change. Currently, we're working on improving the ViewHelpers and improving the test coverage to be able to make bigger changes in the future. I hope that this issue will be such a change.

simonschaufi commented 1 week ago

Actually this is also currently in the TYPO3 Core with the SystemEmail and inline CSS. I just wasted a lot of time simply because of this bug and even the core does it wrong! :rage: See https://github.com/TYPO3/typo3/blob/f947cbc75fb899b6179ad89cc25044c0d4746ebe/typo3/sysext/core/Resources/Private/Layouts/SystemEmail.html#L16-L323 I added a custom variable and was wondering why it was NOT printed even though the backgroundColor from typo3.systemConfiguration.backend.loginHighlightColor was working.