Ember-data and #each - ember.js

I'm trying to follow the example in the Ember guide but with ember-data. Here is my router:
App.Router = Ember.Router.extend({
enableLogging: true,
root: Ember.Route.extend({
index: Ember.Route.extend({
route: '/',
redirectsTo: 'orders'
}),
orders: Ember.Route.extend({
route: '/orders',
connectOutlets: function(router) {
router.get('applicationController').connectOutlet('orders', App.store.findAll(App.Order));
}
}),
order: Ember.Route.extend({
route: '/orders/:ord_id'
})
})
});
And in my template:
{{#each order in controller}}
...
{{/each}}
I get this error: TypeError: 'undefined' is not an object (evaluating 'c.split')

It works using an ArrayController. I changed this:
App.OrdersController = Ember.Controller.extend();
to this:
App.OrdersController = Ember.Controller.extend();

Related

Emberjs how to create a new belongsTo item

I'm working on a simple todo app where each todo item belongs to a user. I'm getting this error:
Uncaught Error: Nothing handled the action 'createTodo'.
I think I'm missing a route and maybe a controller, but I'm not really sure what I need to do.
app/router.js:
import Ember from 'ember';
var Router = Ember.Router.extend({
location: TodoENV.locationType
});
Router.map(function() {
this.route('about');
this.resource('users', function() {
this.route('show', {path: ':user_id'});
});
});
export default Router;
app/routes/users/index.js:
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.find('user');
}
});
app/models/user.js:
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
todos: DS.hasMany('todo')
});
app/models/todo.js:
import DS from 'ember-data';
export default DS.Model.extend({
title: DS.attr('string'),
user: DS.belongsTo('user')
});
app/controllers/todo.js:
import Ember from 'ember';
export default Ember.ArrayController.extend({
actions: {
createTodo: function() {
var title = this.get('newTitle');
if (!title.trim()) { return; }
var todo = this.store.createRecord('todo', {
title: title // how do I get the user id?
});
this.set('newTitle', '');
todo.save();
}
}
});
app/templates/users/show.hbs:
<h4>{{name}}</h4>
<h5>Todos</h5>
{{input type="text" id="new-todo" placeholder="new todo"
value=newTitle action="createTodo"}}
<ul>
{{#each todos}}
<li>{{title}}</li>
{{/each}}
</ul>
The problem is createTodo is implemented in TodoController whereas you are using createTodo action in users/show template. Action is sent to the UsersShowController where createTodo is not implemented. Move createTodo action into UsersShowController and everything should be OK.

Ember.js Masonry rendering doesn't wait for Data Models to be generated

I am trying to build a masonry view of the top selling Items in a hypothetical eCommerce Site but Masonry is being rendered before the Data Models can be generated over RESTAdapter. Here are is my Ember.js code:
App.Userprofile = DS.Model.extend({
loggedIn: DS.attr('boolean'),
name: DS.attr('string'),
totalItems: DS.attr('number'),
});
App.ApplicationRoute = Ember.Route.extend({
setupController: function(controller) {
this.store.find('userprofile', 'bat#man.com').then (function(userprofile) {
controller.set ('model', userprofile);
});
}
});
App.ApplicationAdapter = DS.DjangoRESTAdapter.extend({
host: HOST,
namespace: 'api'
});
App.ApplicationView = Ember.View.extend({
elementId: '',
classNames: ['container','fullwidth'],
templateName: 'application'
});
App.Cloud = DS.Model.extend({
item: DS.attr('string'),
numberItems: DS.attr('number'),
rank: DS.attr('number')
});
App.CloudAdapter = DS.DjangoRESTAdapter.extend({
host: HOST,
namespace: 'api',
});
App.CloudController = Ember.ObjectController.extend({
needs: ['application'],
cloudSize: function() { // Determines the size of the div
var cloudsize = Math.round (this.get('model.numberItems') * 5 / this.get('controllers.application.totalItems')) + 1;
var divName = "score" + cloudsize.toString();
return divName;
}.property('model.numberItems', 'controllers.application.totalitems')
});
App.ItemcloudRoute = Ember.Route.extend({
setupController: function(controller) {
this.store.findAll('cloud').then (function(itemcloud) {
controller.set ('model', itemcloud);
});
}
});
App.ItemcloudController = Ember.ArrayController.extend({
needs: ['cloud', 'application'],
sortProperties: ['rank'],
});
App.ItemcloudView = Ember.View.extend({
elementId: 'magicgrid',
classNames: ['cloudcontainer'],
templateName: 'itemcloud',
didInsertElement: (function() {
this._super();
Ember.run.scheduleOnce('afterRender', this, this.applyMasonry);
}).observes('controller.itemcloud'),
applyMasonry: function() {
setTimeout( function() { // settimeout to ensure masonry is called after data models are generate
console.log ("applyMasonry being called");
$('#magicgrid').masonry({
itemSelector: '.company',
isAnimated: true
});
}, 2000);
}
});
Here is the portion of the template file where itemcloud is generated.
<script type="text/x-handlebars" data-template-name='itemcloud'>
{{#each controller.model itemController="cloud"}}
<div {{bind-attr class=":company cloudSize"}}>
<div class="companylogo">
<img src="images/logos/color-logos/logo-01.jpg" />
</div>
<div class="count">{{numberItems}}</div>
</div>
{{/each}}
<div class="clearfix"></div>
</script>
Now, I am struggling to find a way to hold the Masonry rendering until after the data is fetched due to the asynchronous nature of the data fetching and the template rendering. My research says that using a View for the CloudController Objects would be useful, but am trying to figure out if there is something I am missing in my current design. Also, if someone can provide pointers to use Views correctly here for the CloudController Objects
Let me know if I need to provide any more clarifications. Thanks!
if you doing it in the setupController Ember assumes the model is already ready and continues rendering the page despite the response not coming back from the server.
The easiest way to do it is to return your model/promise in the model hook. Ember will wait on rendering the page until the model has been resolved.
App.ItemcloudRoute = Ember.Route.extend({
model: function(){
this.store.find('cloud');
}
});
The code above will do the same thing your code was doing, except Ember will wait for the find to resolve before creating and setting the model on the controller.
As per kingpin2k comments updating the answer to reflect the working code:
App.ApplicationRoute = Ember.Route.extend({
model: function() {
return this.store.find ('userprofile', 'bat#man.com');
},
setupController: function(controller, model) {
controller.set ('model', model);
}
});

Attempting to create a profile page but App.User.find() errors

I have a simple app on fiddle http://jsfiddle.net/kitsunde/qzj2n/2/
<script type="text/x-handlebars">
{{outlet}}
</script>
<script type="text/x-handlebars" id="profile">
Profile Page
{{ email }}
</script>
Where I'm trying to display a profile page.
window.App = Ember.Application.create();
App.Router.map(function () {
this.resource('profile', {path: '/'});
});
App.User = DS.Model.extend({
email: DS.attr('string')
});
App.User.FIXTURES = [
{
id: 1,
email: 'herpyderp#gmail.com'
}
];
App.ProfileRoute = Ember.Route.extend({
model: function(){
return App.User.find().get('firstObject');
}
});
But I'm getting an exception:
Error while loading route: TypeError: undefined is not a function
What am I missing?
There are a few things missing. You can find the fixed fiddle here:
http://jsfiddle.net/47cHy/
window.App = Ember.Application.create();
App.ApplicationAdapter = DS.FixtureAdapter;
App.Router.map(function () {
this.resource('profile', { path: '/' });
});
App.User = DS.Model.extend({
email: DS.attr('string')
});
App.User.FIXTURES = [
{
id: 1,
email: 'herpyderp#gmail.com'
}
];
App.ProfileRoute = Ember.Route.extend({
model: function() {
return this.store.find('user').then(function(users) {
return users.get('firstObject');
});
}
});
Your template had the id index and not the name of the route profile
You have to tell Ember specifically to use the fixture adapter.
You accessed the model directly via the global object. You should let Ember do the work via the internal resolver and use this.store.find.
.find() returns a promise. You should get the first object in the then handler and return it there.

Why is "Assertion failed: You can only add a 'story' record to this relationship" is being thrown?

I don't think I understand how to set a belongsTo relationship. I'm using
ember-1.1.2, and ember data beta3. Any help appreciated.
The relationship definitions:
App.Story = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
setting: DS.attr('string'),
status: DS.attr('string'),
chapters: DS.hasMany('chapter',{async: true}),
cast: DS.hasMany('actor', {async: true})
});
App.Chapter = DS.Model.extend({
name: DS.attr('string'),
number: DS.attr('number'),
description: DS.attr('string'),
story: DS.belongsTo('story'),
scenes: DS.hasMany('scene',{async: true})
});
The routes:
this.resource('story', {path: '/story'}, function() {
this.route('edit', {path: '/:story_id'})
this.route('new')
this.resource('chapter', {path:"/:story_id/chapter"}, function() {
this.route('edit', {path: '/:chapter_id/edit'})
this.route('new')
this.resource('scene', {path:":chapter_id/scene"}, function() {
this.route('edit', {path: '/:scene_id/edit'})
this.route('new')
})
})
})
Where the error occurs:
App.ChapterNewRoute = Ember.Route.extend({
setupController: function( controller, model) {
this.controllerFor('chapter.edit').setProperties({isNew:true, content:model})
},
model: function(params) {
var chapter = this.store.createRecord('chapter')
this.store.find('story', params.story_id).then(function( story) {
chapter.set('story', story) //ERROR HAPPENS HERE
story.get('chapters').push(chapter)
})
return chapter
},
renderTemplate: function() {
this.render('chapter.edit')
}
})
story_id doesn't exist in that route's model hook, it only lives on the ChapterRoute (aka story_id is undefined and you are probably not getting a story). You can use modelFor to get the model from the chapter's route and get the from that model if it exists.
Here's a jsbin showing it working
http://emberjs.jsbin.com/OxIDiVU/1/edit

Ember.js - Cannot render nested models

Have an upcoming weekend project and using it to evaluate Ember.js and I cannot figure out why I cannot display nested objects in my template. This does not work:
{{#each emails}}
{{email_address}}
{{/each}}
When I try just {{emails}} I get a hint that something is right:
Models:
App.Contact = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
company: DS.attr('string'),
emails: DS.hasMany('App.Email')
});
App.Email = DS.Model.extend({
contact: DS.belongsTo('App.Contact'),
emailAddress: DS.attr('string'),
});
Route:
App.Router.map(function() {
this.resource('contacts', function() {
this.resource('contact', {path: ':contact_id'});
});
});
App.ContactsRoute = Ember.Route.extend({
init: function() {},
model: function() {
return App.Contact.find();
}
});
App.ContactRoute = Ember.Route.extend({
model: function(params) {
return App.Contact.find(params.contact_id);
}
});
I have no idea what to try next. I'm using active_model_serializer in Rails. I've tried embedding, side-loading to no avail. I'm sure it's something simple that I'm missing.Thanks in advance!
When using the each helper it's recomendable to be more specific about the items you are looping over to avoid such problems.
Try the following:
{{#each email in model.emails}}
{{email.emailAddress}}
{{/each}}
This should also work:
{{#each emails}}
{{this.emailAddress}}
{{/each}}
And also, your model property is called emailAddress and not email_address.
Hope it helps.