How do I add html to an emberjs template? - templates

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

Related

Ember template does not remove when transition to different route

I feel as if this is a very simple problem to fix I am just not aware of how to fix it. Currently I have an outlet that displays a template like this:
user.hbs:
<div id="user-profile">
<img {{bind-attr src="avatarURL"}} alt="User's Avatar" class="profilePic"/>
<h2>{{name}}</h2>
<span>{{email}}</span>
<p>{{bio}}</p>
<span>Created {{creationDate}}</span>
<button {{action "edit"}}>Edit</button>
{{outlet}}
</div>
The template to be rendered at the outlet is this:
user_edit.hbs:
<div id="user-edit">
<h3>Edit User</h3>
<div class="panel-body">
<div class="row">
<label class="edit-user-label">Choose user avatar</label>
{{input class="form-control" value=avatarUrl}}
</div>
<div class="row">
<label>User name</label>
{{input class="form-control" value=name}}
</div>
<div class="row">
<label class="edit-user-label">User email</label>
{{input class="form-control" value=email}}
</div>
<div class="row">
<label class="edit-user-label">User short bio</label>
{{textarea class="text-control" value=bio}}
<div>
<div class="row">
<button {{action "save"}}>SAVE</button>
<button {{action "cancel"}}>CANCEL</button>
</div>
</div>
</div>
When I first visit the user route, the outlet does not display because the button has not been clicked. The button is hooked to a controller which takes care of the action. The action just transitions to the route where the template is displayed at the outlet. It appears just as expected but when I click on a different user model, the outlet from the previous user is still there without everything in the <div class="panel-body"> </div>. So Ember hides the panel-body div on transition but not the user-edit div. If you need more information I will be happy to provide it.
Here are the controllers:
userController:
App.UserController = Ember.ObjectController.extend({
actions: {
edit: function() {
this.transitionToRoute('user.edit');
}
}
});
Here is the userEditController:
App.UserEditController = Ember.ObjectController.extend({
actions: {
save: function() {
var user = this.get('model');
user.save();
this.transitionToRoute('user', user);
},
cancel: function() {
var user = this.get('model');
this.transitionToRoute('user', user);
}
}
})
Hi why dont you use {{#link-to 'edit' model}} instead of action ???
you can pass model to link-to so you dont have to get model in controller and then transitionToRoute
Look at this

Ember - displaying model value in form field

I have a user settings form like so:
<script type="text/x-handlebars" data-template-name="settings">
<form class="form-horizontal user-form" {{action "update" on="submit"}}>
<div>
<label>First Name</label>
{{input type="text" value=firstName placeholder="First Name"}}
{{error.firstName}}
</div>
<div>
<label>Last Name</label>
{{input type="text" value=lastName placeholder="Last Name"}}
{{error.lastName}}
</div>
<div>
<label>Email Address *</label>
{{input type="text" value=email placeholder="Email Address"}}
{{error.email}}
</div>
</form>
</script>
In my route for this page, I define the model:
App.SettingsRoute = Ember.Route.extend({
model: function() {
return this.store.find('user', 1);
}
});
If things are left like this, the form will automatically populate with the values retrieved from the model. However, if I add a controller:
App.SettingsController = Ember.Controller.extend({
actions: {
update: function() {
// Do something
}
}
});
...They won't. So how do I use my model in conjunction with this controller to set the properties?
The way you defined your controller was as a regular Ember.Controller and not an Ember.ObjectController so the controller is not proxying the model. If you modify it to be like this:
App.SettingsController = Ember.ObjectController.extend({
actions: {
update: function() {
// Do something
}
}
});
Then it should still automatically populate with the values from the model.

Set Handlebar class on the fly

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

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()
})
});