8

Given this chunk of HTML:

<div id="email_field" class="control-group">
  <label class="control-label" for="account.email">Email</label>
  <div id="email_input" class="controls">
    <input id="account.email" name="account.email" type="text" placeholder="jpublic@example.com">
    <span class="help-block">We just need a valid email address.</span>
  </div>
</div>

How do I turn this into a re-usable partial for whatever attribute I want? IE: email, password, password confirmation, etc.

I would assume some sort of view hierarchy but I'm not quite sure.

EDIT: After further exploration I've knocked out {{view}} and {{render}} and figured out exactly what I need:

I want to: 1. Use a specific view (InputView) 2. Use a specific controller (Preferably similarly named: InputController) ({{view}} doesn't do this I think) 3. Be able to use this multiple times ({{render}} can't do this) 4. Be able to pass in values ({{render}} can't do this)

Example:

<!-- templates/application.hbs -->
{{foo "input" name="Email" id="account.email" placeholder="jpublic@email.com"}}

// controllers/input.js
Application.InputController = Ember.ObjectController.extend({
  type: "text"
});

// views/input.js
Application.InputView = Ember.View.extend({
  templateName: "form/input"
});

<!-- templates/form/input.hbs -->
<input {{bindAttr id="id" name="name" type="type" placeholder="placeholder"}}>
krainboltgreene
  • 1,841
  • 2
  • 17
  • 25

3 Answers3

3

I would create a view that takes all the parameters that are variable. Such as:

{{view App.FormEntity
    name="email"
    placeholder="My placeholder..."
    help="We just need a valid email address."
    valueBinding="value"
}}

From there you could extract the label, the various class names, and then use Ember.TextField to bind the value to.

Once you have all of those arguments passed into the view, it should be nice and easy to create the markup using a mixture of bindAttrs, a couple of computed properties, and Ember helpers (such as the Ember.TextField).

Wildhoney
  • 4,969
  • 33
  • 38
  • 1
    Great response, thanks, but it leaves me wondering what controller does the view look for when using bindAttr? I can't seem to gather that from the docs. – krainboltgreene Feb 18 '13 at 01:32
  • 1
    Well, every view should have access to a controller. By default, if you're in the `IndexRoute` then the index view will have access to `IndexController`, and if you insert a view in that index view, then that view will inherit the `IndexController`. Maybe this will help: http://stackoverflow.com/questions/14802223/different-rendering-techniques-in-emberjs-handlebars-template/14802424#14802424 – Wildhoney Feb 18 '13 at 09:28
  • Interesting, but doesn't help me I guess? I want this helper to have it's own controller. Or more specifically I have computed values that I don't want to attach to IndexController. – krainboltgreene Feb 19 '13 at 07:42
  • So I've unaccepted the answer because this really only gives one piece to the puzzle, it doesn't actually solve my problem: I want to take this reusable chunk of HTML, make it dynamic, and use it in other templates. – krainboltgreene Feb 19 '13 at 07:44
  • Then use something like `{{render "input" property="whatever"}}` and it will attach `InputController` to your `InputView`. – Wildhoney Feb 19 '13 at 09:53
  • Works for a lot of reasons, but raises an exception when used multiple times :( – krainboltgreene Feb 19 '13 at 20:44
1

I am new to Emberjs and looking for pretty much the same thing, but couldn't you also simply use http://emberjs.com/guides/templates/writing-helpers/ for that? I will try it myself, so can give more updates if that works out.

Update: Ok, I got it to work. I created a new Helpers folder with FormgeneratorHelper.js and the following code:

Ember.Handlebars.registerBoundHelper('control-group', function (options) {
    var name = options.hash.test.capitalize();
    console.log(name);
    return new Handlebars.SafeString('<div class="control-group"> \
            <label class="control-label" for="input' + name + '">' + name + '</label> \
            <div class="controls"> \
                <input type="text" id="input' + name + '" placeholder="' + name + '" /> \
            </div> \
        </div>');
});

An then, no matter in which template you can do:

{{control-group test="email"}}

I really like the idea of using helpers, but if you are using plain Javascript (as opposed to CoffeScript) and have more than one line of code, then it gets a bit ugly unfortunately. But will probably still use that method.

Markus
  • 1,214
  • 1
  • 9
  • 27
1

How do I turn this into a re-usable partial for whatever attribute I want? IE: email, password, password confirmation, etc.

What you want is the experimental {{control}} helper. The control helper is currently under development and is considered experimental. To enable it, set ENV.EXPERIMENTAL_CONTROL_HELPER = true before requiring Ember.

I want to: 1. Use a specific view (InputView) 2. Use a specific controller (Preferably similarly named: InputController) ({{view}} doesn't do this I think)

Out-of-box the control helper expects to be passed a template name. That template name is used to lookup a matching view and controller. So for example:

App.InputView = Ember.View.extend()
App.InputController = Ember.Controller.extend()
{{control input}}

See:

  1. Be able to use this multiple times ({{render}} can't do this)

A control can be used multiple times

  1. Be able to pass in values ({{render}} can't do this)

Like the {{view}} helper, {{control}} will accept arbitrary name/value pairs. So as in your example, one could manually pass options to the control helper. Like the {{view}} helper these options become properties on the view instance:

<!-- templates/form/input.hbs -->
<label class="control-label" {{bindAttr for="view.inputId"}}>
  {{view.label}}
</label>
<div class="controls">
  <input {{bindAttr id="view.inputId" name="view.name" type="type" placeholder="view.placeholder"}}>
  <span class="help-block">{{view.help}}</span>
</div>

// controllers/form_input.js
App.FormInputController = Ember.ObjectController.extend({
  type: "text"
});

// views/form_input.js
App.FormInputView = Ember.View.extend({
  classNames: ["control-group"]
});

<!-- templates/application.hbs -->
{{control "form/input" 
    inputId="account.email" 
    name="email"
    label="Email" 
    placeholder="jpublic@email.com" 
    help="We just need a valid email address."
}}

See this jsbin for working example

Also keep in mind that A control can specify a model to use in its template - with this in place we can bind properties to model data. Also if a controller's model changes, its child controllers are destroyed so the control will reset as expected if the model is swapped out.

Mike Grassotti
  • 19,040
  • 3
  • 59
  • 57