store for model is undefined - javascript

I'm trying to write a frontend with ember.js and ember-data for a REST service. The server returns the data (I do see this using fiddler) but I always get the error Unable to set property 'store' of undefined or null reference. My JS code:
window.Cube = Ember.Application.create({
LOG_TRANSITIONS: true,
LOG_TRANSITIONS_INTERNAL: true
});
var attr = DS.attr;
Cube.Subject = DS.Model.extend({
name: attr(),
change_date: attr(),
create_date: attr()
});
Cube.ApplicationAdapter = DS.RESTAdapter.extend({
namespace: 'backend/v1/api',
host: 'http://localhost:58721'
});
Cube.Store = DS.Store.extend({
revision: 12,
url: "http://localhost:58721",
adapter: Cube.ApplicationAdapter
});
Cube.IndexRoute = Ember.Route.extend({
model: function (params) {
var store = this.get('store');
return store.findAll('Subject');
}
});
The error originates in ember-data.js:
modelFor: function(key) {
if (typeof key !== 'string') {
return key;
}
var factory = this.container.lookupFactory('model:'+key);
Ember.assert("No model was found for '" + key + "'", factory);
factory.store = this; // error here
factory.typeKey = key;
return factory;
}
As far as I understand ember, the store should be automatically set, but it is always null.
How to define the model, so the store is available? What am I missing?
Update 1:
Updated ember. Now I use the following versions:
DEBUG: Ember : 1.1.0
DEBUG: Ember Data : 1.0.0-beta.3
DEBUG: Handlebars : 1.0.0
DEBUG: jQuery : 1.9.1
No I get the following errors in the console:
No model was found for 'nextObject'
Error while loading route: TypeError: Unable to set property 'store' of undefined or null reference

subject should be lower case, additionally findAll is an internal method, you should be using find with no additional parameters (which then calls findAll).
Cube.IndexRoute = Ember.Route.extend({
model: function (params) {
var store = this.get('store');
return store.find('subject');
}
});

Related

ember-cli data returned empty using initializer

