Finding a model in ember.js - ember.js

I'm completely new to ember js. I've downloaded the last rc 2 version of ember, ember-data.js 12 revision, and looked at the manual and copy-past this code in order to be able to see the GET request to my server:
App = Ember.Application.create();
App.Router.map(function() {
... some resources...
});
App.Store = DS.Store.extend({
revision: 12 // Default is the REST Adapter
});
App.Person = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
birthday: DS.attr('date'),
fullName: function() {
return this.get('firstName') + ' ' + this.get('lastName');
}.property('firstName', 'lastName')
});
var person = App.Person.find(1);
And I get the next error:
Uncaught Error: assertion failed: Your application does not have a
'Store' property defined. Attempts to call 'find' on model classes
will fail. Please provide one as with 'YourAppName.Store =
DS.Store.extend()'
But as I understand I defined the Store property. Maybe I missed something because I havent read the whole manual, but honestly I can't see what's wrong. As I get it after this code I'll see get /post/1/ request to my server, and it should be an amazing thing, but I'm still struggling with this error

This is happening because ember applications are initialized asynchronously. In general you just define classes when js is being loaded, executable code belongs in hooks/callbacks. Mostly you will be using model find() from the model hook on your routes, but if you really need to do something like this right away you can do this:
App.then(function() {
console.log('App has been initialized...');
var person = App.Person.find(1);
});
If you want to experiment with this approach try this jsfiddle which demonstrates using App.then() with the ember-data fixture adapter based on the getting started screencast

Related

Ember Data: can't get a model from data store

I have tried to populate a template with Ember Data.
I'm getting a weird problem when I try to find a model inside my DS Store.
I've followed some tutorials but got an irritating error.
The error is 'Error while loading route: undefined'.
What I've tried:
MovieTracker.Store = DS.Store.extend({
url: 'http://addressbook-api.herokuapp.com'
});
MovieTracker.Contact = DS.Model.extend({
first: DS.attr('string'),
last: DS.attr('string'),
avatar: DS.attr('string')
});
MovieTracker.Router.map(function() {
this.resource('contacts');
});
MovieTracker.ContactsRoute = Ember.Route.extend({
model: function(){//works when changing to 'activate:'
//return; //this works! it shows me a simple template and updates URL to index.html#/contacts
return this.store.find('contact');//error: 'Error while loading route: undefined'
}
});
In the Index.html I have a simple #link-to to 'contacts' (application handlebar), it works well.
I have also a simple template called contacts, which works fine when I give up the this.store.find('contact') line.
JSBin: http://emberjs.jsbin.com/OxIDiVU/170/edit?html,js,output
The JSON is in: http://addressbook-api.herokuapp.com/contacts
Can you please give me any advice?
Would you prefer Ember Data at all (1.0 Beta 5).
Another question: a website without precompiling the handlebars is not gonna be a good idea?
Thank you a lot for reading!
When defining the host you define that on the adapter, not the store.
MovieTracker.ApplicationAdapter = DS.RESTAdapter.extend({
host: 'http://addressbook-api.herokuapp.com'
});
Additionally, you shouldn't define the id on the model, it's there by default
MovieTracker.Contact = DS.Model.extend({
first: DS.attr('string'),
last: DS.attr('string'),
avatar: DS.attr('string')
});
http://emberjs.jsbin.com/OxIDiVU/172/edit
And the newer versions of ember data aren't documented on the website yet, but the transition document should help explain some of the nuances and changes.
https://github.com/emberjs/data/blob/master/TRANSITION.md

How to save changes to Ember.js model without breaking its relationships

