how to add a new record UI in ember - ember.js

Just when I think that I'm getting a handle on ember, this happens and I run into a brick wall.
I have
App.Router.map(function() {
this.resource('customers', function() {
this.resource('customer', {
path : ':customer_id'
});
customers route :
App.CustomersRoute = Ember.Route.extend({
model: function() {
return this.store.find('customer');
},
customer controller :
App.CustomerController = Em.ObjectController.extend({
needs: ['state'],
isEditing: false,
isNotEditing: Ember.computed.not('isEditing'),
actions : {
startEditing: function() {
this.set('isEditing',true);
this.set('validationErrors', '');
},
save: function() {
and these templates :
<script type="text/x-handlebars" data-template-name="customers">
...
{{outlet}}
...
</script>
<script type="text/x-handlebars" data-template-name="customer">
...
{{#if isEditing}}
<div class="well well-sm">
<a class="btn btn-success" {{action save}}>Save</a>
<a class="btn btn-warning" {{action cancel}}>Cancel</a>
</div>
{{else}}
<div class="well well-sm">
<a class="btn btn-primary" {{action startEditing}}>Edit</a>
<a class="btn btn-danger" {{action delete}}>Remove</a>
</div>
{{/if}}
this is all working well for me. I can select a customer, press the edit button, form inputs get enabled, press the save button and changed data is persistent back to the db
however : this is my brick wall.
How do I enable a feature to create a new record : I dont want to duplicate the edit form, just show blanks
I am assuming that I need to put "new" into the router map
this.resource('customers', function() {
this.resource('customer', {
path : ':customer_id'
});
route('new');
});
but do I create a CustomerNew controller and CustomerNew route and CustomerNew template ?
I have put an action into the customers controller
<a class="btn btn-primary btn-xs pull-right" {{action startNew}}>New</a>
do I really need to create a route & controller & template just to handle the new action ? Or can I reuse the customer/1 route/controller/template ?
thanks

To do things The Ember Way (TM) you'd want to use a new route, controller, and template. Ember does make it easy to consolidate your logic and not have to duplicate code. In templates you can do this with partial, and in your JS code you can do it with Ember.Mixin.
Here's a JSBin of the general idea : http://jsbin.com/ucanam/1056/edit
Here's the interesting parts of the templates :
<script type="text/x-handlebars" data-template-name="customers/new">
Add a new customer<br/>
{{partial "customers/form"}}
</script>
<script type="text/x-handlebars" data-template-name="customer/edit">
Edit a customer<br/>
{{partial "customers/form"}}
</script>
<script type="text/x-handlebars" data-template-name="customers/_form">
{{input value=name}}
<button {{action save}}>Save</button>
<button {{action cancel}}>Cancel</button>
</script>
And then the controllers :
App.CrudController = Ember.Mixin.create({
actions : {
save : function(){
this.get('model').save();
this.transitionToRoute('customers');
},
cancel : function(){
this.get('model').rollback();
this.transitionToRoute('customers');
}
}
});
App.CustomersNewController = Ember.ObjectController.extend(App.CrudController,{});
App.CustomerEditController = Ember.ObjectController.extend(App.CrudController,{});

Related

A property in an ember component is not propagating to its parent controller

I have a component priority-selector that looks like this...
export default Ember.Component.extend({
priority: 'low',
didInsertElement: function() {
this.$('[data-toggle="tooltip"]').tooltip();
},
actions: {
click: function(priority) {
this.set('priority', priority);
this.$('.btn-primary').removeClass('btn-primary');
this.$('.btn-for-' + priority).addClass('btn-primary');
}
}
});
Template code...
<div class="pull-right btn-group" role="group">
<div type="button" class="btn btn-default btn-for-low" {{action 'click' 'low'}}>LOW</div>
<div type="button" class="btn btn-default btn-for-medium" {{action 'click' 'medium'}}>MEDIUM</div>
<div type="button" class="btn btn-default btn-for-high" {{action 'click' 'high'}}>HIGH</div>
</div>
I use this component in a template like so...
<div class='col-sm-3'>
{{priority-selector priority=priority_gender}}
</div>
And I have it specified in the controller...
export default Ember.Controller.extend({
...
priority_gender: 'low'
...
})
But the property in the controller never changes when I interact with the component, I can observe this to be the case by looking in the ember inspector.
Your code is working.
So, make sure to specify priority_gender in right controller for template you're using priority-selector component.

Rendering model via nested templates in Ember

I'm building an app with multiple todo lists. The todo list used to correctly show todo model.
But I added function() to stack router to wrap todos router (so that it would correctly render todos template in stack template).
Then todos/index template (which is rendered through the outlet in todos) stopped displaying todo model.
This is my router structure:
Todos.Router.map(function() {
this.resource('stacks', {path: '/stacks'});
this.resource('stack', {path: '/stacks/:stack_id'}, function () {
this.resource('todos', { path: '/todos/:todos_id' }, function () {
// additional child routes will go here later
this.route('active');
this.route('completed');
this.route('new');
});
this.resource('todo', { path: 'todo/:todo_id' });
});
});
Stack template which renders Todo template:
<script type="text/x-handlebars" data-template-name="stack">
<h1>
<label {{action "editStack" on="doubleClick"}}>{{stackTitle}}</label>
{{input
value=stackTitle
action="createStack"}}
<div>{{model.stackTitle}}</div>
</h1>
{{render 'todos' todo}}
</div>
</script>
Then this todo template has an outlet for todo/index:
<script type="text/x-handlebars" data-template-name="todos">
<div class="container-fluid">
<section id="main">
{{outlet 'todos/index'}}
{{outlet modal}}
{{input type="checkbox" id="toggle-all" checked=allAreDone}}
</section>
</div>
</script>
And todo/index template:
<script type="text/x-handlebars" data-template-name="todos/index">
<ul id="todo-list">
<li {{bind-attr class="todo.isCompleted:completed todo.isEditing:editing"}}>
{{#each todo in model itemController="todo"}}
{{#if todo.isEditing}}
{{edit-todo class="edit" value=todo.title focus-out="acceptChanges"
insert-newline="acceptChanges"}}
{{else}}
{{input type="checkbox" checked=todo.isCompleted class="toggle"}}
{{outlet}}
<button {{action 'openModal' 'modal' model}}>
<label {{action "editTodo" on="doubleClick"}}>{{todo.title}}</label>
</button>
{{/if}}
</li>
{{/each}}
</ul>
</script>
Routers:
Todos.TodosRoute = Ember.Route.extend({
model: function() {
return this.store.find('todo');
},
});
Todos.TodosIndexRoute = Ember.Route.extend({
model: function() {
return this.modelFor('todos');
},
});
I've tried using {{partial}} helper in case that was a problem, but it didn't seem to change much.
Maybe I'm missing something in todos/index router that I need to call data through nested templates?
I appreciate your help!
There's a lot going on here but I think the root of your problem is with the way that you're using render in your templates/stack.
Since your routes/todos route is nested inside your routes/stack route, you're going to want to use an outlet inside your templates/stack if you want any of the templates for the nested routes to be rendered.
When you use render 'todos' todo, you're saying to render templates/todos with the todo property from the controllers/stack as the model. This isn't going to use your routes/todos or routes/todos-index to set the model.
What you probably want is for your templates/stack to have an {{outlet}} that either the routes/todos or routes/todo is rendered into when you visit either of those routes.

emberjs in the console I see that records are created but they are not displayed by handlebars

First many thanks to Mike Grassotti, for helping in the IRC. He helped resolved the bugs with the save method.
My problem is that in the console I see that records are created but they are not displayed.
I am using ember-data to create new record. The addComment function creates the record in a transaction, while the save function, only calls this.transaction.commit.
In the console after I click save(), the record seems created but handlebars doesn't display the newly created record. This is an excerpt of what I see in the console when I dig into the results of console.log
>committed: Object
firstRecordKind: "belongsTo"
firstRecordName: "post"
>firstRecordReference: Object
clientId: 4
id: "ember411"
>type: EmBlog.Comment
ClassMixin: Ember.Mixin
>FIXTURES: Array[1]
0: Object
body: "ty"
id: "ember411"
post: "1"
To create a new record, click on post -> then 'post title' -> at the bottom addComment- > then save and you will see that the record was not created.
The relevant bit of code from the jsfiddle. This controller will not have a route as it will be sideloaded
EmBlog.CommentNewController = Em.ObjectController.extend({
needs: ['postsShow'],
isAddingNew: false,
addComment: function(body){
console.log("this: ", this.toString());
var post = this.get('controllers.postsShow.content');
console.log(post);
transaction = this.get('store').transaction();
console.log(transaction);
console.log(this.get('content').toString());
this.set('content', transaction.createRecord(EmBlog.Comment, ({post: post })));
this.transaction = transaction;
console.log(this.get('content').toString());
this.set('isAddingNew', true);
},
save: function(){
var comment = this.get('content');
comment.one('didCreate', this, function() {
this.set('isAddingNew', false);
});
this.transaction.commit();
}
});
The relevant bit from the handlebars template
<script type="text/x-handlebars" data-template-name="posts/show">
<h1>Post</h1>
<h3> {{title}} </h3>
<h3> {{body}} </h3>
<br/>
<p> {{#linkTo 'posts.index'}} back {{/linkTo}}</p>
<p> {{#linkTo 'posts.edit' content}} Edit the post {{/linkTo}}</p>
<br/>
<b> Comments</b>
{{render 'comment/new' comments}}
</script>
<script type='text/x-handlebars' data-template-name='comment/new'>
{{#if controller.isAddingNew}}
<form {{action save on='submit'}}>
{{view Ember.TextArea valueBinding="body" placeholder="body"}}
<button type="submit"> save comment </button>
</form>
{{/if}}
<br/>
<div>
<button {{action addComment}} {{bindAttr disabled="isAddingNew"}}>Add Comment</button>
</div>
</script>
Thanks
hmm, maybe i'm blind, but i can't see any code for displaying comments in your templates.
something like
<ul>
{{#each comment in comments}}
<li>{{comment.body}}</li>
{{/each}}
</ul>
should probably do the trick.

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/

Action on self created View

If I've a simple template with a Button which has an Action, and create a ember View using this template, how can I let the action target the function on teh View.
Example:
http://jsfiddle.net/Krutius/DxsXz/
Handlebars / HTML:
<div id="content">
<div id="main"></div>
</div>
<script type="text/x-handlebars" data-template-name="test">
<button class="btn btn-primary" {{action go}}>Suchen</button>
</script>​
JavaScript:
$(function() {
App = Em.Application.create({
rootElement: "#content"
});
Em.View.create({
templateName: 'test',
go: function() {
alert: "go";
}
}).append("#main");
});​
The problem is your append call: Ember.View#append does not take any arguments, see the code.
I don't know what the final application / html should look like, so there are several answers how you could solve your problem. The simplest would be to inline the button into a template for the application, see http://jsfiddle.net/pangratz666/vY9PE/:
Handlebars:
<script type="text/x-handlebars" data-template-name="test">
<div id="content">
<div id="main">
<button class="btn btn-primary" {{action go}}>Suchen</button>
</div>
</div>
</script>​
JavaScript:
App = Em.Application.create({});
Em.View.create({
templateName: 'test',
go: function(evt) {
console.log('go', evt);
}
}).append();​
Ember.js IS able to insert an element at a specific position though, but this should only be necessary for an applications' main view. So you could use Ember.View#appendTo(target). But again, this should only be used in rare cases.