I have an app where we need to create an initializer that inject our global into all the route where our global is a function that load data from a JSON file and return the data.
global-variable.js
export function initialize(container, application) {
var systemSetting = {
systemJSON: function(){
return Ember.$.getJSON("system/system.json").then(function(data){
return data
});
}.property()
};
application.register('systemSetting:main', systemSetting, {instantiate: false});
application.inject('route', 'systemSetting', 'systemSetting:main');
}
export default {
name: 'global-variable',
initialize: initialize
};
index.js - route
export default Ember.Route.extend({
activate: function(){
var _settings = self.systemSetting.systemJSON;
console.log(_settings.test);
},
}
system.JSON
{
"test" : 100
}
the result of the console.log give me this
ComputedProperty {isDescriptor: true, _dependentKeys: Array[0], _suspended: undefined, _meta: undefined, _cacheable: true…}
I think it's because of the JSON is not loaded yet but after that I try to do something like this at route
index.js - route
activate: function(){
var self = this;
var run = Ember.run
run.later(function() {
var _settings = self.systemSetting.systemJSON;
console.log(_settings);
}, 1000);
},
but still give me the same log. Am I use wrong approach to this problem?
I finally found the answer. Because of what I want to call is from an initializer then one that I must do is to use .get and if I just using get then the one that I received is a promise and to get the actual data I must use .then
The code will look like this:
index.js - route
activate: function(){
this.get('systemSetting.systemJSON').then(function(data) {
console.log(data.test);
});
}

Ember Data belongsTo async relationship omitted from createRecord() save() serialization

Edit 11/16/14: Version Information
DEBUG: Ember : 1.7.0 ember-1.7.0.js:14463
DEBUG: Ember Data : 1.0.0-beta.10+canary.30d6bf849b ember-1.7.0.js:14463
DEBUG: Handlebars : 1.1.2 ember-1.7.0.js:14463
DEBUG: jQuery : 1.10.2
I'm beating my head against a wall trying to do something that I think should be fairly straightforward with ember and ember-data, but I haven't had any luck so far.
Essentially, I want to use server data to populate a <select> dropdown menu. When the form is submitted, a model should be created based on the data the user chooses to select. The model is then saved with ember data and forwarded to the server with the following format:
{
"File": {
"fileName":"the_name.txt",
"filePath":"/the/path",
"typeId": 13,
"versionId": 2
}
}
The problem is, the typeId and versionId are left out when the model relationship is defined as async like so:
App.File = DS.Model.extend({
type: DS.belongsTo('type', {async: true}),
version: DS.belongsTo('version', {async: true}),
fileName: DS.attr('string'),
filePath: DS.attr('string')
});
The part that is confusing me, and probably where my mistakes lie, is the controller:
App.FilesNewController = Ember.ObjectController.extend({
needs: ['files'],
uploadError: false,
// These properties will be given by the binding in the view to the
//<select> inputs.
selectedType: null,
selectedVersion: null,
files: Ember.computed.alias('controllers.files'),
actions: {
createFile: function() {
this.createFileHelper();
}
},
createFileHelper: function() {
var selectedType = this.get('selectedType');
var selectedVersion = this.get('selectedVersion');
var file = this.store.createRecord('file', {
fileName: 'the_name.txt',
filePath: '/the/path'
});
var gotDependencies = function(values) {
//////////////////////////////////////
// This only works when async: false
file.set('type', values[0])
.set('version', values[1]);
//////////////////////////////////////
var onSuccess = function() {
this.transitionToRoute('files');
}.bind(this);
var onFail = function() {
this.set('uploadError', true);
}.bind(this);
file.save().then(onSuccess, onFail);
}.bind(this);
Ember.RSVP.all([
selectedType,
selectedVersion
]).then(gotDependencies);
}
});
When async is set to false, ember handles createRecord().save() POST requests correctly.
When async is true, ember handles GET requests perfectly with multiple requests, but does NOT add the belongsTo relationships to the file JSON during createRecord().save(). Only the basic properties are serialized:
{"File":{"fileName":"the_name.txt","filePath":"/the/path"}}
I realize this question has been asked before but I have not found a satisfactory answer thus far and I have not found anything that suits my needs. So, how do I get the belongsTo relationship to serialize properly?
Just to be sure that everything is here, I will add the custom serialization I have so far:
App.ApplicationSerializer = DS.RESTSerializer.extend({
serializeIntoHash: function(data, type, record, options) {
var root = Ember.String.capitalize(type.typeKey);
data[root] = this.serialize(record, options);
},
keyForRelationship: function(key, type){
if (type === 'belongsTo') {
key += "Id";
}
if (type === 'hasMany') {
key += "Ids";
}
return key;
}
});
App.FileSerializer = App.ApplicationSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
type: { serialize: 'id' },
version: { serialize: 'id' }
}
});
And a select:
{{ view Ember.Select
contentBinding="controller.files.versions"
optionValuePath="content"
optionLabelPath="content.versionStr"
valueBinding="controller.selectedVersion"
id="selectVersion"
classNames="form-control"
prompt="-- Select Version --"}}
If necessary I will append the other routes and controllers (FilesRoute, FilesController, VersionsRoute, TypesRoute)
EDIT 11/16/14
I have a working solution (hack?) that I found based on information in two relevant threads:
1) How should async belongsTo relationships be serialized?
2) Does async belongsTo support related model assignment?
Essentially, all I had to do was move the Ember.RSVP.all() to after a get() on the properties:
createFileHelper: function() {
var selectedType = this.get('selectedType');
var selectedVersion = this.get('selectedVersion');
var file = this.store.createRecord('file', {
fileName: 'the_name.txt',
filePath: '/the/path',
type: null,
version: null
});
file.set('type', values[0])
.set('version', values[1]);
Ember.RSVP.all([
file.get('type'),
file.get('version')
]).then(function(values) {
var onSuccess = function() {
this.transitionToRoute('files');
}.bind(this);
var onFail = function() {
alert("failure");
this.set('uploadError', true);
}.bind(this);
file.save().then(onSuccess, onFail);
}.bind(this));
}
So I needed to get() the properties that were belongsTo relationships before I save the model. I don't know is whether this is a bug or not. Maybe someone with more knowledge about emberjs can help shed some light on that.
See the question for more details, but the generic answer that I worked for me when saving a model with a belongsTo relationship (and you specifically need that relationship to be serialized) is to call .get() on the properties and then save() them in then().
It boils down to this:
var file = this.store.createRecord('file', {
fileName: 'the_name.txt',
filePath: '/the/path',
type: null,
version: null
});
// belongsTo set() here
file.set('type', selectedType)
.set('version', selectedVersion);
Ember.RSVP.all([
file.get('type'),
file.get('version')
]).then(function(values) {
var onSuccess = function() {
this.transitionToRoute('files');
}.bind(this);
var onFail = function() {
alert("failure");
this.set('uploadError', true);
}.bind(this);
// Save inside then() after I call get() on promises
file.save().then(onSuccess, onFail);
}.bind(this));

