Backbone app: Can't get model data in View - javascript

I'm building my first, relatively simple Backbone app. Right now, it's fetching JSON of local weather information:
Weather = Backbone.Model.extend({
url: '/api/v1/weather',
initialize: function(){
this.fetch({
success: this.fetchSuccess,
error: this.fetchError
});
},
parse: function(response)
{
return response;
},
fetchSuccess: function (model, response) {
console.log('FETCH SUCCESS:', response);
},
fetchError: function (model, response) {
throw new Error("FETCH ERROR");
}
});
The above seems to work just fine, as the fetchSuccess console log returns the JSON response as expected.
The problem happens when I attempt to access this data from the view. Here's my code for that:
WeatherView = Backbone.View.extend({
el: $('#widget__weather'),
initialize: function()
{
_.bindAll(this, 'render');
this.model = new Weather();
this.model.bind('reset', this.render);
this.render();
},
render: function()
{
console.log(this.model.toJSON());
// var template = _.template(weatherTemplate, { weather : this.model });
// this.$el.html(template);
}
});
The console log for the view is an empty Object { }. My attempts to use this.model.get('timezone') result in undefined.
When I console.log(this.model) I get this:
s {cid: "c3", attributes: Object, _changing: false, _previousAttributes: Object, changed: Object…}
_changing: false
_events: Object
_pending: false
_previousAttributes: Object
attributes: Object
currently: Object
daily: Object
flags: Object
headers: Array[13]
hourly: Object
minutely: Object
offset: -4
timezone: "America/New_York"
__proto__: Object
changed: Object
cid: "c3"
__proto__: n
It seems that my JSON data is in the 'attributes' object of the model, but I don't know why it's there or how to access it.
To be clear, when I do console.log(this.model.toJSON()); I get an empty Object { }.
I feel like I'm missing something obvious here and could use any and all help. What am I doing wrong? Is it possible that the structure of the returned JSON data may be causing this? It's a pretty standard response from Forecast.io API.
Let me know if you need any more code / information.
EDIT:
With some help I fixed the issue. Turns out this was the culprit: this.model.bind('reset', this.render);. Changing that to this.model.bind('change', this.render); fixed the problem.

yes is correct.
If you write this:
console.log(this.model);
Uou get the instance of the model, every property that you have parsed are inside attributes.
Instead if you use
console.log(this.model.toJSON());
you transform the instance of the model to a json and you can get directly your property without passing by attributes
Then if you wanna print your data inside your template you need to do this:
if you use:
var template = _.template(weatherTemplate, { weather : this.model });
inside template you need to print your var in this way:
weather.attributes.timezone;
Instead if you use
var template = _.template(weatherTemplate, { weather : this.model.toJSON() });
inside template you need to print your var in this way:
weather.timezone;

Related

Using tagName and className properties on Backbone View Vs using template

I have a Backbone View that is using a Backbone Collection to pull data from an api:
var HousesView = Backbone.View.extend({
initialize: function() {
this.collection = new Houses();
var that = this;
this.collection.fetch({
data: {
pageSize: 50
},
success: function () {
that.render();
},
error: function () {
console.error('There was an error in fetch');
}
});
},
tagName: 'section',
template: Handlebars.getTemplate('houses'),
render: function() {
this.$el.html(this.template({ houses : this.collection.toJSON() }));
return this;
}
});
It then creates models from the json pulled from the api and passes that as a collection to my template which takes each model and for e.g.just outputs the name attribute of that model as a list. That works fine.
My question is: because I am using a template to parse the data from this.collection.toJSON() the tagName I set on HouseView doesn't seem to be output in the html. If I am using a template for a View does this mean properties like tagName, className etc wont be output?
Also, ideally I would like to create a HouseView for each model from the collection and then display all of those in a wrapper HousesView.
In your example you are using custom rendered. In that case the auto generation of view from tagName, className and id will be spiked (overwrites by method this.$el.html in render function.).
The best approach is as you mentioned to create separate view and append it in render of main view to html.

Backbone.js fetch() returning object instead of child in server instance but in local instance retrieves child

I have 2 instances. My local with OS Win7, and a Server instance with OS Linux.
I am fetching the JSON data and setting it to a model using following code.
var RModel = Backbone.Model.extend({
idAttribute: 'name',
parse: function (response) {
return {
'name': response.name,
'title': response.title,
'description': response.description,
'parameters': new ParamsList(response.parameters)
};
}
});
that.model = new RModel();
that.model.url = "url/" + '?limited=false';
that.model.fetch({
cache: false
}).done(function() {
that.headerTemplate = that.headerTemplateEdit;
that.bodyTemplate = that.bodyTemplateEdit;
that.footerTemplate = footerTemplate;
that.load({});
});
In my local instance the result of following code in console.
this.model
child
_changing:false
_pending:false
_previousAttributes:Object
attributes:Object
changed:Object
cid:"c217"
id:"testUndefinedParam"
url:"/url?limited=false"
__proto__:
Backbone.Model
In server instance
this.model
i
_changing: false
_pending: false
_previousAttributes: Object
attributes: Object
changed: Object
cid: "c25920"
id: "testDateError2"
url: "/url?limited=false"
__proto__: t.Model
If anyone came across this issue please show some way retrieve JSON data properly.
My guess is that the code in your server is processed (minify/uglify) and the code in your local is not. So the processor just renamed child to i, Backbone to t etc. You should be concerned about differences in actual data rather than the constructor names the console outputs. As far as I know there is no standard and it can vary between browsers

TypeError trying to set model object in Backbone.js

