Backbone targetModel = undefined - javascript

I am running into this issue with backbone where the model seems to be undefined to backbone, though all scripts are loaded.
(I am using require to load backbone and other javascript files).
So whenever I do a collection.fetch I get this error in firebug:
TypeError: targetModel is undefined
When I run the script it holds at this point:
if (attrs instanceof Model) {
id = model = attrs;
} else {
id = attrs[targetModel.prototype.idAttribute];
}
when I hover with my mouse over targetModel it says: undefined
It somehow doesn't seem to work now and the only thing I did was changing my html template, which only get loaded after the collection.fetch.
Can you please help me out here?
Here is my model:
var OF = OF || {};
OF.UsersMdl = Backbone.Model.extend({
default: {
username: '',
mailinglist: '',
email: ''
},
initialize: function() {
//
},
result: {
success: false,
message: ''
},
validate: function(att) {
}
});
Here is the collection:
var OF = OF || {};
OF.UsersCollection = Backbone.Collection.extend({
initialize: function() {
//
},
parse: function(data){
return data["all-users"];
},
model: OF.UsersMdl,
url: 'php/api/users'
});
And last but not least the router with the require part:
goToUsers: function() {
require(['./models/users', './views/users_view', './collections/user_collection'], function(UsersMdl, UsersView, UsersCollection) {
OF.usersMdl = new OF.UsersMdl;
OF.usersCollection = new OF.UsersCollection;
OF.usersView = new OF.UsersView;
//when the collection is fetched
$.when(OF.usersCollection.fetch({
data: {
"admin": OF.login.attributes.admin,
"session": OF.login.attributes.session
},
success: function(){
//console.log(OF.usersCollection.length);
}
//then render the view
})).then(function(){
OF.usersView.render();
}, 300);
});
},
Here is the JSON which will be retreived by the fetch:
{
"all-users":
[
{
"username":"tester",
"mailinglist":"1",
"email":"tester#tester.test"
},
{
"username":"tester2",
"mailinglist":"1",
"email":"tester2#tester.test"
},
{
"username":"tester3",
"mailinglist":"0",
"email":"tester3#tester.test"
}
]
}
Thanks in advance

I had this same error and banged my head against it for quite a while because backbone is new to me and this was compounding a fetch issue. Anyhow, I eventually figured out that order matters. Doh! (Less obvious when using CoffeeScript and "class" statements I thinks.) With one of my models I was setting the Collection before the Model (thanks to bad example code from the Backbone.js on Rails book). I reversed that and this error went away to reveal my true fetch issue.
Similarly, your model: property may be invalid for this reason or another reason, leaving it undefined when attempting to reference later.
Side note: I had a similar error in Backbone 1.0.0. When I upgraded to Backbone 1.1.0 I then got this exact error at the same point in backbone code.

Related

pass parameters to backbone fetch url to deal with a non-standard api

