Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I create a reusable partial for duplicate markup in ember.js?

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="[email protected]">
    <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="[email protected]"}}

// 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"}}>
like image 600
krainboltgreene Avatar asked Feb 17 '13 20:02

krainboltgreene


3 Answers

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).

like image 150
Wildhoney Avatar answered Nov 14 '22 11:11

Wildhoney


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.

like image 39
Markus Avatar answered Nov 14 '22 09:11

Markus


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:

  • A control renders a template with a new instance of the named controller and view
  • A control's controller and view are lookuped up via template name
  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="[email protected]" 
    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.

like image 1
Mike Grassotti Avatar answered Nov 14 '22 11:11

Mike Grassotti