Given these JSON data models on a RESTful server
/users
{"users":[
{"id":"1","first_name":"John","last_name":"Doe"},
{"id":"2","first_name":"Donald","last_name":"Duck"}
]}
/users/1
{"user":
{"id":"1","first_name":"John","last_name":"Doe","account":"1"}
}
/accounts
{"accounts":[
{"id":"1","owned_by":"1"},{"id":"2","owned_by":"2"}
]}
/accounts/1
{"account":
{"id":"1","owned_by":"1","transactions":[1,17]}
}
and these Ember data models
App.Store = DS.Store.extend({
revision: 11,
adapter: DS.RESTAdapter.create({
url: 'http://api.mydomain.ca'
})
});
App.User = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
account: DS.belongsTo('App.Account')
});
App.Account = DS.Model.extend({
ownedBy: DS.belongsTo('App.User'),
transactions: DS.hasMany('App.Transaction')
});
what other ember code do I have to write to load the data into the models and then write a template that outputs a user's name, account id, and the number of transactions in the account?
I was able to solve this so I will post my code in case it helps someone else. The trick is to make sure the JSON data is formatted exactly how Ember wants it and to create the proper routes.
From what I can tell, Ember expects parent objects to provide a list of child objects. This feels weird to me so if anyone knows a way to do it with child objects referencing their parents with a foreign key please let me know.
I changed the account property on my /user/:user_id JSON object to account_id I also included the account_id on the user objects found at /users and I changed the owned_by property on the account to user_id.
My javascript file
var App = Ember.Application.create();
// Router
App.Router.map(function() {
this.resource('users', function() {
this.resource('user', {path:':user_id'});
}); // '/#/users/:user_id'
this.resource('accounts', function() {
this.resource('account', {path:':account_id'});
});
});
App.IndexRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('users');
}
});
App.UsersRoute = Ember.Route.extend({
model: function() {
return App.User.find();
}
});
App.AccountsRoute = Ember.Route.extend({
model: function() {
return App.Account.find();
}
});
// Controllers
App.TransactionsController = Ember.ArrayController.extend();
// Adapter
App.Adapter = DS.RESTAdapter.extend({
url: 'http://api.mydomain.ca'
});
// Models
App.Store = DS.Store.extend({
revision: 11,
adapter: App.Adapter.create({})
});
App.User = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
account: DS.belongsTo('App.Account')
});
App.Account = DS.Model.extend({
user: DS.belongsTo('App.User'),
transactions: DS.hasMany('App.Transaction'),
balance: function() {
return this.get('transactions').getEach('amount').reduce(function(accum, item) {
return accum + item;
}, 0);
}.property('transactions.#each.amount')
});
App.Transaction = DS.Model.extend({
account: DS.belongsTo('App.Account'),
amount: DS.attr('number'),
description: DS.attr('string'),
timestamp: DS.attr('date')
});
And the handlebars templates
<script type="text/x-handlebars" data-template-name="application">
<div class="row">
<div class="twelve columns">
<h2>Accounts</h2>
<p>{{outlet}}</p>
</div>
</div>
</script>
<script type="text/x-handlebars" data-template-name="users">
<div class="row">
<div class="three columns" id="users">
{{#each user in controller }}
{{#linkTo "user" user class="panel twelve columns"}}{{user.firstName}} {{user.lastName}}{{/linkTo}}
{{/each}}
</div>
<div class="nine columns" id="user">
{{ outlet }}
</div>
</div>
</script>
<script type="text/x-handlebars" data-template-name="user">
<h2>{{firstName}} {{lastName}}</h2>
{{#if account}}
{{render "account" account}}
{{else}}
Error: Account not set up!
{{/if}}
</script>
<script type="text/x-handlebars" data-template-name="accounts">
<div class="row">
<div class="three columns" id="accounts">
{{#each account in controller }}
{{#linkTo "account" account class="panel twelve columns"}}{{account.id}} {{account.user.firstName}} {{account.user.lastName}}{{/linkTo}}
{{/each}}
</div>
<div class="nine columns" id="account">
{{ outlet }}
</div>
</div>
</script>
<script type="text/x-handlebars" data-template-name="account">
<p>Account Number: {{id}}, Balance: {{balance}}, {{transactions.length}} transactions</p>
{{render "transactions" transactions}}
</script>
<script type="text/x-handlebars" data-template-name="transactions">
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Amount</th>
<th>Timestamp</th>
<th>Description</th>
</tr>
</thead>
<tbody>
{{#each transaction in controller}}
<tr>
<td>{{transaction.id}}</td>
<td>{{transaction.amount}}</td>
<td>{{transaction.timestamp}}</td>
<td>{{transaction.description}}</td>
</tr>
{{/each}}
</tbody>
</table>
</script>
Create a Index route that seeds your IndexController with a model and create a related Template that iterates over your relationships.
Here is an example for a simple HasMany-Relationship between post and comments:
var App = Ember.Application.create();
App.Store = DS.Store.extend({
revision: 11,
adapter: DS.RESTAdapter.create()
});
App.Post = DS.Model.extend({
comments: DS.hasMany('App.Comment')
});
App.Comment = DS.Model.extend({
post: DS.belongsTo('App.Post'),
body: DS.attr('string'),
});
App.IndexRoute = Ember.Route.extend({
setupController: function(controller) {
controller.set('content', App.Post.find("1"));
}
});
The HTML-Code should look like this:
<!DOCTYPE html>
<html>
<head>
...
</head>
<body>
<script type="text/x-handlebars" data-template-name="index">
{{#each comment in content.comments}}
{{comment.body}}
{{/each}}
</script>
</body>
And the last but not least the server response /posts/1
{
"post": {
"id": 1,
"title": "Rails is omakase",
"comments": [1, 2, 3]
},
"comments": [{
"id": 1,
"body": "But is it _lightweight_ omakase?"
},
{
"id": 2,
"body": "I for one welcome our new omakase overlords"
},
{
"id": 3,
"body": "Put me on the fast track to a delicious dinner"
}]
}
Related
I have a list of messages that are associated with a model:
App.User = DS.Model.extend({
displayName: DS.attr('string'),
email: DS.attr('string'),
firstName: DS.attr('string'),
lastName: DS.attr('string'),
location: DS.attr('string'),
messages: DS.hasMany('message', { async: true })
});
App.Message = DS.Model.extend({
user: DS.belongsTo('user'),
recipient: DS.belongsTo('user'),
createdAt: DS.attr('isodate'),
updatedAt: DS.attr('isodate'),
fullText: DS.attr('string'),
subject: DS.attr('string')
});
I render a user's messages like so:
<script type="text/x-handlebars" data-template-name="profile">
<form class="new-message navbar-form" role="search" {{action "sendMessage" on="submit"}}>
{{textarea valueBinding="newMessageText" rows="3" placeholder="Send a Message"}}
<button type="submit" class="btn btn-default btn-lg btn-block" style="margin-top: 10px">Submit</button>
</form>
{{#each message in messages}}
<div class="message text-left">
<div class="page-header clearfix">
<p class="created-at">{{format-date message.createdAt}}</p>
<img class="profile-pic" src="http://lorempixel.com/24/24/people" alt="profile" class="img-rounded">
<p class="subject">{{message.subject}}</p>
</div>
<p class="full-text">{{message.fullText}}</p>
</div>
{{/each}}
</script>
When a new message is submitted using the form above, a new messages is added to the store and saved to the server. The problem is that the list of messages is not re-rendered to include the newly-added message when the save is performed:
App.UserController = Ember.ObjectController.extend({
newMessageText: '',
actions: {
sendMessage: function() {
var that = this;
var message = this.get('store').createRecord('message', {
fullText: this.newMessageText,
recipient: this.get('model')
});
message.save().then(function () {
// A new message is added to the store linked to the user, but the
// newly-saved message is not added to the template.
that.set('newMessageText', '');
});
}
}
});
What's the proper way to ensure new messages added to the store force my template to re-render?
You need to use an ArrayController for the messages list, or an Ember.Array (depends on your needs). You need either of those because you want your object to be observable so that the auto-updating mechanism, that the templates use, can react to the changes.
How to update hasMany in Ember.js using different controllers?
Hi
I have Ruby on Rails 4.0.3 app and I am using Ember.js
DEBUG: Ember : 1.6.0-beta.3 ember.js?body=1:3917
DEBUG: Ember Data : 1.0.0-beta.7+canary.f482da04 ember.js?body=1:3917
DEBUG: Handlebars : 1.3.0 ember.js?body=1:3917
DEBUG: jQuery : 1.11.0
I want to display hasMany in the same view using different controllers.
I have seen some example on StackOverflow but most (if not all) of them are for displaying records.
Ok talk is cheap, I am showing the code:
Models:
-javascripts/models/task.js
EmTasks.Task = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr("string"),
list: DS.belongsTo('list')
});
-javascripts/models/list.js
EmTasks.List = DS.Model.extend({
name: DS.attr('string'),
tasks: DS.hasMany('task')
});
Router:
-javascripts/router.js
EmTasks.Router.map(function(){
return this.route("lists", {
path: '/'
});
});
EmTasks.ListsRoute = Ember.Route.extend({
model: function() {
return this.store.find('list');
}
});
Controllers:
-javascripts/controllers/lists_controller.js
EmTasks.ListsController = Em.ArrayController.extend({
addList: function() {
this.store.createRecord('list', {
name: this.get('newListName')
}).save();
return this.set('newListName', '');
},
destroyList: function(id) {
if (confirm("Are you sure?")) {
this.get('store').find('list', id).then( function(record) {
record.destroyRecord();
});
}
},
});
-javascripts/controllers/list_controller.js
EmTasks.ListController = Em.ObjectController.extend({
actions: {
editList: function() {
this.set('isEditingList', true);
var model = this.get('model')
},
acceptChanges: function () {
this.set('isEditingList', false);
var name = this.get('model.name');
if (Ember.isEmpty(name)) {
this.send('removeList');
} else {
var list = this.get('model')
list.set('name', name);
list.save()
}
},
removeList: function () {
var list = this.get('model');
list.destroyRecord();
}
},
isEditingList: false
});
-javascripts/controllers/task_controller.js
EmTasks.TaskController = Em.ObjectController.extend({
isEditingTask: false
});
Templates:
-javascripts/templates/lists.handlebars [fragment]
{{#each itemController='list'}}
<div class='col-md-8'>
<h3>
{{#if isEditingList}}
{{edit-input class="form-control" value=name focus-out="acceptChanges" insert-newline="acceptChanges"}}
{{else}}
<div {{action 'editList' on='doubleClick'}}>
{{name}}
</div>
{{/if}}
</h3>
</div>
<div class='col-md-4 down13p'>
<button class="btn btn-danger btn-small pull-right" {{action "destroyList" id}} type="button">
<span class="glyphicon glyphicon-ban-circle"></span>
</button>
</div>
{{#each task in this.tasks }}
<div class="col-md-10">
{{#if task.isEditingTask}}
{{edit-input class="form-control" value=task.name focus-out="acceptChanges" insert-newline="acceptChanges"}}
{{else}}
<div {{action 'editList' on='doubleClick'}}>
{{name}}
</div>
{{/if}}
But is looks like isEditingTask property is not working...
Any idea how to fix that?
OK found a solution, just add itemController to tasks each loop
{{#each task in this.tasks itemController='task' }}
<div class="col-md-10">
{{#if task.isEditingTask}}
{{edit-input class="form-control" value=task.name focus-out="acceptChanges" insert-newline="acceptChanges"}}
{{else}}
<div {{action 'editTask' on='doubleClick'}}>
{{name}}
</div>
{{/if}}
HTH
I have an ember application which has a number of users. Each of these users can be associated with a number of subjects. So I have a subjects model:
App.Subjects = DS.Model.extend({
subject : DS.attr('string'),
});
App.Subject.FIXTURES = [{
id: 1,
name: 'Sales',
}, {
id: 2,
name: 'Marketing',
}
];
and a users model:
App.User = DS.Model.extend({
name : DS.attr(),
email : DS.attr(),
subjects : DS.hasMany('subject'),
});
App.User.FIXTURES = [{
id: 1,
name: 'Jane Smith',
email: 'janesmith#thesmiths.com',
subjects: ["1", "2"]
}, {
id: 2,
name: 'John Dorian',
email: 'jd#sacredheart.com',
subjects: ["1", "2"]
}
];
I am having trouble representing this 1:M relationship in my templates. I have an edit user template (which Im also using to create a user) in which you can select the user's subjects via checkboxes. However, I want these checkboxes to be driven by the data in my subjects model. Is this possible? I have found very little documentation online and am very new to ember development. Here is my template:
<script type = "text/x-handlebars" id = "user/edit">
<div class="input-group">
<div class="user-edit">
<h5>User name</h5>
{{input value=name}}
<h5>User email</h5>
{{input value=email}}
<h5>Subjects</h5>
{{input type="checkbox" value = "sales" name="sales" checked=sales}}
{{input type="checkbox" value = "support" name="support" checked=support}}
</div>
<button {{action "save"}}> Save </button>
</div>
</script>
EDIT: Here is my current userController.js
App.UserController = Ember.ObjectController.extend({
deleteMode: false,
actions: {
delete: function(){
this.toggleProperty('deleteMode');
},
cancelDelete: function(){
this.set('deleteMode', false);
},
confirmDelete: function(){
// this tells Ember-Data to delete the current user
this.get('model').deleteRecord();
this.get('model').save();
// and then go to the users route
this.transitionToRoute('users');
// set deleteMode back to false
this.set('deleteMode', false);
},
// the edit method remains the same
edit: function(){
this.transitionToRoute('user.edit');
}
}
});
what you need to do is change this line in your template:
{{#each subject in user.subject}}
{{subject.name}},
{{/each}}
for this:
{{#each subject in user.subjects}}
{{subject.name}},
{{/each}}
did you notice I changed subject for subjects ?
and, I would also recommend you to change this code in App.SubjectController:
selected: function() {
var user = this.get('content');
var subject = this.get('parentController.subjects');
return subject.contains(user);
}.property()
to this:
selected: function() {
var subject = this.get('content');
var userSubjects = this.get('parentController.subjects');
return userSubjects.contains(subject);
}.property()
that's a better representation of the data.
I'm trying to follow this basic Ember.js tutorial but having no luck with the "posts" model. I have everything set up according to the demonstration, however I am getting the error:
Uncaught More context objects were passed than there are dynamic segments for the route: post
Since this is the first time I've ever worked with an Ember.js app, I honestly have no clue what this means. Any help (literally anything) would be greatly appreciated.
Here is my App.js
App = Ember.Application.create();
App.Store = DS.Store.extend({
adapter: 'DS.FixtureAdapter'
});
App.Router.map(function () {
this.resource('posts', function() {
this.resource('post', { path:'post_id'})
});
this.resource('about');
});
App.PostsRoute = Ember.Route.extend({
model: function () {
return App.Post.find();
}
})
App.Post = DS.Model.extend({
title: DS.attr('string'),
author: DS.attr('string'),
intro: DS.attr('string'),
extended: DS.attr('string'),
publishedAt: DS.attr('date')
});
App.Post.FIXTURES = [{
id: 1,
title: "Rails in Omakase",
author: "d2h",
publishedAt: new Date('12-27-2012'),
intro: "Blah blah blah blah",
extended: "I have no clue what extended means"
}, {
id: 2,
title: "Second post",
author: "second author",
publishedAt: new Date('1-27-2012'),
intro: "second intro",
extended: "Second extended"
}];
And here is the html for the posts.
<script type="text/x-handlebars" id="posts">
<div class="container-fluid">
<div class="row-fluid">
<div class="span3">
<table class='table'>
<thead>
<tr><th>Recent Posts</th></tr>
</thead>
{{#each model}}
<tr><td>
{{#linkTo 'post' this}}{{title}} <small class='muted'>by {{author}}</small>{{/linkTo}}
</td></tr>
{{/each}}
</table>
</div>
<div class="span9">
{{outlet}}
</div>
</div>
</div>
</script>
<script type="text/x-handlebars" id="post">
<h1>{{title}}</h1>
<h2> by {{author}} <small class="muted">{{publishedAt}}</small></h2>
<hr>
<div class="intro">
{{intro}}
</div>
<div class="below-the-fold">
{{extended}}
</div>
</script>
I think you meant to have a route specified.
this.resource('posts', function() {
this.route('post', { path:'/post/:post_id'})
});
The error sounds like you are passing something like post/12 and you don't have a dynamic segment specified(written as :post_id) The : is the important point that specifies a dynamic segment.
Taken from the Ember.js documentation
The accepted answer works.
However, given where the op is in the example, the more correct fix is to leave the following alone:
this.resource('posts');
and add this below it:
this.resource('post', { path: '/post/:post_id'});
I'm creating an ember.js app. The first page is single field, with a button. On button click, I'd like it to go to the path #/deals/:api_key. However, when I click the button, I'm not clear on the best way to go about it.
Here's what i have so far:
App = Ember.Application.create();
App.Store = DS.Store.extend({
revision: 12,
adapter: 'DS.FixtureAdapter'
});
App.Deal = DS.Model.extend({
name: DS.attr('string')
});
App.Router.map(function() {
this.resource('start', { path: '/' });
this.resource('deals', { path: '/deals/:api_key' });
});
App.DealsRoute = Ember.Route.extend({
model: function(params) {
return App.Deal.find();
}
});
App.StartController = Ember.ObjectController.extend({
apiKey: "",
getDeals: function (model) {
this.transitionToRoute('deals');
}
});
App.DealsView = Ember.View.extend({
didInsertElement: function() {
// Add active class to first item
this.$().find('.item').first().addClass('active');
this.$().find('.carousel').carousel({interval: 1000});
}
});
<script type="text/x-handlebars" data-template-name="start">
{{view Em.TextField valueBinding="apiKey" placeholder="API Key"}}
<br />
<button {{action 'getDeals'}} class="btn btn-large">Get Won Deals!</button>
</script>
<script type="text/x-handlebars" data-template-name="deals">
<div id="carousel" class="carousel slide">
<div class="carousel-inner">
{{#each model}}
<div class="item">
{{name}}
</div>
{{/each}}
</div>
</div>
</script>
Any suggestions on the right way to pass data from a text input into the next transition as a query param?
you need to pass the parameter in the view in a linkTo helper, e.g.
{{#linkTo 'deals' api_key}}go to deals{{/linkTo}}
this generates a link with the dynamic section you need.
go to deals
check the docs about linkTo for more info: http://emberjs.com/guides/templates/links/