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
Implement Helpers. #139
base: master
Are you sure you want to change the base?
Implement Helpers. #139
Conversation
See examples/helpers.js / .html for usage.
+1 (I have nothing intelligent to add) |
looks useful. Maybe it can have a differet syntax though like:
So you could eventually pipe results. |
I'd love to have this, but it seems to violate mustache's "no logic in templates" principle. Helpers are explicitly called out in the mustache readme ("Instead of views consisting of ERB or HAML with random helpers..."). Still, I think simple helpers would really help streamline some of my views' logic. EDIT: also, have you seen handlebars.js? |
I agree with Benoît -- it would be nice to have a better-defined syntax. How about something like this?
That would fit conceptually with blocks {{#varname}} and partials {{>partialname}} |
@benoitc not a bad idea at all. See my latest commit :) @pegli I don't like to introduce more magic symbols, so I went with @benoitc's suggestion. @MartinodF the idea is directly stolen from handlebars :) c.f. http://writing.jan.io/mustache-2.0.html |
+1 great job This is one use case of helpers I had in mind when looking for a i18n helper implementation: listOfAvailablePlaneTickets = [ {id : "se", quantity : 5}, {id : "fi", quantity : 1} ] Should render something like this: <ul>
<li>Sweden - 5 plane tickets left</li>
<li>Finland - 1 plane ticket left</li>
</ul> With a template such as: <ul>
<li>{{id | i18n}} - {{tickets_left | i18n}}</li>
</ul> But not having much experience with i18n (nor Mustache), I'm not sure this is a sound way of doing it? |
mustache.js
Outdated
var helper_name = name.split("|"); | ||
if(helper_name.length > 1) { // we have at least one helper | ||
name = helper_name[0]; | ||
helpers = helper_name.slice(1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be cleaner as something more like this, although I'm not sure if it would be faster or slower, which would be worthwhile to know:
var helpers = name.split("|");
name = helpers.shift();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went with your suggestion as it wins in clarity. I had to change the if() condition before iterating over helpers to account for the fact that if([]) is truthy.
I didn't benchmark this, but I assume that native functions will be faster than my original hackery. I wouldn't mind a jsperf shootout though.
You asked for a code review, so I did one :) I generally really like this addition. Great stuff! |
@bcherry awesome review, thanks :) |
-1 I'd rather not add this patch. It would be much cleaner to keep all logic in your view code, where you can document it properly and properly handle any errors that occur. |
@mjijackson a -1 is a bit strong here for a feature you don't have to use. Why block a feature that others find useful but that doesn’t interfere with your work? |
@janl Agreed, my -1 was a bit too strong. I guess the real question I have is how much do we care about the size and feature set of the code base? I'm ok with adding stuff that's not in the spec, in the hopes that it will find its way into the spec someday. However, I don't see a lot of value in adding features that unnecessarily add complexity to templates. It's been my experience when talking with others about mustache that they don't fully understand the concept of a view. They think about it as a simple hash of variables, similar to what you get in jsp-style (or erb) templates. For me, one of the killer features of mustache is the ability to reference a function in the template that lets you hide the logic in your view. Education about this feature would go a long way towards helping users understand they don't really need handlebars-style filters at all. |
@mjijackson my line of thinking went exactly along the path you outline and I came to the conclusion that helpers would indeed be a good nonstandard addition. |
I actually implemented pipe based helpers in a similar way janl did. What I had to achieve was the replacement of CRLF with What I effectively tried to achieve was "replace CRLF with I started in the "find" function like janl did but then moved the logic to the "render_tags" function as a "post processing" helper for the reason that I wanted to work with the final (default) escaped result. I now see that the 0.5.0 code has changed significantly, so I would really be very very pleased if the helper functionality would become an integral part of mustache. Maybe, as a question to both mjijackson and janl: How would you guys suggest I achieve the result I desire? Thanks and regards, Erik |
I think we can start an extras branch and implement these things there. What do you think? |
Yes, let's do that! Which version you think we should fork? (as the latest seems to have been a considerable refactor) |
yeah, base your work off master, awesome! :) |
When will this feature be merged into master , or 0.4.x branch ? I really need it. |
Hi, I'd like to split text into several parts and then render them respectively. I have no idea about writing the "close" tag. Shall I code like: |
I'm taking another look at this work (sorry it took so long!). As far as I can see you are testing with the following template:
Presumably, the code to render this template could look something like this: Mustache.render(template, {
name: 'michael'
}, {
ucase: function (string) {
return string.toUpperCase();
}
}); In the above snippet the Mustache.render(template, {
name: 'Michael',
ucaseName: function () {
return this.name.toUpperCase();
}
}); There are a few things I like about this style over the helpers style:
I think the added complexity of adding helpers outweighs the gains in this case. Of course, there could be much more complex examples where we might see some significant gains, but I've never seen any in practice. I also think that the locality of reference helps devs to make better use of their view objects and makes the code easier to read for other devs later on. |
I'm gonna side with the first example. Anecdotally, I ran into this case What was frustrating was that I wasn't able to make a generic helper. Every Not tring to be argumentative, just add a data point for you. I appreciate On Friday, August 31, 2012, Michael Jackson wrote:
Brian Duchek |
In response to @manxomfoe and @mjijackson above, couldn't something similar be accomplished using a syntax like.
And Mustache.render(template, {
name: 'Michael',
ucase: function () {
return this.toUpperCase();
}
}); Here the function is working on the context at the top of the stack. I haven't looked at the code in a while, but I presume that the lambda is not currently being called in the context (this) of the top of stack but rather in the one in which it was defined? I understand that this change would break existing method calls then. However, if that is the case, shouldn't the following work?
I'm really looking for a good solution to the singular/plural problem that @lapidus described. |
@janl I've been thinking about this some more. I like the syntax {{tag|helper|helper2}}. But I'm not sure that it's necessary for there to be a different class of helpers. These can all be plain old lambdas.
could be shorthand for
So where the following are functionally equivalent in many cases:
These would be EXACTLY equivalent.
And the code for the first example could look something like this. Mustache.render(template, {
name: 'Michael',
ucase: function () {
return this.toUpperCase();
}
}); So, first 'name' is pushed to the top of the context stack and then 'ucase' is called against the top of the context stack. Whatever it returns as a value would also be pushed to the top of the context stack for any next lambda to operate on if there was another name separated by a pipe on the tag. So these "helpers" can be chained as previously mentioned. |
I like where this is going, and think that the piped helpers notation would make sense for me. |
There's a tl;dr on what will most likely be Mustache.php's implementation of this here. Particularly relevant bits:
|
@bobthecow I've added an implementation here: #261 I've taken a slightly different approach for your numbers 4 and 5 above. 1-3 remain the same. If a named value found in the context stack is a list, that whole list becomes the context of the lambda. This allows for reducing functions.
My naive implementation does not complain if a "helper" is not found.
|
Ok, all. Not sure anyone is still following this discussion, but I thought that I'd add one last thing. After my POC fork of Mustache.js, I decided to go after Hogan.js and I took a slightly different approach with it. There's some documentation on my pull request here: twitter/hogan.js#113 And I'll likely be maintaining a separate fork here: https://github.com/techhead/hogan.js I'll try to get docs up on that ASAP. I'll probably leave my Mustache.js fork up for another week or so and then delete it. My future efforts will likely revolve around Hogan.js. |
(Cross-post from #261) There have been a debate at mustache/spec, but it looks dead now: mustache/spec#41 GRMustache, the Objective-C implementation, has chosen the parenthesis syntax, {{ f(x) }}, for "filters" : https://github.com/groue/GRMustache/blob/master/Guides/filters.md. Highlights are the possibility to build other expressions on top of filtered values, like {{ last(people).name }}, and filters that take several arguments, as in {{ localize(date, format) }}. I'd be happy that janl/mustache.js considers this option, regardless of the trend for pipes today. You can read arguments against pipes at https://github.com/groue/GRMustache/blob/master/Articles/WhyMustacheFilters.md. Since very few Mustache implementations have shipped today with piped helpers, I think it's still time to prevent this bad idea to spread. All, what do you think? |
I forgot another highlight of GRMustache's filters: their ability to behave like Handlebars' helpers. Compare this "each_with_index" handlebars gist https://gist.github.com/1048968 to this GRMustache sample code: https://github.com/groue/GRMustache/blob/master/Guides/sample_code/indexes.md |
See examples/helpers.js / .html for usage.
I'd appreciate a review :)
cc @lapidus @bcherry @bobthecow @khamidou