lapis icon indicating copy to clipboard operation
lapis copied to clipboard

etlua using content_for

Open zachrburke opened this issue 9 years ago • 6 comments

Is there a way to leverage content_for in an etlua template the same way I would in a lapis Widget? For example, in a lapis widget I would do something like this to ensure my javascript loads in the same place and at the bottom of my page:

    @content_for "javascript", ->
        if @Post.Languages
            script src: '/content/js/highlight.pack.js'
            script -> 
                raw "var languages = #{util.to_json(@Post.Languages)};\n"
                raw [[
                hljs.configure({
                    languages: languages
                });
                hljs.initHighlightingOnLoad();
            ]]

Is there a way to do this in an etlua template?

zachrburke avatar Aug 23 '14 15:08 zachrburke

do you have tried a lua expression inside your template?

CriztianiX avatar Sep 03 '14 15:09 CriztianiX

I've tried a few things. I've found I can do the following at the beginning of my home etlua template:

<% function scripts() %>
    <% if Post.languages then %>
        <script src='/content/js/highlight.pack.js'></script>
        <script>
            var languages = <%- Post.languages %>;
            hljs.configure({
                languages: languages
            });
            hljs.initHighlightingOnLoad();
        </script>
    <% end %>
<% end %>

and then <% scripts() %> at the end of my template to render what is in the scripts method.

However, if I try <% content_for('javascript', scripts) %> then I get the following error:

/usr/local/share/lua/5.1/lapis/html.lua:403: attempt to call field 'capture' (a nil value)
Traceback

stack traceback:
    /usr/local/share/lua/5.1/lapis/html.lua:403: in function 'content_for'
    [string "etlua"]:137: in function 'run'
    /usr/local/share/lua/5.1/lapis/etlua.lua:153: in function 'thing'
    /usr/local/share/lua/5.1/lapis/application.lua:250: in function 'write'
    /usr/local/share/lua/5.1/lapis/application.lua:145: in function 'render'
    /usr/local/share/lua/5.1/lapis/application.lua:418: in function </usr/local/share/lua/5.1/lapis/application.lua:412>
    [C]: in function 'xpcall'
    /usr/local/share/lua/5.1/lapis/application.lua:412: in function 'dispatch'
    /usr/local/share/lua/5.1/lapis/nginx.lua:181: in function </usr/local/share/lua/5.1/lapis/nginx.lua:179>

It is possible to do <% content_for('javascript', 'Hello World!') %>. If I convert scripts to return a string with the same content, it just gets rendered as a raw string rather than html which I need.

zachrburke avatar Sep 06 '14 23:09 zachrburke

@zach-binary did you find a solution for this in the end? I'm stuck with the same problem

kraftman avatar Aug 24 '17 05:08 kraftman

I'm not sure what yall are trying to do with content_for but I've only ever used that in layouts.

What you might be looking for is render

<% render("views.my_widget") %>

That will allow you to include any etlua template inside of another. If you need it to always appear in a specific area of the page, that's what layouts are for:

<% render("views.header") %>
<% render("views.nav") %>
<% content_for("inner") %>
<% render("views.footer") %>

content_for("inner") returns whatever view the route is rendering. More Info. Hope that helps.

VaiN474 avatar Aug 24 '17 05:08 VaiN474

@kraftman unfortunately no, I don't maintain my lapis site anymore but looking back at the source code I wasn't able to find a workaround for this problem.

To clarify the problem, I wanted to be able to override parts of my layout with content inside an action. Think about elements in html that have to be in a fixed place, like the title tag. I wanted to be able to say, inside of the view, what the title should be.

@content_for supports this according to the documentation

class MyView extends Widget
  content: =>
    @content_for "title", "This is the title of my page!"

    @content_for "footer", ->
      div class: "custom_footer", "The Footer"

class MyLayout extends Widget
  content: =>
    html ->
      body ->
        div class: "title", ->
          @content_for "title"

        @content_for "inner"
        @content_for "footer"

This only works if you generate html using moonscript. I think the problem is that content_for wasn't able to handle etlua templates for the second parameter. It assumes it's using the moonscript DSL to generate html and that's what results the error above.

I'm not sure if this was addressed or not.

To further clarify, I had scripts that were loaded on my home page but nowhere else, so I only wanted to load them there, but I wanted them at the bottom of the page, which I can only do in the layout, in order to prevent scripts from blocking the rendering of the page.

zachrburke avatar Aug 24 '17 11:08 zachrburke

Thanks. I was hoping to do something similar, with with 'css' or 'js' in the layout.etlua, being replaced by the child templates, so that I'm not always loading large css files, and additional css files are in the header still, not spread randomly throughout depending on my render() blocks.

kraftman avatar Aug 25 '17 03:08 kraftman