I am trying to manipulate backbone's fetch method to to deal with a bit of a non-standard api. The way the api works is as follows:
api/products/[page]?param1=val&param2=val
ex:
api/products/2?budget=low&categories=all
would be equivalent to getting the second page of results for which the budget is low and all categories are included.
I can pass the parameters after the query string just fine through the format:
self.productsItemsCollection.fetch({ success : onDataHandler, dataType: "json", data: { budget: 'low', categories: 'all' } });
but I'm not sure what to do about the pagination, since it comes before the ? question mark.
Here is how the collection is set up:
define([
'underscore',
'backbone',
'models/products/ProductsItemsModel'
], function(_, Backbone, ProductsItemsModel){
var ProductsItemsCollection = Backbone.Collection.extend({
model: ProductsItemsModel,
initialize : function(models, options) {}, //MH - need to pass filters to this function
url : function() {
return '/api/products/'; //MH - need to pass page number to be appended to this url
},
parse : function(data) {
debugger;
return data.items;
}
});
return ProductsItemsCollection;
});
How do I include the pagination in backbone's fetch command given this api URL structure?
You're on the right track in that Backbone can use the return value of a function as its 'url' value. What I personally would do, is set a page property on the collection (referenced through something like this.page), and include that in the output of the url function.
initialize: function() {
this.page = 1; // Or whatever the default should be
},
url: function() {
return '/api/products/ + this.page;
}
The problem then becomes updating the page property, which can be as simple as 'ProductsItemsCollection.page = 2;'. Personally, I would also add a second fetch method to wrap the page update and fetch into a single method call.
fetch2: function(page, options) {
if (page) {
this.page = page;
}
return this.fetch(options);
}
Just few notes to your code. I think you don't need to define page number into your Collection. According to MVC pattern it's more suitable for Controller. Collection just should get parameter and return some data according to it. Meanwhile Backbone doesn't provide classic MVC Controller, but you can use for this purpose Backbone.View. So structure of your application could looks something like this:
// Collection
define([
'backbone',
'models/products/ProductsItemsModel'
], function(Backbone, ProductsItemsModel){
return Backbone.Collection.extend({
// I don't know what exactly your Model does, but if you don't override Backbone.Model with your own methods you don't really need to define it into your collection.
model: ProductsItemsModel,
initialize : function(models, options) {}, //MH - need to pass filters to this function
url : function(page) {
return '/api/products/' + page;
},
parse : function(data) {
return data.items;
}
});
});
And then in your View you can fetch needed page and render it:
define([
'jquery',
'backbone',
'ProductsItemsCollection'
], function($, Backbone, ProductsItemsCollection){
return Backbone.View.extend({
events: {
// Your logic to get page number from your pagination.
'click .pagination': 'getPageNumber'
}
collection: new ProductsItemsCollection(),
initialize : function() {
this.listenTo(this.collection, 'reset', this.render);
// initial loading collection
this.load(1); // load page #1
},
render: function () {
// your render code
}
// Example function to show how you could get page number.
getPageNumber: function(e) {
var pageNumber = $(e.currentTarget).data('pageNumber');
load(pageNumber);
},
load: function(page) {
url: this.collection.url(page),
data: {
budget: 'low',
categories: 'all'
}
}
});
});
Something like that. So in this View you just make initialization of your Collection and make initial loading. Then all you should make is passing page number to your load function.
I read these answers, i guess they make sense but this is what i went with. just really simple:
app.WorkOrder = Backbone.Collection.extend({
model: app.WorkOrderDetail,
urlRoot: '/m2/api/w/',
getWorkOrder: function(workorder_id, options) {
this.url = this.urlRoot + workorder_id;
return this.fetch(options);
}
});
Then in the view i do this:
app.AppView = Backbone.View.extend({
el: '#workorderapp',
initialize: function () {
app.workOrder.getWorkOrder(workorder_id, {
success:function(data) {
//...do something with data
}
});
},
});

Models are not serialized with Ember.js and WebApiAdapter

