I have been trying for hours now, without luck. If you could see on the image, I have mildly complex model. Image is taken from Chrome in debug.
I need to delete a model from collection, also I need to be able to change the URL where the backbone will shoot its ajax for delete. So in essence, this is my model structure:
attributes:
favorites {
bookmarkedArticles: [{id: 123123},{id: ...}],
bookedmarkedSearches: [{}],
dispatchesMailds: []
}
How can I delete model in bookmarkedArticles with id of 123123?
I have tried this:
var model = new metaModel(
{
favourites: {
bookmarkedArticles: {
id: "123123"
}
}
}
);
model.destroy();
also this
aamodel.headerData.collection.remove(model);
No success at all.
The information provided is not giving a lot of details, but I will try to answer considering two scenarios:
Option A:
You are trying to delete a model in the collection that has bookmarkedArticle.id="123123". if that is the case and considering the bookmarkedArticles it is just an Array of objects, I would suggest to filter the Collection using the underscore method filter and then delete the models returned by the filter.
var id = 123123;
var modelsToDelete = aamodel.headerData.collection.filter(function(model){
// find in the bookmarked articles
return _.find(model.get('bookmarkedArticles'), function(ba){
return (ba.id === id);
});
});
_.each(modelsToDelete, function(model){
model.destroy();
});
Option 2: If you want to remove the bookmarked article '123123' associated to your main model using just the 'destroy' method, firstable you have to convert 'bookmarkedArticles' to a Backbone.Collection as it is just an Array of Objects, there are some utilities for Backbone that allows you to do this easily:
https://github.com/blittle/backbone-nested-models
But by default this is not possible, then, If you want to remove the 'bookmarkedArticle' you can create the Backbone.Model and then use the method destroy. Example:
var BookmarkedArticle = Backbone.Model.extend({
url: function(){
return '/bookmarkArticle/' + this.id;
}
});
new BookmarkedArticle({"id": "123123","master": "5",...}).destroy();
Hope this information is useful and provide some guidance to solve your problem.
Related
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);
}
});
I'm developing a RESTful API for a Quiz app, which is going to be built with Backbone.js and Marionette. I'm quite new to backbone and was wondering what de best URL structure would be. I have the following resources:
Answer,
Question which contains Answers,
Question Group which contains Questions,
Quiz which contains Question Groups.
Two possible URL structures come to mind:
GET /quizzes/:id
GET /quizzes/:id/questiongroups
GET /quizzes/:id/questiongroups/:id
GET /quizzes/:id/questiongroups/:id/questions
GET /quizzes/:id/questiongroups/:id/questions/:id
GET /quizzes/:id/questiongroups/:id/questions/:id/answers
or:
GET /quizzes/:id
GET /quizzes/:id/questiongroups
GET /questiongroups/:id
GET /questiongroups/:id/questions
...
Now, I have been trying to use both of these options. With the first one, I can't figure out how to define the collections as a property of the parent models in Backbone so that I can use fetch() on them. The problem with the second option is a bit different: as I understand it, Backbone derives the url for a model from its collection, but the collection is a child of another resource, whereas the url for getting a single resource uses another collection, namely the global set of resources.
I'm pretty sure I'd have to override url() in both cases. I tried some things but didn't come up with anything useable at all. Also, I'd rather not override every single url()-model in the app, changing the API structure to suit the preferences of Backbone seems like a better option to me.
Any pointers as to what seems the right way to do it with Backbone would be great!
Thanks
If questiongroups can only appear in a single quiz, then the first option (the hierarchical one) is an obvious choice. To comply with RESTful conventions, you might want to consider using singular nouns instead: /quiz/:id/questiongroups/:id/question/:id/answer/:id
To solve your fetching problem, I would recommend using nested backbone models as per this answer: https://stackoverflow.com/a/9904874/1941552. I've also added a cheeky little parentModel attribute.
For example, your QuizModel could look something like this:
var Quiz = Backbone.Model.extend({
urlRoot: '/quiz/', // backbone appends the id automatically :)
defaults: {
title: 'My Quiz'
description: 'A quiz containing some question groups.'
},
model: {
questionGroups: QuestionGroups,
},
parse: function(response){
for(var key in this.model){
var embeddedClass = this.model[key];
var embeddedData = response[key];
response[key] = new embeddedClass(embeddedData, {
parse:true,
parentModel:this
});
}
return response;
}
});
Then, your QuestionGroups model could have the following url() function:
var QuestionGroups = Backbone.Model.extend({
// store metadata and each individual question group
url: function() {
return this.parentModel.url()+'/questiongroup/'+this.id;
}
});
Alternatively, if you don't need to store any metadata, you could use a Backbone.Collection:
var QuestionGroups = Backbone.Collection.extend({
model: QuestionGroup,
url: function() {
return this.parentModel.url()+'/questiongroup/'+this.id;
}
});
I'm afraid I haven't tested any of this, but I hope it can be useful anyway!
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.
Hi all I am creating my first Backbone.js app. It is basically a collection that renders the data in a table. What I want to do is to be able to filter and sort data.
What is the best way to do it? Should I use the Router or store some params that render will take into consideration.
I think the Router will get really complex soon as I am going to have 3-4 filters and 1 order option.
What do you think?
In my backbone-based project, I've subclassed Backbone.Collection to allow Controller to add arbitrary GET parameters to it.
Here is a snippet for you:
RailsCollection = Backbone.Collection.extend({
initialize: function() {
_.bindAll(this, 'url');
},
url: function() {
var base = this.baseUrl || this.model.prototype.baseUrl;
if(!this.params) {
return base;
} else {
return base + '?' + $.param(this.params);
}
}
});
I would add methods on my collections for the filtering and sorting, and using a view to just render an arbitrary collection, that might be filtered or ordered.
For ordering there is a built in hook: http://documentcloud.github.com/backbone/#Collection-comparator
For filtering, check out the underscore helper methods on collections, and extend with your own.
You could for example have a collection.doFiltering([filter1, filter2, filter3]);
that returns a filtered array.
I've got the following model with nested collection
var Mdl = Backbone.Model.extend({
initialize: function() {
// collection
this.col1 = new NestedCollection();
},
...
});
I would like to send the data for both the model and the models in the collection in one request looking something like:
{
att1: val,
col1: [{obj1: val}, {...}]
}
I'm unsure about the best way to hand the data in the request to the nested collection (col1). I can't do ...
var Mdl = Backbone.Model.extend({
initialize: function() {
// collection
this.col1 = new NestedCollection(this.get('col1');
},
...
});
... because at the time of initialize is called the parse function of the model has not been called which means that the attribute col1 is empty, another solution I thought of was to listen for the change in the parent model like...
model.bind("change:tags", function() {
model.col1.refresh(model.get('col1'));
});
however this solution feels a little heavy handed and might potentially break any
this.col1.bind("add", function() {})
and
this.col1.bind("remove", function() {})
function set-up on the collection.
Has anyone got any idea of the 'official' way of doing this?
Thanks.
The "official" way is to override the parse method:
http://documentcloud.github.com/backbone/#Model-parse
In your specific case, what I would probably do is, in the parse method, build the nested collection from the col1 data, delete it from the results, then hand the results on. Backbone will then turn the rest of the data into properties.
I have not tried this, so I'm not 100% sure it works:
parse: function(response) {
this.col1 = new NestedCollection(response.col1);
delete response.col1
return response
}
Edit: Nov 28th 2012
Harm points out that this might not be the best way to do it any more. The original answer was written quite a while ago, and the original question indicated that the user wanted the collection to be a property on the model (not an attribute), but Harm has a point that having the collection as an attribute is a more accepted way of doing it these days.
Today, you could use something like Backbone-Relational to handle a lot of this stuff for you, or, if you wanted to do it yourself, and have the collection as a model attribute, you could do something like:
Building = Backbone.Model.extend({
parse: function(response) {
console.log("Parse Called");
response.rooms = new Rooms(response.rooms);
return response;
}
});
Room = Backbone.Model.extend({});
Rooms = Backbone.Collection.extend({
model: Room
});
science_building = new Building();
science_building.fetch(
{success: function(model,resp) {console.log(resp);}}
);
With a model fetch response like:
{ id: 1,
name: "Einstein Hall",
rooms: [
{id:101, name:'Chem Lab'},
{id:201, name:'Physics Lab'},
{id:205, name:'Bio Lab'}
]
}
Resulting in a Building model that allows:
science_building.get('rooms').get(101).get('name') // ==> "Chem Lab"
A working jsFiddle example: http://jsfiddle.net/edwardmsmith/9bksp/