Skip to content
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

New operator proposal: Conditional block that wont loop when given a list #14

Closed
nickretallack opened this issue Jun 9, 2011 · 13 comments

Comments

@nickretallack
Copy link

This is a common idiom you'll see in the form-handling code in every framework out there:

{% if form.errors %}
<div class="errors">
  <h1>Your form has errors in it!</h1>
  <ul>{% for error in form.errors %}
    <li>{{error}}</li>
  {% endfor %}</ul>
</div>
{% endif %}

Now, how do you implement this in mustache? You can't just make "if form.errors" a section or it would trigger a loop. You would have to pass in an additional flag to indicate that the list is empty. In fact, you'd have to do this any time you wanted to omit rendering the scaffolding around an empty list. And that's no fun.

This happens because mustache uses the same syntax for loops and conditionals. It'd be really nice to have two different operators here, so we could consider a non-empty list to be a normal truthy value without looping on it.

I propose we implement a ? operator that acts just like # except that it does not loop when given a list. This is your conditional operator that only cares about truthiness.

@defunkt
Copy link
Member

defunkt commented Jun 9, 2011

This has come up before, and I kinda like it.

I'd rather see this syntax though:

{{?things}}<h1>Things</h1><ul>{{#things}}<li>{{name}}</li>{{/things}}</ul>{{/things}}

Mostly because {{#things?}} is already valid Mustache syntax:

Mustache.render(template, 'things?' => true)

@nickretallack
Copy link
Author

Oh whoops, my example template there is wrong. That is indeed what I meant to do. Let me fix that...

Wow, my test wasn't being run because it was indented with tabs instead of spaces and ended up being a nested function. Guess I have some debugging to do.

@nickretallack
Copy link
Author

Hm. I guess I can't implement it in pystache because of another problem. Pystache doesn't allow you to nest a section within another section of the same name. Unfortunately it uses a single regular expression to match a section, and um, regular expressions are bad at representing matched pairs of things like parenthesis. Perhaps you should make this edge case one of your tests?

@pvande
Copy link
Contributor

pvande commented Jun 10, 2011

I've always had a bit of a soft spot for the united nature of sections as both boolean conditional and iteration. That for me has always been the root of an internal consistency in the language.

That elegance, however, is not particularly friendly in some real world cases: conditionally showing a decorated list of things foremost among them. With the advent of dotted names, many Mustache implementations can work around the lack of syntax:

Mustache.render('{{^list.empty?}}<ul>{{#list}}<li>{{.}}</li>{{/list}}</ul>{{/list.empty?}}', data)
Mustache.render('{{#list.length}}<ul>{{#list}}<li>{{.}}</li>{{/list}}</ul>{{/list.length}}', data)

These workarounds aren't portable, though -- and shouldn't be, if the intent is to force this logic into the View, away from the Template. Practically speaking, however, this is a fairly common pattern. My only concern is dividing the Section Tag semantics if any more elegant option exists.

@defunkt
Copy link
Member

defunkt commented Jun 10, 2011

@pvande That's true. It's also pretty easy to add this into a View:

def is_thing(self): 
    return len(self['thing']) > 0

>>> is_thing({'thing': [2,3,4]})
True
>>> is_thing({'thing': []})
False    

@adamjernst
Copy link

I need this too...

@mahonnaise
Copy link

The demo is actually a good example why something like this is often needed.

You want an unordered list (ul) with several list items (li) inside, but you don't want that ul container if there aren't any items.

FWIW, I'd also go with the leading '?' syntax. I actually assumed that this was the way to do it. (Dust.js does it that way.)

I know that I can probably monkey patch this kind of functionality with a lambda thingy (I guess), but I'll need to upgrade PHP to 5.3+ first. As you can probably tell, this is quite problematic since this kind of thing won't be an option for everyone.

@kdonald
Copy link

kdonald commented Mar 4, 2012

Would definitely use this.

@bobthecow
Copy link
Member

@mahonnaise You can use lambdas in Mustache.php with 5.2... If a section is 'callable' it will be treated a a higher order section even if it isn't an anonymous function.

For example, if you had a method isFooEmpty on your view class, and a public property

$this->fooEmpty = array($this, 'isFooEmpty');

it would be treated as a lambda. This also works if you're using an array as a rendering context:

$data = array('fooEmpty' => array($foo, 'isEmpty');

(Sorry that's not really documented anywhere. That's my next project when I get a few hours of unclaimed time).

@xaviervia
Copy link

This feature makes sense, since the conditional negative operator won't loop. The demos pointed out in this thread led me to a successful workaround, but still I find this feature useful (you don't always get the chance to add a first: true property to the first object).

@psigen
Copy link

psigen commented Feb 16, 2016

While there are viable workarounds in specific languages, given that mustache is being ported to about 40 languages, it seems a bit odd to force each to one come up with a language-specific workaround for the missing logical inverse to {{^foo}} {{/foo}}.

Is there still momentum to standardize the {{?foo}} {{/foo}} operator?

Here is another discussion thread that converged to the exact same solution:
mustache/mustache#47

@Samuel0147
Copy link

Cool

@jgonggrijp
Copy link
Member

This discussion continues in #157.

@mustache mustache locked and limited conversation to collaborators Nov 10, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests