Vapor Leaf use sub context

Hi all, it seems with the #extend tag in Leaf the context is automatically passed from the renderer. However if you have a more complicated (or nested) context, it doesn't seem to know how to separate keys. For example if you have the following:

parent.leaf

<html>
    <head>
        <title>#(title)</title>
    </head>
    <body>#extend("button")</body>
</html>

button.leaf

<button>#(title)</button>

ParentContext.swift

struct ParentContext: Encodable {
    let title: String
    let infoButton: Button
}

ButtonContext.swift

struct Button: Encodable {
    let title: String
}

And you render this with

let button = ButtonContext(title: "Button title")
let context = ParentContext(title: "Parent title", infoButton: button)
request.view.render("parent", context)

Both the parent.leaf and the button.leaf will try and use the 'title' property on ParentContext. Resulting both #(title) to display "Parent title". Is there a way to specify sub contexts for #extend? For example something like:

<html>
    <head>
        <title>#(title)</title>
    </head>
    <body>#extend("button", "infoButton")</body>
</html>

Where the 'infoButton' is the key of the subcontext?

Note: New to HTML templating so not sure if this is in general a bad practice, or if there is a better approach to solve this

Leaf's design means that the context is global across all templates and sub-templates. This is a deliberate decision to make it much simpler to implement and to avoid confusing people.

The easiest way around is just to change the name to something like infoButtonTitle

I wanted to see if something like "with" exists, where you could pass down a sub context. So instead:

#(form.name)
#(form.email)

You can say

#with(form):
  #(name)
  #(email)
#endwith

You could also use this to pass down a sub context to an exported template. That way templates don't need a "consistent" context.

I honestly just started playing around with leaf not 100% sure of the pitfalls with the approach above.

edit: I just tried it and it's already working in the latest version of Vapor! I am not sure why it's not documented. This is so weird!

1 Like

Looks like you can also pass it through the #extend tag now (fix here). Allowing:

struct ParentContext: Encodable {
    let button1: Button
    let button2: Button
    let button3: Button
}

#extend("button", button1)
#extend("button", button2)
#extend("button", button3)
1 Like

Thank you. This was the my question behind the question. It makes processing contexts much more modular and leaf templates reusable.