Comparison-conditional 'if''s in template loop


#1

Ember has a reputation of having a steep learning curve, and though that may be it, what I find online confirms my experience. That experience so far is, that it’s difficult if you want to loop through your model to dynamically build out most of your HTML and let Handlebars write and bind it. As far as I’ve discovered, I’m still stuck with cumbersome JS HTML strings.  The simplest example of this is the inability to do…

{{#if model.input.type == ‘select’}}

or, how about passing model values to a helper…

{{#myhelper model[i]}}     or    {{#myhelper model[i].type}}

*I figured this part out.  I was confused by Ember.Handlebars vs Handlebars, for the  helper method.

…or, returning bound Handlebar expressions, so my helper can return this:

{{input action='myAction}}

Without those conveniences, life is pretty difficult as I have to loop through the model and compile my HTML string.  So, I’d like to suggest the following:

  1. Allowing comparisons in template conditionals.
  2. Helpers return bound Handlebars expressions.

My original [SO post][1] on this issue.

[1]: http://stackoverflow.com/questions/23003702/how-can-i-pass-the-index-from-a-template-loop-into-my-helper-and-have-it-return


If condition inside ember template
#2

I agree that not being able to do something like this, can be annoying at times. Even though many may say you “should” keep this logic out of your views/templates.

However, you could create a {{#whenequal}} block helper, like so: http://jsfiddle.net/bradoyler/Js5Xc/

Idk, maybe a better name would be {{show-if-equal}}, but u get the idea…


#3

I did try that route, but because you can only pass in literals and not values, it didn’t help me. If the {{#blocks}} would resolve the model values, I could do…

{{#myhelper model.type}}// resolve to ‘select’

…and test the string value in the helper to build out the correct HTML. Ah, but then I run into problems returning bound expressions like so…

{{input type=model.type}}{{action ‘myAction’}}

It may be an issue of design and a separation of concerns, in which case I’ll defer to the experts, but I can’t help questioning the utility of that logic in cases like this.


Currently selected model conditional in a loop w/ link-to
#4

I’m not entirely sure what you’re aiming at but it looks like you want to create a component, something(roughly) like:

{{form-input value=model}}

App.FormInput = Ember.Component.extend({
  tagName: "input",

  type: function(){
    return value.get("type");
  }.property('value')
});

#5

Thanks, I’ll take another look the components. My first impression was that I would face the same problems there. This JsBin shows what I am going for. If the helper could return compiled and bound Handlebars expressions, I think I’d be home free.


#6

I ran into this same problem with Angular too, [as someone else posted][1]. It looks like their solution is to build a custom [Angular.widget][2], which allows building out the form in the HTML code with a custom directive. The ng-if didn’t work either. Maybe something to do with it creating it’s own scope, so this is meaningless as question is not in scope:

ng-if="question.type == 'checkbox'

it has no awareness of ‘question’, and just returns true.

So far, KnockoutJS is the only thing that made dynamic form building easy. [1]: https://groups.google.com/forum/#!topic/angular/f8KbLtT_Mqs [2]: http://jsfiddle.net/34y3b/


#7

I think you just want to do something along the lines of:

{{#each model.questions}}
  {{render model.type this}}
{{/each}}

App.Radio = Ember.Component.extend({
     tagName: "input",
     type: "radio",        
})

App.Checkbox = Ember.Component.extend({
     tagName: "input",
     type: "radio",        
})

I’m afraid I haven’t implemented the radio and checkbox components for you but you should get the general idea i.e. you define each of the different types of input that you need as a component and let the loop decide which to use based on the type property of the question.

By the way, ember doesn’t include a radio button component at the moment (there’s talk of adding one), but for now take a look at this http://jsfiddle.net/andersonmat/CM6fK/


#8

You may find Ember EasyForm helpful either to use directly or get inspiration from.

Dynamically rendering things is definitely an easy task with ember, and some of the approaches mentioned here will work great. The approach I would probably use to keep this really simple is to wrap each input in an itemController, then use Em.computed.equal macro to determine what to render:

App.InputController = Em.ObjectController.extend({
  isRadio: Em.computed.equal('type', 'radio'),
  isCheckbox: Em.computed.equal('type', 'checkbox')
  // etc
})

Then in template:

{{#each itemController="input"}}
  {{#if isRadio}}
     {{input type="radio"}}
  {{/if}}

  {{#if isCheckbox}}
    {{input type="checkbox"}}
  {{/if}}
{{/each}}

A lot of figuring out ember is figuring out how to build the data structures your template needs, and an item controller is a nice way to do this for templates without manually looping over your data and adding new properties.


#9

Thanks for the tips. I can investigate them after work.
At first glance, it’s not clear where the JSON would get iterated over. Would this approach allow you to dynamically build out a form defined by big blob of JSON?


#10

Yes, the solution I gave above assumes you are iterating over JSON. Although you may need to preprocess your JSON using a computed property in order to make it into the right structure you need.

The each helper in the example is where the json is iterated over. This assumes you have set it to be the model of this template’s controller.


#11

It might be more obvious with the model mentioned explicitly in the each loop.

{{#each model itemController="input"}}
  {{#if isRadio}}
     {{input type="radio"}}
  {{/if}}

  {{#if isCheckbox}}
    {{input type="checkbox"}}
  {{/if}}
{{/each}}

#12

Sitll struggling with this. I don’t see where it compares the question.type to determine which input type to build out. I JSbin’d it, so you can see what I’m after. It should produce 2 checkboxes, 3 radio bottons, and a select DDL. It will also need to iterate over the answers obj to set the attributes and displayed text for each input.

So far it seems the fundamental problem remains; comparing model values on the fly.


#13

I haven’t been able to spend much time testing this out, but I am seeing the same problem, which is resolving the model value in the Render statement. It just comes through as a string. You can see it here


#14

You were pretty close but the critical thing that was missing was the itemController in the each loop. When you specify the itemController each item in the loop receives it’s own new copy of the controller. It’s also important to use an ObjectController rather than the basic Controller so that the controller can receive the item as it’s model.

I’ve updated your jsbin at http://emberjs.jsbin.com/juzex/1/edit


#15

If you’re referring to the values for the select that’s because you’ve quoted them, if you want to pass a bound value or literal you just omit the quotes i.e.

    {{view Ember.RadioButton name="selectionTest" selectionBinding="isSelected" value=1}}

instead of

    {{view Ember.RadioButton name="selectionTest" selectionBinding="isSelected" value="1"}}

note you can use also use the binding naming convention (using quotes), i.e.

    {{view Ember.RadioButton name="selectionTest" selectionBinding="isSelected" valueBinding="1"}}

#16

Yes, thanks! I think this is it. It’s looking really good. It’s producing the output I want, with out too much work, and no cumbersome JS HTML strings. See the Updated JSbin with the radio buttons wired up to an action.

The ability to bring in another separate controller into the template loop provides the comparison logic I was looking for. This provides a lot of flexibility. I’m looking forward to stepping through the code and exploring this further.


#17

Once you start getting the radio, checkboxes etc. wired up you’re probably going to want to introduce a controller for each one, the next step towards that is to break out the individual views, http://emberjs.jsbin.com/suzalosu/1/edit


#18

You may see this around, but it’s slowly being deprecated in favour of quoting or not quoting as appropriate:

{{some-helper fooBinding="bar"}}
{{some-helper foo=bar}}

So those two are equivalent, they will both set foo to be the value of the bar property. However, you should prefer the latter one in new code.

Contrast with the following, which sets foo to be the string "bar"

{{some-helper foo="bar"}}

#19

I want to reuse the template and just pass in a diff model. I got this working at home. The JSbin works in Chrome, but it doesn’t seem to work in FF. I think this seals it; all my big questions have been answered. I’m now pretty enthusiastic about Ember.

If there’s anything catastrophic about doing it this way, I’m all ears.


#20

Works fine for me in firefox 27.0.1.

I don’t think there’s anything wrong with your general approach but personally(and I think this is more idiomatic ember) I’d prefer to break out the components so that you’d have something like:

{{#if isCheckbox}}
    {{checkboxes options=q.answers value=answer on-change="nextQuestion"}}
{{/if}}
<br/>

{{#if isRadio}}
    {{radio options=q.answers value=answer on-change="nextQuestion"}}
{{/if}}
<br/>

{{#if isSelect}}
    {{select options=q.answers value=answer on-change="nextQuestion"}}
{{/if}}