-
Notifications
You must be signed in to change notification settings - Fork 71
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
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
{{#item.0}} checks and context #138
Comments
in JS, a better check for "array isn't empty" is probably |
Slight nuance: not every programming language might indicate the first element of an array as a Lines 9 to 26 in 5d3b58e
Anyway, it is indeed the case that A third alternative is to reorganize your template a bit. The following version will even properly nest the list items within a
Side note: mustache.js, which powers the well-known demo page, unfortunately does not adhere to the spec very well. You can tell so in this case because it strips all indentation. If I take your original template and input data through an implementation that does adhere to the spec, I get the following output:
Coincidentally, I am working on a new implementation for JavaScript that does adhere to the spec, and also on a playground site where you can try templates in a spec-compliant way. If you're interested, have a look at Wontache and perhaps consider donating to my Patreon. |
Right. I wasn't trying to say that dotted names were a language-specific feature, rather grabbing the 0th element of an array at all was language-specific. Note that |
Thanks everyone. We can preprocess the data too to add a The wording of that section spec also suggests to me the perl implementation is not handling dotted entries correctly. |
@DeeHants What did you find? |
It would be really nice if there was a spec'ed way to deal with this like 99% use case of dealing with lists. There are three exceptionally common use cases where the only option is to re-decorate the list:
So many implementations have varying extensions for dealing with the above. Ideally you would have a lambda do it but as I mentioned here: #135 (comment) It is not easily possibly even with enhanced lambdas as ambiguity comes when referencing a list. For example if I'm passed the context stack can I get access to the list (e.g. It is just sad that this is such an incredibly common use case without a clear option that doesn't vary greatly from implementation to implementation other than mindlessly redecorating the model (which in some cases is not easily possibly particularly with immutable objects etc). Maybe power lambdas can get direct access to lists. Then you just have to port a lambda to other implementations. |
@agentgt Yes, I think power lambdas could and should solve this issue. The way I currently think of it, lambdas receive a second argument which somehow (i.e., in an implementation-defined way) makes the following things possible:
Whereby lambdas must not modify the pre-existing contents of the context, and implementations are welcome to actively prevent this if the programming language can enforce it. However, lambdas can (already) push a new frame on the stack, which still has the net effect of changing what's available in the context. Given such a hypothetical second argument (named Data with power lambdas {
// Pushes a new frame on the stack that shadows all keys currently visible.
// Each key on the new frame is a lambda that lazily checks whether the
// corresponding key on the lower frames is an empty list.
empty: function(section, magic) {
var allKeys = magic.somehowGetAllKeys();
var decoratedFrame = {};
function createChecker(key) {
// Crucial: the following function closes over the `magic` that was passed
// to the `empty` lambda, so it resolves against lower frames only.
return function() {
return magic.somehowResolve(key).length === 0;
};
}
for (var l = allKeys.length, i = 0; i < l; ++i) {
var key = allKeys[i];
decoratedFrame[key] = createChecker(key);
}
return decoratedFrame;
},
// Pushes a shadowing frame, similar to `empty`. However, each key pushes
// a new list on the stack that shadows the original list. Each item in the
// new list has the same contents as the corresponding element of the
// underlying list, but an `index` property is added with its numerical
// position in the list.
enumerate: function(section, magic) {
var allKeys = magic.somehowGetAllKeys();
var decoratedFrame = {};
function createEnumerated(key) {
return function() {
var list = magic.somehowResolve(key);
if (!(list instanceof Array)) return list;
var decoratedList = [];
for (var l = list.length, i = 0; i < l; ++i) {
decoratedList.push({...list[i], index: i});
}
return decoratedList;
};
}
for (var l = allKeys.length, i = 0; i < l; ++i) {
var key = allKeys[i];
decoratedFrame[key] = createEnumerated(key);
}
return decoratedFrame;
},
// Check whether we are currently rendering the first element of a list.
// Only works inside an `{{#enumerate}}{{/enumerate}}`.
first: function(section, magic) {
var index = magic.somehowResolve('index');
return index === 0;
},
// Check whether we are currently rendering the last element of a list.
// Only works inside an `{{#enumerate}}{{/enumerate}}`.
last: function(section, magic) {
var index = magic.somehowResolve('index');
var frameIndex = magic.somehowGetFrameIndexOf('index');
// Next line assumes that lower frames have lower indices.
var list = magic.somehowGetFrame(frameIndex - 1);
return index === list.length - 1;
}
} Template with example usage {{! rendering something only if a list is not empty, but only once, regardless
of list length }}
{{^empty.myList}}
<ul>
{{/empty.myList}}
{{#myList}}
<li>{{item}}
{{/myList}}
{{^empty.myList}}
</ul>
{{/empty.myList}}
{{! rendering something between elements, but not before or after }}
I like {{#enumerate.myList
}}{{^first}}{{^last}}, {{/last}}{{#last}} and {{/last}}{{/first}}{{item}}{{/
enumerate.myList}}. |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
Not sure if this counts as a bug, and seems to be by design as per the wording of the docs, and (at least) the perl and JS implementations, but a gotcha...
We make extensive use of
{{#items.0}}
type checks to see if an array has any items before rendering the container and the items themselves ({{#items}}{{value1}}{{value2}}{{/items}}
).This gives example code like:
The issue here is that the 0th item is now part of the context stack, meaning that if items 1+ do not have a specific named value, but the 0th does, that value will be rendered instead.
If you put the above mustache into the demo, you get this broken output:
It now thinks every item is "first" as it pulls that value from the
{{#items.0}}
context.We can work around it in some cases, by changing
{{subitem.value}}
(which also exists at.0
) to{{#subitem}}{{value}}{{/subitem}}
.This works, as the
.n
entries will have asubitem
which becomes the new context, and then looks forvalue
which.0
(and.n
) doesn't.The text was updated successfully, but these errors were encountered: