backbone.js is not saving model with updated attributes - javascript

usersCollection.fetch({
success: function () {
var getModel = usersCollection.where(checkIDJSON);
//update that partcular attribute
getModel.set('interest', 'rolling stones');
console.log("Users:" + usersCollection.toJSON());
},
error: function () {
// something is wrong..
}
});
After running the code, it complains that the function is undefined, when trying to save to the model. Any idea why? thanks
I am using backbone.js in titanium mobile

From the fine manual:
where collection.where(attributes)
Return an array of all the models in a collection that match the passed attributes.
So when you say this:
var getModel = usersCollection.where(checkIDJSON);
you end up with an array of models in getModel. If you're sure that there will only be one model that matches, then use findWhere:
findWhere collection.findWhere(attributes)
Just like where, but directly returns only the first model in the collection that matches the passed attributes.
like this:
var getModel = usersCollection.findWhere(checkIDJSON);
If there could be multiple matches then presumably you'd want to call set on each one.

Related

Bakbone model extend filtering a json

I am very new in backbone js.
I am trying to filter some specific key and values in backbone js model extend here is the code below.
var items = ["open","close"];
var ReportModel = Backbone.Model.extend({
url: function() {
return tab+".json";
}
});
where tabe is dynamic json file name.In my json file many key value pair are there but I want to load only those key which is mentioned in items list.
I saw some where using parse function but that a;so did not work out.Please do let me know how to filter the specific keys form json using the backbone.
I also tried creating a dict from json and pass it to model like.
var ReportModel = Backbone.Model.extend({
"open":{.......}
});
but there I am getting issue.
throw new Error('A "url" property or function must be specified');
Please help me out with this.
You are missing some steps to be succesfull on your task.
First a note about the error: Backbone expects a string on the url property while you're passing a function. If you want to use a function to return your url dinamically use urlRoot.
Now onto the real coding:
since you talk about a json file that has multiple key value, maybe you should declare your model as a key-value object, and then create a Backbone.Collection that will wrap your models.
A Backbone.Collection expose a lot of utilities that can help us modeling the results, in this case by using the where() function of our collection you will be able to filter the data after you have retrieved from the remote file.
Alternatively to filter your collection if you need more control over the function you can always call the undescore function filter() .
Please refer to the official documentation of underscore and backbone, as you will find a lot of functions that can help you and most of them have an example that shows how to use them.
Now that we have everything lets create our Backbone.Collection that will wrap your already defined model:
var ReportCollection = Backbone.Collection.extend({
model: ReportModel,
urlRoot: function(){
return 'yoururl.json';
}
});
now if you want to filter the result you can simply fetch the collection and perform a filter on it:
var myReports = new ReportCollection();
//call the fetch method to retrieve the information from remote
myReports.fetch({success: function(){
//the collection has been fetched correctly, call the native where function with the key to be used as a filter.
var filteredElements = myReports.where({my_filter_key : my_filter_value});
});
in your filteredElements you will have an array of object made up of all the model that matched the key/value passed to the where function.
If you need a new Collection from that you just need to pass the result as argument: var filteredCollection = new ReportCollection(filteredElements);
You can use _.pick() in the parse method as shown below:
var items = ["open", "close"];
var ReportModel = Backbone.Model.extend({
url: function() {
return tab + ".json";
},
parse: function(response) {
return _.pick(response, items);
}
});

interpreting backbone collection data that it received from an api controller

I'm trying to use a simple collection and view to write out data from my backbone collection to my website. I just want to iterate over the collection and display properties like Id, Name, etc. in my template.
My collection gets its data from an api controller(a sample of the data is shown below).
My limited knowledge leads me to guess that the api controller is returning an object and not JSON.
So I'm guessing that is messing up my results. I've written out the collection to my Chrome console and attached a screenshot of what I see below.
So looking at the code below, is there a way that I can format the data returned from the api so that my collection can use it effectively?
Here is the code:
var ResearchCollection = Backbone.Collection.extend({
url: '/api/lab',
getresearch: function() {
this.fetch({
url: this.url
});
}
});
var researchCollection = new ResearchCollection();
return Backbone.View.extend({
className: 'labRender',
template: _.template(tmpl, null, { variable: 'x' }),
render: function () {
researchCollection.getresearch();
console.log('collection: ', researchCollection);
}
Basically, I just want to iterate over my collection and display properties like Id, Name, etc. in my template.
Here is the raw data from the api controller that I am using to populate my collection:
{
"odata.metadata":"http://sol.edu/SOM/Api/v1/$metadata#ApiKeys","value":[
{
"odata.id":"http://sol.edu/SOM/Api/v1/ApiKeys('2f2627ed-3a97-43aa-ac77-92f227888835')","Id":"2f2627ed-3a97-43aa-ac77-92f227888835","Name":"VideoSearch","TimeoutInMinutes":20160,"IsDefault":false,"CreateAuthTicketsForResources":false,"ReportAuthFailureAsError":false,"ExcludePrivatePresentations":true,"Internal":true,"ViewOnlyAccessContext":true
}
]
}
when piped to the browser's console(why is each character a separate attribute?):
I think maybe this is because you mixed up collection and model. In Backbone, Model are fundamental unit, a Model can be used to render a template.However, Collection are ordered sets of 'Models'. So, if you just want to transform a data like you describe above, you may better select a Model instead of 'Collection'. Just try this:
var ResearchModel = Backbone.Model.extend({
initialize: function(attributes) {
this.url = 'api/lab'
}
});
// when you initialize a Model or collection, the first parameter is the attribute you want to initialize
var researchModel = new ResearchModel({});
return Backbone.View.extend({
className: 'labRender',
template: _.template(tmpl, null, { variable: 'x' }),
render: function () {
researchModel.fetch();
console.log('collection: ', researchModel);
}
Otherwise, if you just want to use collection, you had better specify its Model.Backbone use JSON, so you can also specify the model with your key.

Can you bind a simple javascript array to your ember.js template?

I'm using ember.js RC1 + ember-data rev 11 (but I also need some plain ajax for configuration like models). I want to loop over a simple objects list and display the records (note -here I create just a basic array)
The content I have bound has the following custom find method defined
App.Foo = DS.Model.extend({
name: DS.attr('string')
}).reopenClass({
records: [],
all: function() {
return this.records;
},
find: function() {
var self = this;
$.getJSON('/api/foo/', function(response) {
response.forEach(function(data) {
//say I want to kill everything in the array here for some strange reason...
self.records = [];
//the template still shows the record ... not an empty list ?
}, this);
});
return this.records;
}
});
My other model uses this directly
App.Related = DS.Model.extend({
listings: function() {
return App.Foo.find();
}.property()
});
Now inside my template
{{#each foo in related.listings}}
{{foo.name}}<br />
{{/each}}
The list loads up with whatever I put in the array by default (say I add a simple object using createRecord like so)
add: function(record) {
this.records.addObject(App.Foo.createRecord(record));
},
and when the template is rendered I see anything listed here... but as I put in the comments above, if I decide to remove records or null out the list that is bound it doesn't seem to reflect this in any way.
Is it possible to bind a simple array as I have and yet remove items from it using something basic such as splice? or even a drastic self.records = []; ?
self.records.splice(i, 1);
Even when I query the client manually after the splice or empty work it returns 0
console.log(App.Foo.all().get('length'));
Initially I see records, but then I see they are gone (yet the html doesn't change)
I understood your question this way, that the following remark is the point your are struggling with:
response.forEach(function(data) {
//say I want to kill everything in the array here for some strange reason...
self.records = [];
//the template still shows the record ... not an empty list ?
}, this);
You are wondering, why your template is showing no empty list? It's because you did not tell Ember when to update the template. You can tell Ember this way:
App.Related = DS.Model.extend({
listings: function() {
return App.Foo.find();
}.property("App.Foo.records.#each")
});
Now Ember knows, whenever something is added or removed from your array, it should update the listings property of your model. And therefore it knows that your view needs rerendering.
One additional remark to the orignal question regarding "simple javascript arrays". When you use Ember, you actually do not instantiate simple js arrays. When you declare:
var a = []; // is the same as -> var a = Ember.A();
Ember does some magic and wraps in an enhanced ember version of an array (Ember.NativeArray), which enables you to use such property dependency declarations mentioned above. This enables Ember to use ArrayObservers on those arrays, although they may feel like a plain JS Array.
You need to use the set method when you modify properties and get when you return them, or else Ember won't be able to do its magic and update the template.
In your case, there is an additional problem, which is that in find(), you return a reference to records before your asynchronous getJSON call replaces it with a new empty array. The calling method will never see the new array of records. You probably want to use clear() instead.
Your model should look something like this:
App.Foo = DS.Model.extend({
name: DS.attr('string')
}).reopenClass({
records: [],
all: function() {
// can't use 'this.get(...)' within a class method
return Ember.get(this, 'records');
},
findAll: function() {
var records = Ember.get(this, 'records');
$.getJSON('/api/foo/', function(response) {
records.clear();
// in this case my json has a 'foos' root
response.foos.forEach(function(json) {
this.add(json);
}, this);
}, this);
// this gets updated asynchronously
return records;
},
add: function(json) {
// in order to access the store within a
// class method, I cached it at App.store
var store = App.get('store');
store.load(App.Foo, json);
var records = Ember.get(this, 'records');
records.addObject(App.Foo.find(json.id));
}
});
Note that the addObject() method respects observers, so the template updates as expected. removeObject() is the corresponding binding-aware method to remove an element.
Here's a working jsfiddle.

Backbone.js fetch not actually setting attributes

I have a basic backbone model, its urlRoot attribute is set and the corresponding target on the server side returns a correct JSON output (both JSON string and application/json header).
I call a fetch like this:
var athlete = new Athlete({ id: 1 });
athlete.fetch();
at this point if I add a
console.log(athlete);
I can see the model, and inspecting it in firebug I can open the attributes object and see all the values returned from the server.
BUT if I do a:
console.log(athlete.get('name'));
I get undefined (the name appears under the attributes in the DOM inspection I mentioned above)
also doing a:
console.log(athlete.attributes);
returns an object containing only {id: 1} which is the argument I passed while creating the model.
If I create the model like this:
var athlete = new Athlete(<JSON string copypasted from the server response>);
then everything works fine, the .get() method returns whatever I ask, and athlete.attributes shows all the values.
What am I doing wrong?
fetch is asynchronous, which means that the data won't be available if you immediatly call console.log(athlete.get('name')) after the fetch.
Use events to be notified when the data is available, for example
var athlete = new Athlete({id: 1});
athlete.on("change", function (model) {
console.log(model.get('name'));
});
athlete.fetch();
or add a callback to your fetch
var athlete = new Athlete({ id: 1 });
athlete.fetch({
success: function (model) {
console.log(model.get('name'));
}
});
or take advantage of the promise returned by fetch:
athlete.fetch().then(function () {
console.log(athlete.get('name'));
});
Just as a quick remark when using events in this example. It did not work with change in my case because this events fire on every change. So sync does
the trick.
var athlete = new Athlete({id: 1});
athlete.on("sync", function (model) {
console.log(model.get('name'));
});
athlete.fetch();

Backbone.js appending more models to collection

I have a following code that fetches data from the server and add to the collection.
// common function for adding more repos to the collection
var repos_fetch = function() {
repos.fetch({
add: true,
data: {limit:curr_limit, offset:(curr_offset*curr_limit)},
success:function() {
curr_offset++;
console.log(repos.length);
}
});
};
each time I call the function "repos_fetch", the data is retrieved from the server and added to the collection "repos".
My problem is that I want to APPEND to the collection, instead of REPLACING. So I put the option "add: true" there.
But below function looks like it's keep replacing the data in collection.
What's even strange is that if I remove the line "curr_offset++;" then the data gets appended!. "curr_offset" just increments, so I get different sets of data.
What's going on here?

Categories