Open matthewbeta opened 4 years ago
The reasoning was provided in the pull request that introduced the render tag (https://github.com/Shopify/liquid/pull/1122)
Unlike
include
,render
does not permit specifying the target template using a variable, only a string literal. For example, this means that{% render my_dynamic_template %}
is invalid syntax. This will make it possible to statically analyze the dependencies
If you are just rendering a different partial based on whether the user is logged in or out, then you could just use conditional control flow to render the appropriate nested partial.
If you want your partial to be more generic so that it isn't coupled to the nested content, then you could capture the rendering of the nested content and pass the rendered content into the generic partial. For example, that would allow you to pass in different attributes to different nested partials, as you have done with text: 'View cart'
.
What we will do if things go like this? I need to show flags according to user input, there are literally hundreds of them. Writing hundreds of if-else statements will be somewhat cumbersome.
What puzzles me about this answer is that the official documentation seems to contradict it, but I can't get it to work.
What we will do if things go like this? I need to show flags according to user input, there are literally hundreds of them. Writing hundreds of if-else statements will be somewhat cumbersome.
Do you mean you have literally hundreds of snippets that you might need to dynamically include? If so, that seems cumbersome even with the include tag.
Global objects don't need to be passed down. They are accessible from all files.
That isn't referring to assigns. Look at the global objects in the documentation that it links to. Are any of those not accessible from all files?
I just had this issue when trying to dynamically render a snippet, but instead of Shopify showing a liquid error like it usually does it just broke the whole page with no indication of the error. Can that be resolved please. I don't remember that happening in the past
.
Found this workaround. By appending a string (even empty) it transforms the variable into a string.
{%- render variable_name | append: ".liquid" -%}
Found on this thread. https://community.shopify.com/c/shopify-design/how-can-i-render-a-snippet-this-name-is-defined-by-a-variable/td-p/640212
Found this workaround. By appending a string (even empty) it transforms the variable into a string.
That is clearly relying on a bug. Intentionally relying on a bug is fragile to changes we make to liquid or Shopify's extensions to liquid, since it puts you code at risk from breaking from a bug fix.
I just had this issue when trying to dynamically render a snippet, but instead of Shopify showing a liquid error like it usually does it just broke the whole page with no indication of the error.
Looks like that was another bug in Shopify that has already been fixed. It should now render to the error: <!-- Syntax error in tag 'render' - Template name must be a quoted string -->
.
Any updated on this? The feature would be awesome!
+1, please.
+ 2 please
+1
I found a solution for this one. Just extend the Render class and create a CustomRender class. The code should be something like this:
class CustomRender < Liquid::Render
SYNTAX = /(?:#{QuotedFragment}|#{VariableSegment})(\s+(with|for)\s+(#{QuotedFragment}+))?(\s+(?:as)\s+(#{VariableSegment}+))?/o
def initialize(tag_name, markup, options)
super
# Update the syntax to support variables or string fragments
match = markup.match(SYNTAX)
template_name = match[0]
@variable_name_expr = match[3] ? parse_expression(match[3]) : nil
@template_name_expr = parse_expression(template_name)
@alias_name = match[5]
@attributes = {}
markup.scan(TagAttributes) do |key, value|
@attributes[key] = parse_expression(value)
end
end
def render_tag(context, output)
# Allow variable-based template names
template_name = context.evaluate(@template_name_expr)
raise ::ArgumentError unless template_name.is_a?(String)
# Load the partial (same as the original code)
partial = PartialCache.load(
template_name,
context: context,
parse_context: parse_context,
)
context_variable_name = @alias_name || template_name.split('/').last
render_partial_func = ->(var, forloop) {
inner_context = context.new_isolated_subcontext
inner_context.template_name = partial.name
inner_context.partial = true
inner_context['forloop'] = forloop if forloop
@attributes.each do |key, value|
inner_context[key] = context.evaluate(value)
end
inner_context[context_variable_name] = var unless var.nil?
partial.render_to_output_buffer(inner_context, output)
forloop&.send(:increment!)
}
variable = @variable_name_expr ? context.evaluate(@variable_name_expr) : nil
# Support for loops (same as the original code)
if @is_for_loop && variable.respond_to?(:each) && variable.respond_to?(:count)
forloop = Liquid::ForloopDrop.new(template_name, variable.count, nil)
variable.each { |var| render_partial_func.call(var, forloop) }
else
render_partial_func.call(variable, nil)
end
output
end
end
Then override the render tag or name it anything you want (In my case, I override my render tag):
Liquid::Template.register_tag('render', CustomRender)
You can use this method instead
Snippet
<div>
{{ content }}
</div>
In a section
<section>
{% capture my_content %}
{% render 'icon' %}
{% endcapture %}
{% render 'button', content: my_content %}
</section>
Render
<section>
<div>
<svg>
...
</svg>
</div>
</section>
Hi!
It seems pretty unreasonable (as an outsider) that I can't do this:
I can't understand why
render
should care if the string it uses is inline or from a variable, so long as its a string.This feature would be useful for rendering partials based on things that need to be dynamic (For example rendering a different partial if a user was logged in or out) or when you want to make components more generic.
For example, say I have a bunch of
svg
icons as.liquid
files. I want to render them, wrapped with some markup. Rather than edit each icon with repetitive wrapping HTML, I want to create an adapter component and pass in the icon name so I could write:And then inside
snippets/icon.liquid
Am I being dense? This feels like a step back from
include
tags.Cheers
Matt