1
votes

I am brand new to Ember.js and have a question about cleaning up code. I am using Ember(v1.0.0-rc.1), Handlebars("1.0.0-rc.3"), and Bootstrap(v2.3.1).

Take one of my templates below:

<form class="form-horizontal">

  <div class="control-group">
    <label class="control-label" for="inputEmail">Name</label>
    <div class="controls">
      {{view Ember.TextField valueBinding="name" id="inputEmail"}}
    </div>
  </div>

  <div class="control-group">
    <label class="control-label" for="inputNumber">ID Number</label>
    <div class="controls">
      {{view Ember.TextField valueBinding="number" id="inputNumber"}}
    </div>
  </div>

  <div class="control-group">
    <label class="control-label" for="inputContactName">Contact Name</label>
    <div class="controls">
      {{view Ember.TextField valueBinding="invoiceContactName" id="inputContactName"}}
    </div>
  </div>

  <div class="control-group">
    <label class="control-label" for="inputContactEmail">Contact Email</label>
    <div class="controls">
      {{view Ember.TextField valueBinding="invoiceContactEmail" id="inputContactEmail"}}
    </div>
  </div>
</form>

Now that I have this rich view framework, all that repetition feels so dirty! The problem is the nature of the template ... I would need to pass in the object I am binding too, plus alter the values that the template will render. My first attempt was to "nest" Handlebars templates ... which was messy and I didn't get anywhere. My second attempt was to create a "pre-processor" to modify the template string before Handlebars compiles it ... this seemed cleaner, but doesn't work at all ... turns out when "template" is called on the view we are way out of context. Example below:

App.BootstrapTextField = Ember.View.extend({
  displayLabel: null, 
  valueToBind: null,
  classNames: ['control-group'],

  templateString: '<div class="control-group">' +
                    '<label class="control-label" for="input##valueToBind##">##displayLabel##</label>' +
                    '<div class="controls">' +
                      '{{view Ember.TextField valueBinding="##valueToBind##" id="input##valueToBind##"}}' +
                    '</div>' +
                  '</div>',

  preprocessTemplate: function () {
   var template =  this.templateString.replace(/##valueToBind##/g, this.get('valueToBind'));
   return template.replace(/##displayLabel##/, this.get('displayLabel'));
  },

  template: Ember.Handlebars.compile(this.preprocessTemplate())
})

Error:

Uncaught TypeError: Object [object global] has no method 'preprocessTemplate'

My question is: What is the best way to go about cleaning this up?

2
You might want to take a look at Ember Bootstrap. - ahmacleod
While I didn't use Ember Bootstrap, looking at their models led me in the right direction :). Thank you! - BushyMark
Your error is unrelated to Ember... template: Ember.Handlebars.compile(this.preprocessTemplate()) the value of this is the window object. - Trek Glowacki

2 Answers

1
votes

Off the top of my head, I think using an Ember Mixin with the layout property can help you achieve what you want.

App.BootstrapMixin = Em.Mixin.extend({
  layout: Em.Handlebars.compile(
              '<div class="control-group">' +
                '<label class="control-label" for="input##valueToBind##">##displayLabel##</label>' +
                '<div class="controls">' +
                  '{{yield}}' +
                '</div>' +
              '</div>')

})

then you could define your custom bootstrap view like so:

 App.BootstrapTextField = Em.TextField.extend(App.BootstrapMixin, {
    // your custom logic
 })

and include it in the template like this:

<form class="form-horizontal">
   {{view App.BootstrapTextField valueBinding="name" id="inputEmail"}}
   .. 
   ..
</form>

I haven't exactly checked to make sure this all works (may have made some silly error or something), but I think the general idea should be sufficient to get you off the ground. Hope this helps!

1
votes

Found the answer in another post. The problem was the processing of the template to have the "for"attribute of the label match the id element of the input element. here is the post:

Using Ember.js text field ids for a <label> tag

And my solution:

App.BootstrapTextField = Ember.View.extend({
  displayLabel: null, 
  classNames: ['control-group'],

  template: Ember.Handlebars.compile('' +
              '<label class="control-label" {{bindAttr for="view.textField.elementId"}}>' +
              '{{view.displayLabel}} </label>' +
              '<div class="controls">{{view Ember.TextField valueBinding=view.value viewName="textField"}}</div>'
                                    ),
})

Called with the following template:

{{view App.BootstrapTextField valueBinding='somefield' displayLabel='FieldName'}}