I've been playing around with the backbone.js + cordova + require.js frameworks, based mainly off of Cristophe Coenraets' PhoneGap examples on GitHub. Displaying my model in a view seems to be straightforward, but I'm still unable to update the model via calls to set or save.
My model looks something like this:
SourcePhrase = Backbone.Model.extend({
// default values
defaults: {
id: null,
markers: "",
orig: null,
source: "",
target: ""
},
sync: function (method, model, options) {
if (method === "read") {
findById(this.id).done(function (data) {
options.success(data);
});
}
}
}),
// etc
I can pull objects out of my collection by making a call to get:
// find and update the model object
var strID = $(event.currentTarget.parentElement).attr('id');
var model = this.collection.get(strID);
So far, so good:
model.set('target', trimmedValue);
TypeError: 'undefined' is not a function (evaluating '(i=t[r]).callback.call(i.ctx,n,a)')
Hmm...that's not right. Any idea where I need to start looking to track this down?
EDIT: console output for model just before the call to set:
model: Object
_changing: false
_events: Object
_pending: false
_previousAttributes: Object
attributes: Object
id: "RUT001-10"
markers: "\hdr"
orig: null
source: "Ruth"
target: "Ruth"
__proto__: Object
changed: Object
cid: "c15"
collection: Object
id: "RUT001-10"
__proto__: Object
strID: "RUT001-10"
Yes it's the right method to use, and you can even change your code like this :
this.model.bind('change', this.render, this);
and it will work.
Ok, I think I might have tracked it down, maybe? I had in my View for a single item:
initialize: function () {
this.model.bind('change', this.render());
this.render();
},
The bind() call was causing the TypeError, which means I might have been running into a "this" issue? (backbone.js and binding "this".) At any rate, I've replaced the block with this one:
initialize: function () {
this.listenTo(this.model, 'change', this.render);
},
It seems to do the trick. If someone with more backbone.js expertise could comment on this approach, I'd very much appreciate it. Am I doing this correctly?

Backbone.js collection fetch, can't retrieve items

I'm new to Backbone.js, I'm following a tutorial trying to adapt it to my needs.
I'm calling the fetch method from the main app view in order to retrieve multiple objects to be inserted in the collection.
I can see in chrome that the json data are returned, the fetch functions returns success but the collection is not populated.
I'm using IcanHaz for rendering. It prints out only the default model as I defined it in the Job model.
var Job = Backbone.Model.extend({
defaults: {
title: 'Not specified',
status: 0,
created_at: 'Not specified'
}
});
var JobView = Backbone.View.extend({
render: function () {
// template with ICanHaz.js (ich)
this.el = ich.jobRowTpl(this.model.toJSON());
return this;
}
});
// define the collection of jobs
var JobCollection = Backbone.Collection.extend({
model: Job,
url: '/api/1.0/jobs/'
});
// main app, the collection view
var AppView = Backbone.View.extend({
tagName: 'tbody',
initialize: function() {
this.jobs = new JobCollection();
this.jobs.on('all', this.render, this);
this.jobs.fetch({
error: function () {
console.log("error!!");
},
success: function () {
console.log("no error");
}
}).complete(function () {
console.log('done');
console.log('length1:' + this.jobs.length);
});
console.log('length2: '+ this.jobs.length);
},
render: function () {
this.jobs.each(function (job) {
var tmpjob = new JobView({model: job}).render().el;
$(this.el).append(tmpjob);
console.log('job': + this.tmpjob);
}, this);
return this;
}
});
var app = new AppView();
$('#app').append(app.render().el);
In the Chrome console I get this:
length2:0
job:undefined
no error
done
Uncaught TypeError: Cannot read property 'length' of undefined //referring to the lenght1 logging
These are the data that I fetch, from the chrome inspector under network/xhr/response:
{"count": 2, "next": null, "previous": null, "results": [{"id": 1, "title": "testAlbum", "status": 0, "created_at": "2012-12-31"}, {"id": 2, "title": "testAlbum2", "status": 0, "created_at": "2012-12-31"}]}
I don't understand why the 'jobs' collection exists after calling the fetch method but it is undefined inside the fetch block when the 'success' helper has been called.
And why the collection is not being populated despite it returns success and the json data are returned from the server?
I'm quite lost.
Add a parse method to your collection that just returns the results arrays. A collection needs to be an array of models, not your whole JSON response.
The Backbone docs explain how to use parse.

JavaScript: Backbone.js fetch json and load it into a collection of models?

So far i have the following code, but it doesn't seem to be working, and I don't know when the asynchronous fetch is completed:
var item = Backbone.Model.extend({
defaults: {
id: 0,
an_id: 0,
strval: null,
th_id: 0,
text: null
},
url: 'page.php',
options: {
success: function(data) {
alert('s: ' + dump(data));
// the dump function is my way of dumping objects into a string,
// use console.log if you want, as I have that disabled
},
error: function(x, t, e) {
alert('e: ' + t + ', ' + e);
}
}
});
var coll = Backbone.Collection.extend({
model: item
});
var options = new Options();
Backbone.sync("create", coll, item.options); // 'undefined' is not an object (evaluating c.url) in backbone-min.js
Update
I have modified the original code to what I have now, and the backend can now tell the difference between New, Update, Save and Delete requests.
I still cannot find out how to populate the collection coll.
Backbone.Collection is for keeping multiple items - you seem to be trying to have your Collection "inherit" from your model, which isn't the right approach.
Collections are ordered sets of models. You can bind "change" events
to be notified when any model in the collection has been modified,
listen for "add" and "remove" events, fetch the collection from the
server, and use a full suite of Underscore.js methods.
you can add a success handler to your fetch call. try this:
coll.fetch({
success: function() {
alert("success");
console.log(coll.toJSON());
},
error: function(){
alert("error")}
});

Categories

Resources