I'm trying to use the Ember.js MVC4 Spa Template with my own Models, but I'm not getting this to work.
For now, the serverside code is is working. The result to the browser is correct. But Ember-Data, or the custom WebApi - Serializer, is not able to prepare the Data.
I have two Models:
Patient:
App.Patient = DS.Model.extend();
App.Patient.reopen({
patientId: DS.attr('number'),
firstName: DS.attr('string'),
lastName: DS.attr('string'),
aufenthalte: DS.hasMany('aufenthalt'), //, { async: true }
fullName: function () {
return this.get('firstName') + ' ' + this.get('lastName');
}.property('firstName', 'lastName'),
});
App.PatientSerializer = DS.WebAPISerializer.extend({
primaryKey: 'patientId',
// ember-data-1.0.0-beta2 does not handle embedded data like they once did in 0.13, so we've to update individually if present
// once embedded is implemented in future release, we'll move this back to WebAPISerializer.
// see https://github.com/emberjs/data/blob/master/TRANSITION.md for details
extractArray: function (store, primaryType, payload) {
var primaryTypeName = primaryType.typeKey;
var typeName = primaryTypeName,
type = store.modelFor(typeName);
var data = {};
data[typeName] = payload;
data.aufenthalte = [];
var normalizedArray = payload.map(function (hash) {
hash.aufenthalte.map(function (aufenthalt) {
data.aufenthalte.push(aufenthalt);
});
hash.aufenthalte = hash.aufenthalte.mapProperty('aufenthaltId');
return hash;
}, this);
payload = data;
return this._super.apply(this, arguments);
},
normalizeHash: {
patient: function (hash) {
hash.patientId = hash.id;
return hash;
}
}
});
Aufenthalt:
App.Aufenthalt = DS.Model.extend({
aufenthaltId: DS.attr('number'),
name: DS.attr('string'),
patientId: DS.attr('number'),
patient: DS.belongsTo('patient'),
});
App.AufenthaltSerializer = DS.WebAPISerializer.extend({
primaryKey: 'aufenthaltId',
normalizeHash: {
aufenthalte: function (hash) {
hash.aufenthaltId = hash.id;
return hash;
},
}
});
When I get a List of "Patients" from my Controller, the Datamodels are filled correctly (I can check it in the Chrome Ember plugin.) When I hit a Action with the Patient Id, I get the error: "Error while loading route: TypeError: Cannot set property 'store' of undefined"
Thank You!
Did you added the proper router in app/routes folder, controller in app/controllers folder, and corresponding views and templates? Feel free to psot a link to your sample solution so I can download and have a look.
=== Update 2/22/2014 ===
I fixed the code. You should be able to download the modified solution from https://www.dropbox.com/s/4j3vbczqr4nx68m/EmberVSTemplateModified.zip. You should do a windiff on the two directories to see the changes. I need to change a few places to make it work for your scenario, including:
patient.js, make it directly extend from RESTSerialzer, and add extractSingle implementation.
change template of patsucheautocomplete.hbs
added patient\index.hbs . You should be able to remove patient.hbs file
paitentview.js (may not be necessary as it's all default)
modified controllers\htmlhelperextensions.cs, to make it work correctly for sub folder templates in debug mode.

Backbone Boilerplate - fetch method don't refresh collection

is my first question here, so I please about some patience and forgive my english:)
When I type link in browser address bar, all is OK. But when I do this inside browser by clicking element, collection is empty. But the main problem is there is always the same response from server, but fetch "dont load" any items, so view render empty collection.
I use Backbone Boilerplate,
Browser.Views.Catalog - it is Backbone.View
Browser.Catalog - it is of Backbone.Collection
My router:
var Router = Backbone.Router.extend({
routes: {
'' : 'browse'
},
refreshCatalog: function(folder){
app.layout.setViews({
"#catalog" : new Browser.Views.Catalog({
collection: app.catalog
})
}).render();
},
browse: function(folder){
app.catalog = new Browser.Catalog();
app.folders.fetch({
error: function() { console.log(arguments); },
success: this.refreshFolders(folder),
data: $.param({folder: folder}),
cache:false
});
//app.catalog = new Browser.Catalog();
app.catalog.fetch({
error: function() { console.log(arguments); },
success: this.refreshCatalog(folder),
data: $.param({folder: folder}),
cache:false
});
},
I belive you should set the catalog in the initialize function
app.catalog = new Browser.Catalog();
should go in here ( add this function)
initialize: function (options) {
app.catalog = new Browser.Catalog();
}
the initialize function is called when the page is loaded so when browsing to #catelog it will have been set http://backbonejs.org/#Router-constructor

Backbone.js and local storage . A "url" property or function must be specified

I'm improving my knowledge about Backbone.js and have this code sample taken from a tutorial. (http://bardevblog.wordpress.com/2012/01/16/understanding-backbone-js-simple-example/)
This example will not access the server for now, so to simulate the retrieval of data from the server I have a file name movies.json.
What I am trying to do:
Add json data in local storage (using localStorage adapter)
For this I am using Backbone.ajaxSync, Which Is Given to the alias Backbone.sync by the localStorage adapter: I created the method refreshFromServer () to do this
The reason for doing this is that I'm trying to implement a way to get data only one time (and only refresh when i need to)
My issues:
  I'm having an error "Uncaught Error: 'url' property or function must be specified" when I call refreshFromServer ().
I do not understand why because I set the url collection. (url : "scripts/data/movies.json" )
Sample code
var Theater = {
Models : {},
Collections : {},
Views : {},
Templates : {}
}
Theater.Models.Movie = Backbone.Model.extend({})
Theater.Collections.Movies = Backbone.Collection.extend({
model : Theater.Models.Movie,
localStorage : new Backbone.LocalStorage("MovieStore"), // Unique name within your app.
url : "scripts/data/movies.json",
refreshFromServer : function() {
return Backbone.ajaxSync.apply(this, arguments);
},
initialize : function() {
console.log("Movies initialize")
}
});
Theater.Templates.movies = _.template($("#tmplt-Movies").html())
Theater.Views.Movies = Backbone.View.extend({
el : $("#mainContainer"),
template : Theater.Templates.movies,
initialize : function() {
this.collection.bind("reset", this.render, this);
},
render : function() {
console.log("render")
console.log(this.collection.length);
}
})
Theater.Router = Backbone.Router.extend({
routes : {
"" : "defaultRoute"
},
defaultRoute : function() {
console.log("defaultRoute");
Theater.movies = new Theater.Collections.Movies()
new Theater.Views.Movies({
collection : Theater.movies
});
Theater.movies.refreshFromServer();
//Theater.movies.fetch();
console.log(Theater.movies.length)
}
})
var appRouter = new Theater.Router();
Backbone.history.start();
Notes:
If a comment localStorage property in the collection
Theater.Models.Movie = Backbone.Model.extend({})
Theater.Collections.Movies = Backbone.Collection.extend({
model : Theater.Models.Movie,
//localStorage : new Backbone.LocalStorage("MovieStore")
...
});
and then in router call normal fetch method
Theater.Router = Backbone.Router.extend({
routes : {
"" : "defaultRoute"
},
defaultRoute : function() {
Theater.movies = new Theater.Collections.Movies()
new Theater.Views.Movies({
collection : Theater.movies
});
//Theater.movies.refreshFromServer();
Theater.movies.fetch();
}
})
I can see the json list correctly in my view
If I use the localStorage property in the collection and then call the standard fetch () method, I see only an empty list (I think it is normal as it is read from the local storage and is empty)
The error only occurs when using the method refreshFromServer () witch use Backbone.ajaxSync (alias for backbone.sync)
Err... my bad. The refreshFromServer implementation is from my answer to your earlier question., and it's completely, uselessly wrong.
Backbone.sync expects arguments (method, model, options), but as it stands, it doesn't get what it needs from refreshFromServer because the refresh method simply sends forward whatever arguments it gets. Sorry for the mistake.
The correct, working implementation would be:
refreshFromServer : function(options) {
return Backbone.ajaxSync('read', this, options);
}
It can be used either via success / error callbacks passed to the options hash:
this.collection.refreshFromServer({ success: function() { /* refreshed... */ });
Or via the jqXHR Promise API:
this.collection.refreshFromServer().done(function() { /* refreshed... */ })
Or not signing up for callbacks and waiting for the collection reset event like in your example:
this.collection.bind("reset", this.render, this);
this.collection.refreshFromServer();
This should work. Please let me know if it doesn't. I fixed my answer in the previous question too, in case someone stumbles onto it.
Edit: To save the data to local storage after refreshing you need to manually save each of the models:
var collection = this.collection;
collection.refreshFromServer({success: function(freshData) {
collection.reset(freshData);
collection.each(function(model) {
model.save();
});
}});

Nested Model in Backbone.js

I want to map JSON having hierarchical structure onto Model. I can map the data at a top hierarchy onto Model. However, I can't map it onto Model which nested the element which I nested.
JSON
{
"attr1":"data1",
"chi1": {
"attr1":"chi1_data"
},
"list1":[
{"name":"name1"},
{"name":"name2"}
]
}
JavaScript
var Child2 = Backbone.Model.extend({
fun1:function() {
alert("this is Child2");
}
});
var List1 = Backbone.Collection.extend({
url: "list1",
model: Child2,
fun1:function() {
alert("this is List1");
}
});
var Child1 = Backbone.Model.extend({
});
var Root1 = Backbone.Model.extend({
url: "sample.json",
defaults : {
list1 : new List1,
chi1 : new Child1,
}
});
var View1 = Backbone.View.extend({
el: "#friends",
events: {
"click button": "sample"
},
initialize: function() {
this.root1 = new Root1();
},
sample: function() {
this.root1.fetch({
success: function(model) {
// this is success
alert(model.get("attr1"));
// this is error
alert(model.get("list1").fun1());
// this is error too.
model.get("list1").each(function(attr) {
alert(attr.fun1());
});
},
error: function(model, res) {
alert("error: " + res.status);
}
});
},
});
You might want to take a look at this plugin.
http://documentup.com/afeld/backbone-nested/
Might not be exactly what you want, but it could at least point you in the right direction.
The other thing you can do is override the parse method on your model...
parse: function(resp){
// And setup the model using the raw resp
// The resp data is your json from the server and will
// be used to setup the model. So overriding parse, you can
// setup the model exactly they way you want.
return resp;
}
thank you jcreamer.
backbone-nested plugin seems to be different from what I want to do.
I can realize the nest of the model. In using parse function.
// it is able to get "chi1_data"
new Child2(JSON.parse(JSON.stringify(resp["chi1"]))).get("attr1")
// it is able to get "name2"
new Child2(JSON.parse(JSON.stringify(new List1(JSON.parse(JSON.stringify(resp["list1"]))).get(2)))).get("name")
I found Backbone-relational plug in. I will try this
https://github.com/PaulUithol/Backbone-relational

Categories

Resources