I am using the latest version of Ember-data (v1.0.0-beta.2)
I have a problem updating an Ember.JS model. I reduced this case to most simplistic model
App.Post = DS.Model.extend({
subject: DS.attr('string'),
author: DS.belongsTo('user')
});
App.User = DS.Model.extend({
name: DS.attr('string'),
msg: DS.attr('string')
});
I save it using .save() method, and it successfully resolves the promise. I've added some logging to show the problem
App.IndexController = Ember.ObjectController.extend({
actions: {
save: function() {
console.log("Author name before: " + this.get("model").get("author").get("name"));
this.get("model").save().then(function(m) {
console.log("Author object after: " + m.get("author"));
console.log("Author name after: " + m.get("author").get("name"));
});
}
}
});
After saving, the author property becomes just a number 1, not an object.
The JSON returned by the server is always the same. To be able to demonstrate this on JSbin, I had to overwrite DS.RESTAdapter.ajax.
http://jsbin.com/EWUSEkA/3/edit?html,js,output
The question is: How to make Ember sideload or by other means resolve object relationships after saving the object?
This is a known issue with beta 2. The bug report has some suggested workarounds.
https://github.com/emberjs/data/issues/1228
Hopefully it'll be fixed in beta 3.

How to get model properties

I have model:
App.Item = DS.Model.extend({
itemId: DS.attr('string'),
itemName: DS.attr('string'),
itemType: DS.attr('string'),
});
I successfully create some items from JSON. I can put them to page by {{#each items}}{{ itemName}}{{/each}}. But I don't know, how to get itemName in javascript.
I tried this:
var item = App.Item.find(1);
console.log(item.itemName);
--> undefined
I can't find anything useful from emberjs and ember-data docs. Can anyone help me?
Thanks
I tried this:
var item = App.Item.find(1);
console.log(item.itemName);
--> undefined
This is normal because the call to .find(1); is asyncronous and returns a promise and not the item you are expecting.
Therefore you should try:
App.Item.find(1).then(function(result) {
console.log(record.get('itemName'));
});
It also depends from where you are doing App.Item.find() if it's from inside a route you should wait until the afterModel hook is called to access your items:
App.FooRoute = Ember.Route.extend({
model: function() {
return App.Item.find(1);
},
afterModel: function(record) {
console.log(record.get('itemName'));
}
});
Also be aware that if you where calling find() without parameter then you will receive a RecordArray which you need to loop over to get access to your items. Also worth mentioning is that in ember you should always use .get() and .set() instead of the vanilla dot-notation otherwise you hijack the binding mecanism resulting in no updates in your view etc.
Note, if you are using the latest ember.js release (1.0.0) then the call to .find() should be made somewhat different. But that's not clear from your question.
Hope it helps.

ember.js cannot set property 'store' of undefined

I have been trying to set up an Ember.js application together with a RESTful API i have created in Laravel.
I have encountered a problem trying to get the data trough the store, and depending on my implementation, I get different errors, but never any working implementations.
The ember.js guide have one example, other places have other examples, and most information I find is outdated.
Here's my current code:
App = Ember.Application.create();
App.Router.map(function() {
this.resource("world", function() {
this.resource("planets");
});
});
App.PlanetsRoute = Ember.Route.extend({
model: function() {
return this.store.find('planet');
}
});
App.Planet = DS.Model.extend({
id: DS.attr('number'),
name: DS.attr('string'),
subjectId: DS.attr('number')
});
And when I try to click the link for planets, thats when the error occurs, and I get the following error right now:
Error while loading route: TypeError {} ember-1.0.0.js:394
Uncaught TypeError: Cannot set property 'store' of undefined emberdata.js:15
No request is sent for /planets at all. I had it working with a $.getJSON, but I wanted to try to implement the default ember-data RESTAdapter.
For reference, these are some of the implementations i've tried:
var store = this.get('store'); // or just this.get('store').find('planet')
return store.find('planet', 1) // (or findAl()) of store.findAll('planet');
App.store = DS.Store.create();
I also tried DS.Store.all('planet') as I found it in the ember.js api, but seemed like I ended up even further away from a solution.
Most other implementations give me an error telling me there is no such method find or findAll.
EDIT (Solution)
After alot of back and forward, I managed to make it work.
I'm not sure exactly which step fixed it, but I included the newest versions available from the web (Instead of locally), and the sourcecode now looks like this:
window.App = Ember.Application.create();
App.Router.map(function() {
this.resource("world", function() {
this.resource("planets");
});
});
App.PlanetsRoute = Ember.Route.extend({
model: function() {
return this.store.findAll('planet');
}
});
App.Planet = DS.Model.extend({
name: DS.attr(),
subjectId: DS.attr()
});
The error you had is probably due to the fact that you added a "s" plural of your objects.
i.e. if you use
App.Planets = DS.Model.extend({
})
you would get that error.

Ember todos: an Ember.CollectionView's content must implement Ember.Array

I'm trying to get my head around Ember and going through the todos tutorial. I get stuck on the displaying-model-data step here
http://emberjs.com/guides/getting-started/displaying-model-data/
here's the javascript i copied and pasted from the tutorial:
window.Todos = Ember.Application.create();
Todos.Router.map(function () {
this.resource('todos', { path: '/' });
});
Todos.TodosRoute = Ember.Route.extend({
model: function () {
return Todos.Todo.find();
}
});
Todos.Store = DS.Store.extend({
revision: 12,
adapter: 'DS.FixtureAdapter'
});
Todos.Todo = DS.Model.extend({
title: DS.attr('string'),
isCompleted: DS.attr('boolean')
});
Todos.Todo.FIXTURES = [
{
id: 1,
title: 'Learn Ember.js',
isCompleted: true
},
{
id: 2,
title: '...',
isCompleted: false
},
{
id: 3,
title: 'Profit!',
isCompleted: false
}
];
Then here's my handlebars template:
...
{{#each controller}}
<li>
<input type="checkbox" class="toggle">
<label>{{title}}</label><button class="destroy"></button>
</li>
{{/each}}
And yet I get this error
Uncaught Error: assertion failed: an Ember.CollectionView's content must implement Ember.Array. You passed <(generated todos controller):ember257>
It looks to me like whatever default controller object Ember generates should be of type Ember.Array but it is not happening for some reason. I am wondering if it is a problem with ember-data?
I am using all the files from the starter kit which are
ember 1.0.0 rc5
handlebars 1.0.0 rc4
jquery 1.9.1
and ember-data, the only versioning indication i can tell is from a comment
// Last commit: 3981a7c (2013-05-28 05:00:14 -0700)
Is there a dependency problem someone knows about or did I do something wrong?
I wouldn't say its a problem with ember data, since that module is responsible only for talking to the api and giving you clever model objects.
You were right in saying ember is generating the wrong type of controller. By default Ember will probably generate a Controller, when what you need is an ArrayController. To get around the issue, simply create an empty controller like this
Todo.TodosController = Em.ArrayController.extend({});
The guide does say that ember creates an ArrayController, but perhaps it doesn't anymore!? let me know if it works by explicitly creating an arraycontroller. If it does we can let the ember team know.
I had this exact same issue today walking through the Getting Started Guide but it appeared to be due to a typo.
According to the documentation, the generated controller is supposed to be of type ArrayController. I dug into the Ember source and found the Ember.generateController method that generates the controller depending on the context. I set a break point and found that when Ember was trying to create a controller for the "Todos" route, the context was undefined, so the basic controller was generated.
Working backward from there, I set a breakpoint on the model function of my router to see what it was returning but found it was not being called at all. At this point, I began to get suspicious that I had done something wrong. And that is when I noticed that I had named the TodosRoute as TodosRouter (as you have in your original question). Changing the name to TodosRoute correctly called my model function and everything worked as expected. It was not necessary to include the line that explicitly created the TodosController as an ArrayController.
While it appears you had it correct in your question, I wanted to post this here in case someone else has the same issue.
Adding the line Gevious suggested corrected this issue for me. For clarification my router.js file now looks like this:
Todos.Router.map(function(){
this.resource('todos', {path: '/'});
});
Todos.TodosRoute = Ember.Route.extend({
model: function () {
return Todos.Todo.find();
}
});
Todos.TodosController = Em.ArrayController.extend({});