Alpine.js and Blade/ACF-composer

Is it possible and/or good practice to use Alpine.js in Blade templates for ACF Blocks (made with ACF Composer)?

I used Alpine.js in normal Blade templates before, but using it Blade templates for ACF blocks is giving me a lot of troubles so far

Here is one example:

This works:
<div x-screen="topmargin = Math.floor((( $width < 1400 ? $width : 1400 ) / Math.sin(85.8 * Math.PI/180)) * (Math.sin(4.2 * Math.PI/180)))">

But if I use $width > instead of $width < the parser chokes on it. Also got errors when using $watch

In the editor the block doesn’t work at all when I add Alpine code to the template, even though I import Alpine in editor.js the same way as I do in app.js.

If people are interested in the exact errors let me know.

I typically filter out Alpine code from running in the editor itself and keep it frontend only.

I do this either by wrapping Alpine logic in @if (! $block->preview) or by using a preview view for more complicated blocks.

2 Likes

Thanks. conditionals and the separate preview are useful yes.

Do you filter out Alpine because you found it problematic too? I was kind of hoping to mirror the front-end in the editor, but maybe I want too much;)

Also wondering why I get those parsing errors on the front-end with the example I gave

Yeah, most of my Alpine functionality doesn’t need to operate in the editor. Things like sorting posts, opening menus or accordions, etc. I don’t really want the editor to be interactive beyond the editing portion.

In general, those are the requirements I work with… but that could change depending on the specific use case.

My guess as to the syntax breaking the editor would be that it’s attempting to parse the view as jsx?. Is the supports['jsx'] option for that particular block set to false? Totally spitballing but might be worth a look.

1 Like

Yep, disabling JSX works wonders for Alpine.js support in the editor.

I’ve also ran into trouble with shorthand attributes, so @click="..." wouldn’t work, but x-on:click="..." did.

1 Like

Thanks, hadn’t thought of that

For my case it doesn’t make a difference though. Didn’t try the editor yet, but I still get this error on the front-end when using > instead of <
Uncaught SyntaxError: illegal character U+2018.

Note it’s all fine in a regular blade template.

You both use acf-composer right? Trying to narrow down what the problem is

Not sure what is happening, I’ve tried your code (without x-screen, I don’t use that Alpine plugin) and it just works. So maybe the problem is with the plugin.

Maybe you can abstract the code in the x-screen attribute to a function call?

Also I just Googled the unicode character and it’s this: “‘” U+2018 Left Single Quotation Mark Unicode Character So maybe you just have a wrong opening quote or something?

Thanks for helping.
It is weird.
I removed that plugin, then created a standard block with acf-composer, disabled jsx support and added this to the blade template:

<div class="{{ $block->classes }}" 
  x-data="{topmargin: 0}" x-init="topmargin=(1 < 2 ? 1 : 2)"
  >

When I replace < with > I get the error again, but this time the Right Double Quotation Mark:

Uncaught SyntaxError: illegal character U+201D

I just dropped your snippet into a block with JSX disabled and didn’t receive a parse error. (And yep, using acf-composer.)

Enabling JSX does throw an error and the block doesn’t render at all.

Sorry! Wish I could replicate.

1 Like

Thanks anyway. It is good to know that it should work.

I guess it may have to do with Acorn and/or the fact that I have Acorn installed as a plugin (v2.1.2)

Edit:

Just to be 100% clear: in the snippet < should be replaced with > to produce the error. Also the above block works for me in the editor (when disabling jsx, so thanks for that), the error occurs on the front-end.

I went through this a year ago, it has to do with ACF’s parsing of the view when using JSX, nothing to do with Acorn. To fix you need to wrap anything with “illegal” characters with esc_attr when enabling JSX. Or move the JS to a file and just call the function from x-data, x-init, etc.

<div class="{{ $block->classes }}" 
  x-data="{topmargin: 0}" x-init="<?= esc_attr('topmargin=(1 < 2 ? 1 : 2)') ?>"
>
3 Likes

Thanks! Is still don’t know why I get these errors in my basic example with jsx disabled (and joshf not). But I can confirm wrapping with esc_atrr does work (also with my non-jsx example).

That is also good to know.

logix has fixed it by

adding
alpine-support.blade.php

<script type="text/javascript">
    acf.addFilter('acf_blocks_parse_node_attr', (current, node) => node.name.startsWith('x-') ? node : current);
</script>
   add_filter('acf/input/admin_footer', function () {
            echo view('fastlane::components.alpine-support')->render();
        });

refs:

https://github.com/Log1x/acf-composer/blob/e17b51a6afe4f2a9273de4b568160e55248836d1/src/AcfComposer.php#L104-L106

from acf js


  /**
   * Converts the given attribute into a React friendly name and value object.
   *
   * @date	19/05/2020
   * @since	5.9.0
   *
   * @param	obj nodeAttr The node attribute.
   * @return	obj
   */
  function parseNodeAttr(nodeAttr) {
    let name = nodeAttr.name;
    let value = nodeAttr.value;

    // Allow overrides for third party libraries who might use specific attributes.
    let shortcut = acf.applyFilters('acf_blocks_parse_node_attr', false, nodeAttr);
    if (shortcut) return shortcut;
    switch (name) {
      // Class.
      case 'class':
        name = 'className';
        break;

      // Style.
      case 'style':
        const css = {};
        value.split(';').forEach(s => {
          const pos = s.indexOf(':');
          if (pos > 0) {
            let ruleName = s.substr(0, pos).trim();
            const ruleValue = s.substr(pos + 1).trim();

            // Rename core properties, but not CSS variables.
            if (ruleName.charAt(0) !== '-') {
              ruleName = acf.strCamelCase(ruleName);
            }
            css[ruleName] = ruleValue;
          }
        });
        value = css;
        break;

      // Default.
      default:
        // No formatting needed for "data-x" attributes.
        if (name.indexOf('data-') === 0) {
          break;
        }

        // Replace names for JSX counterparts.
        name = getJSXName(name);

        // Convert JSON values.
        const c1 = value.charAt(0);
        if (c1 === '[' || c1 === '{') {
          value = JSON.parse(value);
        }

        // Convert bool values.
        if (value === 'true' || value === 'false') {
          value = value === 'true';
        }
        break;
    }
    return {
      name,
      value
    };
  }