EmberJS Error message: Assertion Failed: Cannot find model name - ember.js

I am in the mids of following: Vic Ramon EmberJS tutorial. I reached the point where I am trying to list the leads and have the following setup:
Adapter
App.Store = DS.Store.extend()
App.ApplicationAdapter = DS.ActiveModelAdapter.extend()
DS.RESTAdapter.reopen
namespace: 'api/v1'
Controller
App.LeadsController = Ember.ArrayController.extend
sortProperties: ['firstName', 'lastName']
Model
App.Lead = DS.Model.extend
firstName: DS.attr('string')
lastName: DS.attr('string')
email: DS.attr('string')
phone: DS.attr('string')
status: DS.attr('string', defaultValue: 'new')
notes: DS.attr('string')
fullName: (->
#get('firstName') + ' ' + #get('lastName')
).property('firstName', 'lastName')
Routes
App.LeadsRoute = Ember.Route.extend
model: ->
#store.findAll 'lead'
Router
App.Router.reopen
location: 'auto'
rootURL: '/'
App.Router.map ()->
#resource 'leads', path: '/'
#resource 'lead', path: 'leads/:id'
Template
<article>
<h1>Leads</h1>
<ul>
{{#each lead in controller}}
<li>{{#link-to 'lead' lead}}{{/link-to}}</li>
{{lead.fullName}}
{{/each}}
</ul>
</article>
The issue simply is when I redirect my browser to http://localhost:3000 it shows me the list of fullNames but doesn't show me the anchor links. Also another thing I seem to get in my chrome browser console is:
Error message: Assertion Failed: Cannot find model name
Bonus Question
From reading a few books an tutorials I understand that you can include mixins as a way of injecting additional properties into Controllers or Views. So for example you can do:
Ember.View.extend(App.Editable)
But can you include a mixin into a model? As in can you do:
App.Lead = DS.Model.extend(App.SomeMixin)

Here is working jsbin for Ember 1.12 jsbin.com/mezakepemu/1/edit?html,js,console
1) Links. You could try
<li>
{{#link-to 'lead' lead}}
{{lead.fullName}}
{{/link-to}}
</li>
or
<li>{{link-to lead.fullName 'lead' lead}}</li>
And in modern Ember versions it's better to write
{{#each lead in model}} ... {{/each}}
or
{{#each model as |lead|}} ... {{/each}}
2) Error message: Assertion Failed: Cannot find model name - please show the code where this error is thrown. Your leads route, controller and template looks normal.
3) Yes, mixins can be included in models and in every Ember.Object.

Related

ember-data: Inheritance in Parent-Child resources

Let's say this is my router setup -
router.js :
App.Router.map(function(){
this.resource('photos', function(){
this.resource('photo', {path: '/:photo_id'}, function(){
//this.route('edit');
});
});
});
photo.js:
App.Photo = DS.Model.extend({
path: DS.attr('string'),
resolution: DS.attr('string'),
author: DS.belongsTo('user'),
dateTaken: DS.attr('date'),
aperture: DS.attr('string'),
focalLength: DS.attr('string'),
.
.
.
exposure: DS.attr('string')
});
photosRoute.js:
App.PhotosRoute = Ember.Route.extend({
model: function(){
return this.store.find('photo');
}
});
photos.hbs:
<div class="container">
<div class="row">
{{#each photo in controller}}
<div class="col-md-4">
{{#link-to 'photo' photo}}{{photo.path}}{{/link-to}} <br />
By: {{photo.author}} <br />
</div>
{{/each}}
</div>
</div>
{{outlet}}
As seen above, I am only using {{photo.path}} and {{photo.author}} in the photos.hbs template to show the list of all photos. However, in this setup a call to /#/photos would fetch all the bunch of fields for every photo from my django REST server - which I am not interested. Is there a way to fetch just a few fields from the photo model for /#/photos and the complete photo model only when I click on individual photo i.e. /#/photos/photo_id
I have a tried a couple of things:
Created a new child resource called 'photoDetail' that extends the original 'photo'. Ideally this should replace the singular 'photo'.
From what I gather {async: true} property holds only for async fetching btween models that have relationships setup between them - but not for individual fields in the photo model like: 'exposure', 'focalLength'.
Any help would be greatly appreciated.
No, there's no partially loaded models in Ember-Data. (In fact, I don't think any of the big Ember-Data alternatives have that feature). For the most part your models should be small enough where loading all of the records won't really matter. By the time you enable GZIP, you probably won't even notice a difference. If you have a special use case where bandwidth is extremely limited, you'll probably just want to write your own persistence library. (You could probably also modify Ember-Data or abuse some of its features to accomplish the same task, but I'd recommend against it.)

Ember JS Deep Linking

I have an Ember JS 1.5.1 app with ember-data 1.0.8 beta. There are TWO simple compiled templates the relevant parts are:
index
<div class="container-fluid">
<div class="col-md-2 sidebar">
<ul class="nav nav-sidebar">
{{#each model}}
<li>
{{#link-to 'activities' this}}{{name}}{{/link-to}}
</li>
{{/each}}
</ul>
</div>
<div class="col-md-10 col-md-offset-2">
{{outlet}}
</div>
</div>
activities
<div>
<ul>
{{#each model.activities}}
<div class="row">
<p>activity {{id}} is {{name}}</p>
</div>
{{/each}}
</ul>
</div>
The application is also simple, reduced to a few bits of fixture data and some route functions:
window.App = Ember.Application.create();
App.ApplicationAdapter = DS.FixtureAdapter;
App.Router.map( function(){
this.resource('index', {path: '/'}, function(){
this.resource('activities', { path:':name'}, function(){
this.resource('activity');
});
});
});
App.IndexRoute = Ember.Route.extend({
model: function(){
return this.store.find('role');
}
});
App.ActivitiesRoute = Ember.Route.extend({
model: function(params){
var roles = this.modelFor('index');
return roles.findBy('name', params.name).get('activites');
}
});
App.Role = DS.Model.extend({
name: DS.attr('string'),
activities: DS.hasMany('activity', {async:true} )
});
App.Activity = DS.Model.extend({
name: DS.attr('string')
});
App.Role.FIXTURES = [{
id: 1,
name: 'Management',
activities: [1]
},{
id: 2,
name: 'Analysis',
activities: [1,2]
},{
id: 3,
name: 'Development',
activities: [2]
}]
App.Activity.FIXTURES = [{
id: 1,
name: 'talking'
},{
id: 2,
name: 'doing'
}];
What I get when I navigate to localhost is a simple list of the three roles on the left hand side of the screen and nothing on the right hand side. (as expected)
When I then select a link (such as 'Analysis') the outlet on the right hand side fills with the expected list of two activity names "talking" and "doing".
LHS list RHS pane
========== ========
Management talking
Analysis doing
Development
So far so good.
I noticed that when I hovered over the 'Analysis' link the browser shows the url below as expected
localhost:/#/Analysis
However when I cut and paste this url into the browser address bar directly I only get the left hand side list of links and nothing in the main window. The list of "talking" and "doing" does no appear. There are no errors shown in the browser and ember does not raise and exceptions.
How do you get this simple nested route to refresh all the contents when you directly deep link rather than having to navigate from the root all the time?
When you use link-to and pass it the model, it will skip the model hook supplying the model from the link-to to the route. If you refresh the page, it will hit each route down the tree until it's fetched the models for each resource/route necessary to fulfill the request. So if we look at your routes one at a time it will do this:
Hit the application route, fetch its model if it exists (application route is the root of every Ember app).
Hit your index route, where it will return App.Role.find()
Hit your activites route, where it will return App.Activity.find()
Number 3 is where you real issue lies. Regardless of whether or not that part of the url says Analysis, Management, or Development you will already return App.Activity.find(). You've defined the dynamic slug :name, ember will parse the appropriate part of the url, and pass that part is as an object, in the case of Analysis Ember will pass in { name: 'Analysis' } to your model hook. You will want to take advantage of this, to return the correct model.
App.ActivitiesRoute = Ember.Route.extend({
model: function(params){
var roles = this.modelFor('index');
return roles.findBy('name', params.name);
}
});
Additionally you are using a fairly old version of Ember Data. Here's a small example of how Ember Data should be used with newer versions: http://emberjs.jsbin.com/OxIDiVU/617/edit
As you can see, you no longer declare the store. Additionally you may run into trouble with what would be considered async properties, and might want to read https://github.com/emberjs/data/blob/master/TRANSITION.md

Ember -- TypeError: arrangedContent.addArrayObserver is not a function

I am trying to build a simple category browser with ember. I have two very simple views. When the user visits / they will see a list of all categories and when they click a category in that list they will be directed to #/10 where 10 is the id.
My problem is that when a user clicks on a category at the / route I am getting the following error
TypeError: arrangedContent.addArrayObserver is not a function
[Break On This Error]
didChange: 'arrangedContentArrayDidChange'
If I refresh the page at the #/10 route the proper api call is made to my backend /api/categories?parent=99. What could I be doing wrong that is throwing this error during the transition? A full example of my code is below.
Templates:
<script type="text/x-handlebars">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="categories">
{{#each category in controller}}
<p>{{#linkTo 'category' category}}{{ category.name }}{{/linkTo}}</p>
{{/each}}
</script>
<!--this is an array instead of object -->
<script type="text/x-handlebars" data-template-name="category">
{{#each category in controller}}
<p>{{category.name}}</p>
{{/each}}
</script>
Javascript:
var App = Ember.Application.create();
App.Router.map(function(){
this.resource('categories', { path : '/' });
this.resource('category', { path : '/:category_id' });
});
App.CategoriesRoute = Ember.Route.extend({
model: function(){
return App.Category.find();
}
});
//this is causing the error possibly
App.CategoryRoute = Ember.Route.extend({
model: function(params){
return App.Category.find({parent: params.category_id});
}
});
App.CategoryController = Ember.ArrayController.extend();
// Models
App.Store = DS.Store.extend({
revision: 11,
adapter: 'DS.RESTAdapter'
});
DS.RESTAdapter.configure("plurals", {
category: "categories"
});
App.Category = DS.Model.extend({
name: DS.attr('string'),
parent_id: DS.attr('number')
});
Debug info:
DEBUG: Ember.VERSION : 1.0.0-rc.1
DEBUG: Handlebars.VERSION : 1.0.0-rc.3
DEBUG: jQuery.VERSION : 1.9.0
Hint: After writing this i realized that you probably did not get the model hook right. This hook is called when you are entering your app via url. It converts the URL into an appropriate model and transition with this model into the Route. I guess you thought that this model() hook would be called with the arguments of {{#linkTo}}? This is not the case!
This does not work because you are passing a single model to your #linkTo helper in your template. So Ember wants to set this single object as content of your ArrayController. This causes your error. And your model hook returns an array. Rule of Thumb: You should always pass the same data structure to #linkTo, which you are returning in your model hook.
Therefore i would suggest to use an event instead of linkTo and do the following:
<script type="text/x-handlebars" data-template-name="categories">
{{#each category in controller}}
<p {{action 'showParentCategory' category}}>{{category.name}}</p>
{{/each}}
</script>
App.CategoriesRoute = Ember.Route.extend({
model: function(){
return App.Category.find();
},
events: {
showParentCategory : function(parentCategory){
var cats = App.Category.find({parent: parentCategory.get("category_id")});
this.transitionTo("category", cats);
}
}
});
What have i done here?
I created an action called "showParentCategory".
As this is an action with is about routing, i am handling this event in your CategoriesRoute. As you see, events/action handlers are declared in the events property of your route.
I am performing the same logic there as in your model hook and then i am calling manually the transitinTo with the fetched categories.
UPDATE: How to serialize
By implementing serialize, you are telling Ember what to put into your url.
App.CategoryRoute = Ember.Route.extend({
model: function(params){
return App.Category.find({parent: params.category_id});
},
serialize : function(models){
var first = models.objectAt(0);
return {
category_id : first.get("parentId")
}
}
});
If you do #each over a numeric value instead of doing it on an array content in your template, this issue occurs.
I had a numeric value count in my 'poll' model . I was iterating like,
{{#each poll in content.count}}
{{/each}}
I think, we have to use #each only on ember arrays.

Has no method 'addArrayObserver' during {{#linkTo}} click

Hey not sure if anyone can help me, but I have been struggling with this error for a long time:
"Uncaught TypeError: Object <App.AssetType:ember408:2> has no method 'addArrayObserver'"
Here is the template with the {{#linkTo}}'s that produce this error when clicked
<div class="row">
<div class="twelve columns">
<h2>{{title}} - Assets</h2>
</div>
</div>
<div class="row">
<div class="three columns">
<ul>
{{#each assetTypes}}
{{#linkTo 'product.filter' this}}{{title}}{{/linkTo}}
{{/each}}
</ul>
</div>
<div class="nine columns">
{{outlet}}
</div>
</div>
and the Application code
window.App = Ember.Application.create
rootElement: '.solution_products_documents'
App.ApplicationRoute = Ember.Route.extend
model: ->
App.Product.find()
App.ApplicationController = Ember.ArrayController.extend
sortProperties: ['title']
App.ProductRoute = Ember.Route.extend
model: (params) ->
App.Product.find params.product_id
setupController: (controller, model) ->
controller.set 'documents', model.get 'document_ids'
App.ProductController = Ember.ObjectController.extend
assetTypes: (->
docs = #get('documents')
docs.getEach 'asset_type_id'
).property('documents')
App.ProductFilterRoute = Ember.Route.extend
model: (params) ->
type = App.AssetType.find params.asset_type_id
product = this.modelFor 'product'
docs = product.get 'document_ids'
model = docs.filterProperty 'asset_type_id', type
App.ProductFilterController = Ember.ArrayController.extend()
App.Router.map ->
#route 'index', { path: '/' }
#resource 'product', { path: '/products/:product_id' }, ->
#route 'filter', { path: '/filter-by/:asset_type_id' }
##
# MODELS / EMBER-DATA
##
serializer = DS.JSONSerializer.create()
serializer.configure 'App.Document',
sideloadAs: 'documents'
serializer.configure 'App.AssetType',
sideloadAs: 'asset_types'
serializer.configure 'App.Product',
sideloadAs: 'products'
App.RestAdaptor = DS.RESTAdapter.extend
serializer: serializer
namespace: URL.slice 1
DS.Store.extend(
adapter: App.RestAdaptor
revision: 11
).create()
App.Product = DS.Model.extend
title: DS.attr 'string'
document_ids: DS.hasMany 'App.Document'
App.Document = DS.Model.extend
title: DS.attr 'string'
product_id: DS.belongsTo 'App.Product'
asset_type_id: DS.belongsTo 'App.AssetType'
App.AssetType = DS.Model.extend
title: DS.attr 'string'
document_ids: DS.hasMany 'App.Document'
######### /> END MODELS #################
Everything works as planned if I put the URL #/products/4/filter-by/2 into the address bar. It's only when I click the {{#linkTo}}'s that I get this error and the content is not displayed. The error is thrown before it get's to the App.ProductFilterRoute because the debugger statement in the route is not executed, but it is on page refresh.
Any help or direction is greatly appreciated, as I don't really know where to look.
UPDATE:
If I do not use the {{#linkTo}} helper and instead manually construct the url
{{title}}
everything works fine. What is different between the linkTo and manual href?
The error basically says that Ember expects an Array, when you navigate to the ProductFilterRoute.
Why does Ember expect an Array here?
The Controller for your Route (ProductFilterController) is of type ArrayController.
I am not very familiar with coffeescript, but your model hook seems to return an array too. Important Notice: The model hook is just executed when entering your App via Url. (This is why your manual navigation by Url and the href both work. Cite from EmberDoc: "A hook you can implement to convert the URL into the model for this route.")
Why is the error thrown?
So your route revolves around an array. You are passing just a plain object. So the golden rule is: Pass the same data structure (an array in this case) to your {{linkTo}} helper, which is returned by your model hook implementation.
A possible solution:
Use an action instead of {{linkTo}}
Implement an action in your route that finds all document with the given asset_type and pass it to your route.
Modifiations to template:
<a {{action 'filterProductByAsset' this}}> {{title}} </a>
Extensions to ProductFilterRoute:
events:{
filterProductByAsset : function(assetTypeId){
type = App.AssetType.find(asset_type_id);
product = this.modelFor('product');
docs = product.get('document_ids');
models = docs.filterProperty('asset_type_id', type);
this.transitionTo("product.filter", models)
}
}
I suspect this is the problem:
assetTypes: (->
docs = #get('documents')
docs.getEach 'asset_type_id'
).property('documents')
this looks like it will produce an array like this:
[1,2,3,4,5]
When really you need an array of objects that respond to id, e.g.
object1 = Em.Object.create id: 1
object2 = Em.Object.create id: 2
[object1, object2] #etc
If you want to change this behaviour, you will need to look into the serialize hook of the route you are linking to, in this case product.filter
With regard to {{linkTo}} vs. manually created links, the linkTo helper is js enabled, you should always use it instead of a manual link. This becomes more of a real problem when using the HistoryLocation / pushState support as it forces a full page reload.
There is another problem here: you can't observe documents like this. You must use something like property('documents.#each')

Clear Ember.TextField after submit

I'm learning ember.js on a small example app and there is a final piece I can't get to work.
I have a list of "quips" (tweets) and there is a text input field that allows to create a new one. After I submit a new tweet, I want to clear the text input, to no avail. I basically copied the todomvc example verbatim at this point and it works there (I even use the same ember.js and ember-data.js versions just to rule out this possibility).
Here is the template:
<script type="text/x-handlebars" data-template-name="index">
<h2>Latest Quips</h2>
<div>
{{view Ember.TextField id="new-quip" placeholder="Enter your Quip"
valueBinding="newQuip" action="createQuip"}}
</div>
The action in the appropriate controller:
App.IndexController = Ember.ArrayController.extend({
createQuip: function() {
App.Quip.createRecord({
text: this.get('newQuip'),
user: 'ree'
});
this.set('newQuip', ''); // this row should clear the view
this.get('store').commit();
}
});
And the model for the sake of completeness:
App.Quip = DS.Model.extend({
text: DS.attr('string'),
user: DS.attr('string')
});
App.Store = DS.Store.extend({
revision: 11,
adapter: 'App.QuipsAdapter'
});
App.Quip.FIXTURES = [
{ id: 1, user: 'ree', text: 'Which is the best JS MVC?' },
{ id: 2, user: 'baaz', text: '#ree Definitely Ember.js!' }
];
App.QuipsAdapter = DS.FixtureAdapter.extend({});
The app runs here.
I would be really glad if someone could point at what I'm doing wrong.
Thank you,
Balint
It's a bug related to jQuery 1.9.0 - try v1.8.x
Also, as I can recall, it's been fixed on master, so grabbing the latest Ember release may also solve your problem.