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.
Related
I am creating a blog web application. I currently have the blogSchema set up as
var BlogSchema = new mongoose.Schema({
title: String,
image: String,
body: String,
created:
{type: Date, default: Date.now},
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
},
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
]
});
module.exports = mongoose.model("Blog", BlogSchema);
I have a post form of the following
<div class="ui main text container segment">
<div class="ui huge header">New Blog</div>
<form class="ui form" action="/blogs" method="POST">
<div class="field">
<label>Title</label>
<input type="text" name="blog[title]" placeholder="title">
</div>
<div class="field">
<label>Image</label>
<input type="text" name="blog[image]" placeholder="image">
</div>
<div class="field">
<label>Blog Content</label>
<textarea name="blog[body]"></textarea>
</div>
<input class="ui violet big basic button" type="submit">
</form>
</div>
On my blog routes page I have my create route as
router.post("/blogs", function(req, res){
// create blog
//req.body.blog.body = req.sanitize(req.body.blog.body);
var title = req.body.title;
var image = req.body.image;
var desc = req.body.body;
var author= {
id: req.user._id,
username: req.user.username
}
var newBlogpost = {title: title, image: image, body: desc, author: author}
Blog.create(newBlogpost, function(err, newBlog){
if(err){
res.render("blogs/new");
} else {
//then, redirect to the index
console.log(newBlogpost)
res.redirect("/blogs");
}
});
});
However when I sign in on the web application and complete the form and view my commmand line for the return of console.log(newBlogpost) after I submit, I get:
{ title: undefined,
image: undefined,
body: undefined,
author: { id: 5a0cbcc3d6c7070a7bb6c45e, username: 'cat' }
I am not sure why the new variable array I am creating has these three variable as undefinded and would appreciate help.
I am prototyping an emberjs app. I am facing a problem when I try to save the data.
My model:
App.User = DS.Model.extend({
id: DS.attr('string'),
name: DS.attr('string'),
description: DS.attr('string')
});
My Controller:
App.UsersAddController = Ember.ArrayController.extend({
actions: {
addUser: function () {
var name = this.get('name');
var description = this.get('description');
if (!name.trim() && !description.trim()) {
return;
}
var user = this.store.createRecord('user', {
'id': 'id-' + Math.random().toString(36).substr(2, 16),
'name': name,
'description': description
});
this.set('id', '');
this.set('name', '');
this.set('description', '');
user.save();
}
}
});
My template:
{{input type="text" placeholder="Name" value=name class="name-input"}}
{{input type="text" placeholder="Description" value=description class="name-input"}}
<button {{action 'addUser'}} class="submit">Submit</button>
The event bubbles up to the right controller. But fails to save. I am a beginner with emberjs. Please help me out.
You should remove id attribute from model definition.
App.User = DS.Model.extend({
// no id
name: DS.attr('string'),
description: DS.attr('string')
});
I am adding commenting to my ember app. When I submit a comment I want to add the object id of the current model as a foreign key to the comment object.
My html:
<div id="respond">
<h3>Leave a Comment</h3>
<form {{action leaveComment on="submit"}} class="form-inline">
<h5>Your Name:</h5>
{{input type="text" value=myname}}<br>
<h5>Your Email:</h5>
{{input type="text" value=myemail}}<br>
<h5>Comment:</h5>
{{textarea class="form-control" rows="3" type="text" value=mycomment}}<br>
<h5>Tool Id:</h5>
{{input type="text" value=id}}<br>
<button type="submit" class="btn btn-primary button-form">Save</button>
</form>
</div>
When I use this code, on the site my last input box contains the id of the active model (which is what I want) but when I post it, it is left blank. It is the only field that does not work.
Here is my js:
App.ViewController = Ember.Controller.extend({
name: null,
email: null,
comment: null,
toolid: null,
leaveComment: function() {
var comment = App.Comment.createRecord({
name: this.get('myname'),
email: this.get('myemail'),
comment: this.get('mycomment'),
toolid: this.get('id')
});
comment.save().then(function() {
this.transitionToRoute('view');
this.set('myname', '');
this.set('myemail', '');
this.set('mycomment', '');
this.set('id', '');
}.bind(this));
}
});
NOTE: Everything else posts fine.
EDIT: MODELS ADD on Request
App.Tool = DS.Model.extend({
expertise: DS.attr('string'),
name: DS.attr('string'),
link: DS.attr('string'),
description: DS.attr('string'),
rating: DS.attr('string')
});
App.Comment = DS.Model.extend({
name: DS.attr('string'),
email: DS.attr('string'),
comment: DS.attr('string'),
toolid: DS.attr('string')
});
You have two choices. It is a little hard to tell without seeing your routes.
1: If the model is associated with this controller, try this.get('model').get('id');
2: Access the model through a parent controller, i.e.
App.CommentController = Ember.Controller.extend({
needs: ['tool']
});
Then you can access the tool id via this.get('controllers.tool').get('model').
Note that this is dependent on your routes being nested.
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'});
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"
}]
}