New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Macro tag #580
Comments
This could be combination of the |
@carsonreinke Sorry, but just for my benefit, are you suggesting a way that I could effectively have a macro in Liquid as-is or a way that Liquid could be modified? |
Liquid out of the box, no. Customizations to the |
FWIW, I've implemented this in a soon-to-be-released C# port of Liquid: {% macro mymacro arg1 arg2 %}
You said '{{ arg1 }}'. Also '{{arg2}}'.
{% endmacro %} then {% assign myvar = "there" %}
{% mymacro "hello" myvar %}
My implementation is a simplified version of "include", but without 'for' or 'with', and with positional rather than named arguments. |
@mikebridge Looks great to me! Any status on getting this included in a release of the main version? |
Is there interest from maintainers in adding this in if someone were to work on it? IMO, the macro tag is important to add. My use case is for Shopify themes. Maintaining logic in themes is extremely hard. In many cases I can't use Javascript because of SEO concerns. This usually leads to 100s of lines of imperative mess. Being able to "componentize" my UI would result major reduction in duplicated code. Creating variations of templates using snippets is painful because I can't pass children elements to |
@Thibaut can you comment on that? |
I don't think Liquid should support global macros (shared between templates), because it would significantly increase complexity (allowing anyone to basically make their own language on top of Liquid, thereby increasing the learning curve for anyone editing templates they didn't create), and add more dependencies between templates (load order). Would local macros be of any use? I'm not convinced they'd be a good tradeoff against complexity and performance.
@sdn90 Not sure what you mean by that. Snippets inherit their parent template's variables. Doesn't that solve your problem? |
You can essentially do the same thing with named parameters now:
@emichael can you create a gist of your 400 character lines example and what the alternative would look like with a macro? |
@cshold I think that it wouldn't look all that different? There might be limitations to The biggest difference is that with In Liquid, you should be able to define and use a subroutine in the same file, too. It lets template designers keep closely coupled bits of template all in the same place and doesn't force large projects to have huge numbers of snippet files lying around. |
Just chiming in to say I too think this would be very useful. |
@Thibaut http://jinja.pocoo.org/docs/dev/templates/#call Let's say I want to create a Bootstrap component library. Using Bootstrap Panel Panel Macro {% macro panel(color='default') %}
<div class="panel panel-{{ color }}">
<div class="panel-body">
{{ caller() }}
</div>
</div>
{% endmacro %} Usage {% call panel(color='success') %}
<h1>Successfully added to cart</h1>
<a href="/cart" class="btn btn-success">View cart</a>
{% endcall %} Expected output <div class="panel panel-success">
<div class="panel-body">
<h1>Successfully added to cart</h1>
<a href="/cart" class="btn btn-success">View cart</a>
</div>
</div> |
@sdn90 This isn't something we're interested in supporting in Shopify, because it would significantly increase the complexity of templates. When reading a template you'd see something completely different from the output. If you wanted to make a change to a theme you didn't create, you'd have to search for macros, figure out what they do, etc. Every theme would have a learning curve, and what you'd learn in one theme wouldn't apply to other themes. You also couldn't easily copy/paste code from one theme to another anymore. Global macros would also introduce dependencies between templates that would be hard to resolve, as well as potential conflicts. |
@Thibaut Sorry, but that's complete nonsense. First of all, providing named functions (a feature of every modern programming language I can think of) doesn't mean the user has to use them. Secondly, you already have named functions. They just have the strange property that they have to go in a file of the same name. It is already the case in Liquid that the output of a template doesn't correspond 1-1 with the template itself (you also have variable assignment and usage). Thirdly, complexity isn't a function of the programming language being used; it's a function of the problem being solved. And as it stands, when users of Liquid need to manage some inherent complexity in certain problems, problems that require nested function calls etc., the only thing they can do is create a slurry of tiny snippet files instead of packaging the related functionality into one file (a much better way to manage complexity). Also, about performance..... You seem to have a certain idea about how this would be implemented, and I think it's a bit of a strawman. There are lots of ways to manage namespacing, conflicts, etc. But even something basic like the C preprocessor's include would be better than what you have. |
@emichael Liquid is not a modern programming language, nor does it want to be. Liquid is designed to be approachable to (and usable by) a wide range of users, from designers to front-end developers to even non-technical people. Templates need to be easy to understand. Repeated code is perfectly ok in that context. Adding global macros would be akin to encouraging every developer to make their templates more complex. We do not want this in Shopify, sorry. Keeping the majority of templates as simple as possible in the eyes of the many is more important to us than making the few templates that are doing complex things a bit simpler in the eyes of a technical audience. To be clear, I'm not arguing that macros are not a good solution to manage complexity — only that they would significantly increase the average complexity of templates, and that is what matters to us. |
If you're really interested in macros, why not simply extend your instance of liquid? https://github.com/Shopify/liquid/wiki/liquid-for-programmers#create-your-own-tags |
@dyspop Liquid <- Jekyll <- Github Pages |
if you can stand to store your macros in the same file you can do:
I didn't run this code, but I use this technique (as a concept) extensively, with a lot of enhancements that make it more manageable than calling like this, but this is the core concept. |
@dyspop That's not what I'm suggesting. You can only do simple string replacement with that technique. You can't feed it more complex data structures, and you can't do any sort of control flow based on the "inputs." |
not directly, no... fair enough. |
@Thibaut I can see you are not creating complex templates and you never run in the issue of really missing macros, not having macros is a pain when you have a complex layout, you end up having several snippets with never ending if and capture or assign. The templates becomes unreadable. Cut and paste is not your friend. |
In my experience, the lack of macros in fact creates far more complexity in the readability of templates, as theme authors frequently have to resort to hacky, complex, and incomprehensible workarounds to get the job done where a short, simple macro could have solved the problem perfectly. I don't buy this argument that readability would suffer. I think the opposite would be the case. There are some truly horrible hacks (out of necessity) to be found in some third party themes that probably even the original author can barely follow. That hurts all of us who have to understand that code to make customizations for clients. |
This is... a bit of a journey. First of all, it's caused me to switch to njk templates instead of liquid. In large part, this is because liquid doesn't seem to want to have recursion features, and that makes a nonstarter for handling things like a menu that can have variable depth. (See Shopify/liquid#580 ; from this, I gather that it's not just that liquid doesn't have a feature for this, it's that they don't *want* one. I understand the desire to avoid recursion (and thus turing-completeness), but it's unhelpful here.) In smaller part, it's just going with the flow of eleventy docs, which seem to prefer njk. Ultimately, the strength of my preference in *any* direction would be weak, but liquid crossed a hard-disqualifying line, so, let's try njk now. Okay. Moving on. All that was just to describe why one file is renamed. About this nav plugin: - It works! - I like having fairly explicit control over what ends up in nav, and how its labeled. (I expect we'll have quite a few pages, so, being able to be explicit about what's listed and how will be import to be able to control the signal-noise of the display.) - Ordering in the list is controlled by numbers in the per-page frontmatter. I'm not really a fan of this at all. I'd rather nudge around an overall list. Oh well; this isn't critical enough to spend a lot of time on. - I'm super unthrilled by having to repeat the parent key a lot. I think this navigation plugin is trying to support the ability to have navigational hierarchy that's totally divorced from directory structure... but _I don't really want that_, and it makes basic use more redundant. - I feel like I might be able to improve the above by computed properties based on the page URL. But I haven't discerned an incantation for that yet. - One can also use the `foo/foo.11tydata.json` file to set the nav parent property for the whole directory... and maybe I'll take that angle... but I'd still find this relatively high friction and a bit disappointing. - I'm a tad worried this nav thing is gonna get me in trouble when it collides with the intention to sometimes have the same page names in parallel in the docs and the specs directory trees (e.g. for those things that need both a formal spec as well as a human-friendly explainer doc). The likely result will be I'll stop using human-readable keys for the nav config, and use separate title properties...? Mmfh. - I think I'm going to be using CSS on the nav menu *heavily* to control the legibility and noise level of things. Probably including setting all elements beyond a certain depth to display-none. (And then adding back other css selectors that can toggle them back into visibility.) Future work. So it feels like I'm going to be reinventing a good bit of kludge to make this navigation plugin work in the simple way that I want. But maybe that's okay. Let's see how it goes. Worst case scenario? I might make a totally static nav menu, by hand (with just a little template power sprinkled on for the you-are-here highlighting based on the current page url). I'd like to have data that can be used to generate group listings, too (for example, as you can see being formed for the "known codecs" page), but maybe that can be a separate problem anyway (using "categories" instead of a nav tree for that might be appropriate)). Anyway. Let's see how it goes.
Jinja2 has the incredibly useful macro tag which allows quick and efficient parameterized code reuse. I've looked and asked around on Stack Overflow, and it doesn't look like there is anything equivalent in Liquid. There really needs to be.
As it stands, I'm having to write horrendous 400 character lines with no whitespace and lots of repeated elements (just with different variables).
The text was updated successfully, but these errors were encountered: