Set Handlebar class on the fly - ember.js

I'd like to update the class of a div according to the user input. A simple input text that need to be validated.
I have to go with a helper but I can't figure it out.
<div class="{{validationClass}}">
<p>{{input type="text" id="titleInput" value=title placeholder="Title"}}</p>
</div>
When there's nothing written in the text field I'd like to surround the box with the red colour, after the used typed a single character I want it to go default.
So, according to bootstrap 2.x I'd need to set the div class to control-group error or control-group success etc.
I've never created a helper so I'm struggling, I don't know how to call it and how to return the desired string to be replaced in {{validationClass}}
Thanks.

You can use the bind-attr helper .
This is a sample:
<script type="text/x-handlebars" data-template-name="index">
<div {{bind-attr class=":control-group validationClass"}}>
<p>{{input type="text" id="titleInput" value=title placeholder="Title"}}</p>
</div>
</script>
App.IndexController = Ember.ObjectController.extend({
title: null,
validationClass: function() {
var title = this.get('title');
return title ? 'success' : 'error';
}.property('title')
});
http://jsfiddle.net/marciojunior/6Kgty/

Use {{bind-attr}} helper
{{!hbs}}
<div {{bind-attr class=":control-group isError:error"}}>
{{input type="text" class="form-control" value=testVal}}
</div>
//Controller
App.ApplicationController = Em.Controller.extend({
testVal: '',
isError: Em.computed.empty('testVal')
});
Sameple Demo

Related

How do I add html to an emberjs template?

If I start with the following:
<script type="text/x-handlebars" id="Parent">
<p>Name: {{input type="text" value=name}}</p>
<button {{action 'addChild'}}>Add Child</button>
</script>
I would like clicking the button to produce the following:
<script type="text/x-handlebars" id="Parent">
<p>Name: {{input type="text" value=name}}</p>
<p>Child1 Name: {{input type="text" value=child1_name}}</p>
...
...
...
<p>Childn Name: {{input type="text" value=childn_name}}</p>
<button {{action 'addChild'}}>Add Child</button>
</script>
Thanks.
You want to put the html you're looking to add into the template, but within a looping construct - in this case {{#each}}. The loop will iterate over an array of children that you keep track of. Whenever you add an object to your children array, Ember will re-render the loop and therefor add the html for you. Your template will look like this:
<script type="text/x-handlebars">
<p>Name: {{input type="text" value=name}}</p>
{{#each child in children}}
<p>{{child.name}}: {{input type="text" value=child.value}}</p>
{{/each}}
<button {{action 'addChild'}}>Add Child</button>
</script>
You want to handle the addChild action so that it adds an object into your children array. You can do this in the Controller like so:
App.ApplicationController = Ember.Controller.extend({
name: 'Parent Name',
children: [],
actions: {
addChild: function() {
var children = this.get('children');
var id = children.length + 1;
children.addObject({
name: 'Child Name ' + id,
value: id
});
}
}
});
Here is a functional JSBin that you can experiment with: http://emberjs.jsbin.com/gujomizici/1/edit?html,js,output

EmberJS multiple yield helper

I have a custom view that I've created in Ember. I really love the {{yield}} helper to allow me to control the 'bread' of the sandwich. However, what I'd like to do now, is create a 'double decker' sandwich, and have a view with more than 1 yield in it, or at the very least be able to parameterize which template to use in the 2nd yield.
so for example:
layout.hbs
<div>
<div class="header">Header Content</div>
<div class="tab1">
Tab 1 Controls.
<input type="text" id="common1" />
{{yield}}
</div>
<div class="tab2">
Tab 2 Controls.
<input type="text" id="common2" />
{{yield second-template}} or {{template second-template}}
</div>
</div>
app.js
App.MyDoubleDeckerView = Ember.View.extend({
layoutName:"layout',
templateName:"defaultTemplate",
"second-template":"defaultSecond"
});
App.MyExtendedDoubleDecker = App.MyDoubleDeckerView({
templateName:"myTemplate",
"second-template":"mySecondTemplate"
});
is there any way of doing something like this? What I love about the views in ember is the ability to centralize & extend views which allows me to keep the things that are common among all the views in one place...
As of Ember 3.25 you can use so called "named blocks" (see the Passing multiple blocks subsection of https://api.emberjs.com/ember/release/modules/#glimmer%2Fcomponent).
Example component:
<h1>{{yield to="title"}}</h1>
{{yield}}
and then use it like this:
<PersonProfile #person={{this.currentUser}}>
<:title>{{this.currentUser.name}}</:title>
<:default>{{this.currentUser.siganture}}</:default>
</PersonProfile>
I think you should use named outlets for this
http://emberjs.com/guides/routing/rendering-a-template/
Something like this should work:
layout.hbs
<div>
<div class="header">Header Content</div>
<div class="tab1">
Tab 1 Controls.
<input type="text" id="common1" />
{{yield}}
</div>
<div class="tab2">
Tab 2 Controls.
<input type="text" id="common2" />
{{view "view.secondView"}}
</div>
</div>
app.js
App.MyDoubleDeckerView = Ember.View.extend({
layoutName:"layout',
templateName:"defaultTemplate",
secondView: Ember.view.extend({
templateName: "defaultSecond"
})
});
App.MyExtendedDoubleDecker = App.MyDoubleDeckerView({
templateName:"myTemplate",
secondView: Ember.view.extend({
templateName: "mySecondTemplate"
});
});
In other words, invoke a view given by view.secondView from within your template. Then, set the secondView property in your class or subclass.
You could add a bit of syntactic sugar with
App.viewForTemplateName = function(templateName) {
return Ember.View.extend({
templateName: templateName
});
};
Then, in your view definitions above, do
secondView: App.viewForTemplateName('mySecondTemplate')

Nesting existing textfield view in a new view

I have been getting a bit stuck in finding a way to reuse an ember textfield so any help would be appreciated.
What I have is (simplified here) a selection of rows like:
<div class="detailItem">Email: {{view Ember.TextField valueBinding="email"}} </div>
<div class="detailItem">Name: {{view Ember.TextField valueBinding="name"}} </div>
and instead of always wrapping in a div I'd like to make use of a new view. e.g.:
<script type="text/x-handlebars" data-template-name="detailItem">
<div class="detailItem">{{Item name}}: {{view Ember.TextField valueBinding="itemValue"}} </div>
</script>
App.DetailItemView = Em.View.extend({
templateName: 'detailItem',
name: "",
......
});
The thing I am not sure is how I get the textfield's valueBinding to link up to my controller (well actually it's content). I can obviously add another property to DetailItemView and instantiate it with that property having the values 'email' and 'name'. How though would I then pass these into the contained Ember.TextField?
Thanks for any assistance
You can do the following:
App.DetailItemView = Ember.View.extend({
templateName: 'detail_item',
classNames: ['detailItem'],
label: null,
value: ''
});
and the template:
<script type="text/template" id="detail_item">
{{view.label}}:
{{view Ember.TextField valueBinding="view.value"}}
</script>
And then use it like this:
{{view App.DetailItemView label="Email" valueBinding="email"}}
{{view App.DetailItemView label="Name" valueBinding="name"}}

In my controller I can't read a value from a form in my template

I have the following templates defined in my HTML:
<script type="text/x-handlebars" data-template-name="application">
<div>
<p>{{outlet}}</p>
</div>
</script>
<script type="text/x-handlebars" data-template-name="registration">
<form autocomplete="on">
First name:<input type='text' name='firstName'><br>
Last name: <input type='text' name='lastName'><br>
E-mail: <input type='email' name='primaryEmailAddress'><br>
Password: <input type='password' name='password' autocomplete='off'><br>
<button type='button' {{action 'createUser'}}>Register</button>
</form>
</script>
My JavaScript is as follows:
App.UsersController = Ember.ObjectController.extend({
createUser : function () {
var name = this.get('firstName');
}
});
When I click the button on my form the 'createUser' function is called. However, I am unable to read any of the values from the form.
My view is as follows:
App.UsersView = Ember.View.extend({
templateName : 'registration'
});
I appreciate it makes the association between my controller and the template, however in this scenario I'm not seeing any other value - does it offer me anything else?
The reason being you did not bind any values from the input fields to any of the property in the controller, you can use Ember's built in Ember.TextField as follows
<script type="text/x-handlebars" data-template-name="registration">
<form autocomplete="on">
<!--
The valueBinding="firstName" binds the value entered by the user in the
textfield to the property firstName in the controller
-->
First name:{{view Ember.TextField valueBinding="firstName"}}<br>
Last name:{{view Ember.TextField valueBinding="lastName"}}<br>
E-mail:{{view Ember.TextField valueBinding="email"}}<br>
Password: {{view Ember.TextField valueBinding="password" type="password"}}<br>
<button type='button' {{action 'createUser'}}>Register</button>
</form>
</script>
Now can get the access
App.UsersController = Ember.ObjectController.extend({
createUser : function () {
alert(this.get('firstName'));
alert(this.get('lastName'));
alert(this.get('email'));
alert(this.get('password'));
}
});
Fiddle: http://jsfiddle.net/QEfCG/4/

Ember.js - CollectionView, add an attribute to the div wrapper of the child view

I'm trying to combine two ebmer.js examples: Integrating with jQuery UI and the todos example from emberjs.com. I want to have a todo list that is sortable.
Everything went smooth until I got to a point where I wanted to serialize the sortable. For that to work, I need to be able to add an attribute to the sortable items.
this is the template:
{{#collection Todos.TodosListView}}
{{#view Todos.TodoView contentBinding="content" checkedBinding="content.isDone"}}
<label>{{content.title}}</label>
{{/view}}
{{/collection}}
Todos.TodosListView is a CollectionView, similar to the menu in the jQuery UI example. Todos.TodoView is a Checkbox.
This generates the following html:
<div class="ember-view todo-list ui-sortable" id="ember267">
<div class="ember-view" id="ember284">
<input type="checkbox" class="ember-view ember-checkbox todo" id="ember297">
<label>
<script type="text/x-placeholder" id="metamorph-1-start"></script>
something to do
<script type="text/x-placeholder" id="metamorph-1-end"></script>
</label>
</div>
</div>
What I need to be able to do is edit the <div> that wraps the <input>. Assuming the todo's id is 1, I want to add serial=todos_1. I tried to add didInsertElement to TodoView and add an attribute to the parent view, but I didn't have access to the content of the view (the todo itself).
Is this possible?
Thanks for your help.
EDIT:
I found a workaround - adding the ID to the DOM as a hidden element.
The updated template:
{{#collection Todos.TodosListView}}
{{#view Todos.TodoView contentBinding="content" checkedBinding="content.isDone" serial="content.serial"}}
<label>{{content.title}}</label>
<span style="display: none;" class="todo-id">{{content.id}}</span>
{{/view}}
{{/collection}}
Todos.TodoView.didInsertElement:
didInsertElement: function() {
var $div = this.get('parentView').$();
var id = $div.children('.todo-id').text();
$div.attr('serial', 'todos_' + id);
}
Generated html:
<div class="ember-view todo-list ui-sortable" id="ember267">
<div class="ember-view" id="ember284" serial="todos_100">
<input type="checkbox" class="ember-view ember-checkbox todo" id="ember297">
<label>
<script type="text/x-placeholder" id="metamorph-1-start"></script>
something to do
<script type="text/x-placeholder" id="metamorph-1-end"></script>
</label>
<span class="todo-id" style="display: none;">
<script type="text/x-placeholder" id="metamorph-2-start"></script>
100
<script type="text/x-placeholder" id="metamorph-2-end"></script>
</span>
</div>
</div>
I would still like to know if there's a more elegant way of achieving this.
You can create a computed property serial and add this property to the attributeBindings (documented here) of your itemViewClass of Todos.TodosListView, see http://jsfiddle.net/pangratz666/6X4QU/:
Todos.TodosListView = Ember.CollectionView.extend({
itemViewClass: Ember.View.extend({
attributeBindings: ['serial'],
serial: function() {
return 'todos_' + Ember.getPath(this, 'content.id');
}.property('content.id').cacheable()
})
});