plush
plush copied to clipboard
string concatenation in for loop
Writing menu renderer with recursive calls. The problem is that I cannot append a string to menuString inside a for loop. Or I don't know some catch :\
package main
import (
"fmt"
"github.com/gobuffalo/plush"
"log"
)
func main() {
html := `<html>
<%
let renderMenu = fn(menuItems, inner) {
let menuString = "<ul class='nav nav-pills nav-sidebar flex-column' data-widget='treeview' role='menu' data-accordion='false'>"
if (inner) {
menuString = "<ul class='nav nav-treeview'>"
}
for (item) in menuItems {
if (len(item["items"]) > 0) {
menuString = menuString + "<li class='nav-item has-treeview menu-open'><a href='#' class='nav-link'><p>" + item["name"] + "<i class='right fas fa-angle-left right'></i></p></a>" + renderMenu(item["items"], true) + "</li>"
} else {
menuString = menuString + "<li class='nav-item'><a href='" + item["link"] + "' class='nav-link'><p>" + item["name"] + "</p></a></li>"
}
}
return raw(menuString + "</ul>")
}
let renderMenuOuter = fn(menuItems) {
return renderMenu(menuItems, false)
}
%>
<%= renderMenuOuter(menuItems) %>
</html>`
ctx := plush.NewContext()
menuItems := []interface{} {
map[string]interface{} {
"name": "o1",
"link": "#",
"items": []map[string]interface{} {
{
"name": "i1",
"link": "/i1",
},
{
"name": "i2",
"link": "/i2",
},
},
},
map[string]interface{} {
"name": "o2",
"link": "/o2",
},
map[string]interface{} {
"name": "o3",
"link": "/o3",
},
}
ctx.Set("menuItems", menuItems)
s, err := plush.Render(html, ctx)
if err != nil {
log.Fatal(err)
}
fmt.Print(s)
}
Current render result is:
<html>
<ul class='nav nav-pills nav-sidebar flex-column' data-widget='treeview' role='menu' data-accordion='false'></ul>
</html>a
Expected: to have some <li> elements
@paganotoni ,
r := require.New(t)
input := `<%let b = "1" %> <% for (i,v) in ["a", "b", "c"] {b = b + 1} %><%= b %>`
s, err := Render(input, NewContext())
r.NoError(err)
r.Equal("1abc", s)
I took a look at this issue and it seems the culprit is this line of code. The global context which b gets copied to a For scope context. So during the for loop, b gets appened with v correctly, however, on function return, the old context gets copied back which has the old value of b. So it overwrites it.
Possible solutions: A) Easiest -> remove that line of code. All the tests ran fine, but I don't know if buffalo will be affected by it or not. B) Keep track of what the global values are and copy them back to the global context.
Another solution will be introducing a new keyword global for identifiers. I think that will be the cleanest way. No reason to keep track of the identifiers if they're not needed in the local scope.
@paganotoni , please let me know and I can send a pull request.
so instead of let x = "Hello world" it will be global x = "Hello world" or global x = func() {}
@paganotoni @sio4,
Any updates on this? I think a global identifier will be the best route to go. Right now, this code doesn't work:
let isBackOrder = false
for (variant) in product.ProductVariants {
if (variant.VariantAllowBackorder) {
isBackorder = true
break
}
}
We re-wrote the above code to this:
let isBackOrder = for (variant) in product.ProductVariants {
if (variant.VariantAllowBackorder) {
return true
}
}
It doesn't feel natural, and I'm sure there will be times that we will need to modify the identifier in other parts of the code. So I guess a global identifier will help solve this, and changes will be backward compatible
Hi @Mido-sys, I didn't aware of this issue, I will take a look at this soon.
Currently, I am not sure if I understood the issue. Will try, and will ask you for more information or a sample if it is needed.
@sio4,
Here's a quick sample:
<%
let isBackOrder = false
for (i,v) in [1] {
if (v == 1) {
isBackOrder = true
}
}
%>
<%= isBackOrder %>
isBackOrder should return true, but it doesn't due to this
I could have a PR ready for a new keyword global soon if we're ok adding to plush.
Oh, I see. Sorry for the late response. I just tried to understand what is the @random-char's issue and the followings are my explanation:
- Plush is a templating engine rather than an inline programming environment.
- I feel like what OP tried to do is somewhat more than a templating. I think writing a recursive function within the template is not a templating.
- However, I agree that sometimes we need a more programmatic way to handle the template.
So the possible solution is that:
- Keep the template as simple as possible.
- If there is a recursive thing, consider using partial.
- If you still need more programmatic handling, consider writing a helper rather than trying to make the template complex.
So the OP's template could be changed as something similar to:
<html>
<%=
if (inner) { %><ul class='nav nav-treeview'><% } else { %><ul class='nav'><% } %><%=
for (item) in menuItems { %><%=
if (len(item["items"]) > 0) { %>
<li class='nav-item has-treeview menu-open'><a href='#' class='nav-link'><p><%= item["name"] %><i class='right fas fa-angle-left right'></i></p></a>
INCLUDE_AS_PARTIAL
</li><%
} %>
<li class='nav-item'><a href='<%= item["link"] %>' class='nav-link'><p><%= item["name"] %></p></a></li><%
} %>
</ul>
</html>
I just stop implementing the full example here, but I think it could indicate the direction of templating with plush.
It will print out the following:
<html>
<ul class='nav'>
<li class='nav-item has-treeview menu-open'><a href='#' class='nav-link'><p>o1<i class='right fas fa-angle-left right'></i></p></a>
INCLUDE_AS_PARTIAL
</li>
<li class='nav-item'><a href='#' class='nav-link'><p>o1</p></a></li>
<li class='nav-item'><a href='/o2' class='nav-link'><p>o2</p></a></li>
<li class='nav-item'><a href='/o3' class='nav-link'><p>o3</p></a></li>
</ul>
</html>
This is the way of templating with plush.
For the @Mido-sys's example, I feel like that kind of thing could be easily implemented as a helper function. If you need a helper function rather than a partial, please refer to the following section: https://github.com/gobuffalo/plush#helpers
In fact, I don't work with plush recently, and my memory and ideas could be rusty. Please feel free to let me know if there was some change or if there is something I missed.
@sio4 , Partials will be an overkill for a simple task to create a flag in a loop. Our FED developers don't code in Go and they will require a backend developer to add a partial every time they want to use a flag in a loop.
I think creating a new global keyword flag will be the best option.
@sio4 , no worries, I enjoy working on Plush and as I mentioned I could have a PR for a global keyword ready. We faced the above issue many times and creating partials just takes a long time.
Actually, I don't have enough context but IMO, your last few example such as isBackOrdet like things could be implemented as helper function which is easily used on the plush templates.
Could you please give me more context if that is not an option? I also will take a look at the code you mentioned earlier but it could not be a high priority.
I am about to close it since it has no activity. please feel free to reopen it.