Not fetching correct url issue

I have a backboneJS app that has a router that looks
var StoreRouter = Backbone.Router.extend({
routes: {
'stores/add/' : 'add',
'stores/edit/:id': 'edit'
},
add: function(){
var addStoresView = new AddStoresView({
el: ".wrapper"
});
},
edit: function(id){
var editStoresView = new EditStoresView({
el: ".wrapper",
model: new Store({ id: id })
});
}
});
var storeRouter = new StoreRouter();
Backbone.history.start({ pushState: true, hashChange: false });
and a model that looks like:
var Store = Backbone.Model.extend({
urlRoot: "/stores/"
});
and then my view looks like:
var EditStoresView = Backbone.View.extend({
...
render: function() {
this.model.fetch({
success : function(model, response, options) {
this.$el.append ( JST['tmpl/' + "edit"] (model.toJSON()) );
}
});
}
I thought that urlRoot when fetched would call /stores/ID_HERE, but right now it doesn't call that, it just calls /stores/, but I'm not sure why and how to fix this?
In devTools, here is the url it's going for:
GET http://localhost/stores/
This might not be the answer since it depends on your real production code.
Normally the code you entered is supposed to work, and I even saw a comment saying that it works in a jsfiddle. A couple of reasons might affect the outcome:
In your code you changed the Backbone.Model.url() function. By default the url function is
url: function() {
var base =
_.result(this, 'urlRoot') ||
_.result(this.collection, 'url') ||
urlError();
if (this.isNew()) return base;
return base.replace(/([^\/])$/, '$1/') + encodeURIComponent(this.id);
},
This is the function to be used by Backbone to generate the URL for model.fetch();.
You added a custom idAttribute when you declared your Store Model to be like the one in your DB. For example your database has a different id than id itself, but in your code you still use new Model({ id: id }); when you really should use new Model({ customId: id });. What happens behind the scenes is that you see in the url() function it checks if the model isNew(). This function actually checks if the id is set, but if it is custom it checks for that:
isNew: function() {
return !this.has(this.idAttribute);
},
You messed up with Backbone.sync ... lots of things can be done with this I will not even start unless I want to make a paper on it. Maybe you followed a tutorial without knowing that it might affect some other code.
You called model.fetch() "a la" $.ajax style:
model.fetch({
data: objectHere,
url: yourUrlHere,
success: function () {},
error: function () {}
});
This overrides the awesomeness of the Backbone automation. (I think sync takes over from here, don't quote me on that).
Reference: Backbone annotated sourcecode

How to post json to api method in backbone.js?

i have rest api based on django rest framework, that include next method of creation object, that takes the data in JSON-format on 'myapp/create_obj/' and if the data is correct object will created, otherwise it returns an error also in JSON-format.
def create_obj(request):
stream = StringIO(request.raw_post_data)
data = JSONParser().parse(stream)
serializer = ObjSerializer(data=data, many=True)
if serializer.is_valid():
serializer.save()
return JSONResponse(serializer.data, status=201)
else:
return JSONResponse(serializer.errors, status=400)
Also i tried to create a module on backbone.js, that post the input in form data to this method. Im very new to js, in particular to backbone and i bad understand how backbone works with server api. i have something like
App.module('Createobj', function(Mod, App, Backbone, Marionette, $, _) {
Mod.id = 'create-obj';
Mod.controllers = {};
Mod.Obj = Backbone.Model.extend({
defaults: {
real_ref : '',
share : ''
}
});
Mod.View = Marionette.ItemView.extend({
id: 'create-obj-page',
template: '#tpl-create-obj-page',
model: Mod.obj,
ui: {
'real_ref': 'input[name=real_ref]',
'share': 'input[name=share]',
'error': 'div.error'
},
hammerEvents: {
'tap button': 'submit:tap'
},
hammerOptions: {
tap: true
},
showError: function(message) {
this.ui.error
.text(message)
.show();
},
hideError: function() {
this.ui.error.hide();
},
});
Mod.Controller = SRClient.PageController.extend({
id: Mod.id + '.main',
ViewClass: Mod.View,
setup: function() {
this.listenTo(this.view, 'submit:tap', this.submit);
},
submit: function() {
var real_ref = this.view.ui.real_ref.val(),
share = this.view.ui.share.val();
if (!real_ref || !share) {
this.view.showError($t('create-obj.error_empty_fields'));
return;
}
App.vent.trigger('loading-screen:show', $t('app.please_wait'));
var obj = new Mod.obj({
real_ref : this.view.ui.real_ref.val(),
share : this.view.ui.share.val()
});
}});
Mod.addInitializer(function() {
Mod.Controllers = {
default: Mod.Controller
};
App.pageControllers[Mod.id] = Mod;
});
});
What i need to do, that data which i input in webform sends to 'myapp/create_obj' in json-format? Thanks!
Backbone expects a RESTful api so instead of being the endpoint an action like create_obj, REST works with Resources and with HTTP methods. In your case you could have a Model like this:
var Obj = Backbone.Model.extend({
defaults: {
real_ref : '',
share : ''
}
});
and a collection like this
var Objects = Backbone.Collection.extend({
url: 'myapp/obj',
model: Obj
});
the collection has a propetry url that specifies the server endpoint. So the operations will be
POST /myapp/obj/ for create a new item
GET /myapp/obj/:id/ if you want to retreive an specific item
GET /myapp/obj/ retreving the whole list
PUT /myapp/obj/:id/ update an item
DELETE /myapp/obj/:id/ delete an item
Tastypie is a good framework to create RESTful api with Django.

Ember Data - TypeError: Object has no method 'eachRelationship'

So, I'm trying to build routes in my Ember application dynamically with data from an API endpoint, /categories, with Ember Data. In order to do this, I'm adding a didLoad method to my model, which is called by the controller and set to a property of that controller. I map the route to my router, and all that works fine. The real trouble starts when I try to set up a controller with a content property set by data from the server retrieved by findQuery.
This is the error:
TypeError {} "Object /categories/548/feeds has no method 'eachRelationship'"
This is the code:
window.categoryRoutes = [];
App.Categories = DS.Model.extend({
CATEGORYAFFINITY: DS.attr('boolean'),
CATEGORYID: DS.attr('number'),
CATEGORYNAME: DS.attr('string'),
CATEGORYLINK: function () {
var safeUrl = urlsafe(this.get('CATEGORYNAME'));
categoryRoutes.push(safeUrl);
return safeUrl;
}.property('CATEGORYNAME'),
didLoad: function () {
var categoryLink = this.get('CATEGORYLINK');
var categoryId = this.get('CATEGORYID');
App.Router.map(function () {
this.resource(categoryLink, function () {
// some routes
});
});
App[Ember.String.classify(categoryLink) + 'Route'] = Ember.Route.extend({
setupController: function(controller, model) {
// source of error
this.controllerFor(categoryLink).set(
'content',
this.store.findQuery('/categories/' + categoryId + '/feeds', {
appid: 'abc123def456',
lat: 39.75,
long: -105
})
);
}
});
}
});
Any 'halp' is appreciated!
Also, if I'm doing this completely wrong, and there's a more Ember-like way to do this, I'd like to know.
I figured this out. I got this error because I was passing in a string instead of a real 'type' from the App.Helpers object to an extract method in some custom RESTAdapter code I had overridden.
The solution is to pass in the corresponding model helper in App.Helpers using my custom type name.
Something like this in the overridden RESTAdapter.serializer.extractMany method:
var reference = this.extractRecordRepresentation(loader, App.Helpers[root], objects[i]);

Categories