Deleted items from collection persist in local storage - backbone.js

I have a user model in backbone as follows
var User = Backbone.Model.extend({
initialize: function(){
},
defaults: {
firstname: '<FirstName>',
lastname: '<LastName>',
email: '<EmailAddress>'
}
});
and collection:
UserCollection=Backbone.Collection.extend({
model : User,
localStorage:new Backbone.LocalStorage("UsersCollection")
});
When I delete the model, I use the following code
var model=this.collection.get(cid);
this.collection.remove(model);
cId comes from the view, which i have not shown here. The model is removed from teh collection as I can render it just fine and the model is not there. (cid) is an attribute on the delete button.
Anyways, when I refresh the page, the deleted item reappearrs. Editing works fine. After refresign, the item isn't there anymore. Also, if I try to do Collection.sync(), I get an error
Cannot read property 'localStorage' of undefined
model.destroy() gives an error too: A "url" property or function must be specified
How do I remove the item from local storage

You should always define either an url of collection or an url of model.
Even if you are using only local storage.
Just try to specify url of collection as "/".

Related

How to specify filtered Backbone collection for Marionette view

I have a Marionette composite view that displays a collection, which I set in my Application start handler:
App.on('start', function() {
Backbone.history.start({pushState: true});
// I load up this.appsCollection in my before:start handler
var tblView = new this.appsTableView({
collection: this.appsCollection
});
this.regions.main.show(tblView);
});
This works as expected, displaying my entire collection. In my models, I have a state field, and I want to display only models with state 0. I tried:
collection: this.appsCollection.where({state: 0})
but that doesn't work. I actually want to display states in 0 and 1, but I'm trying to just display state in 0 for right now.
What am I missing?
The problem probably resides in that .where() doesn't return a collection, but an array. http://backbonejs.org/#Collection-where This was supposedly to maintain compatibility with underscore.
If you change the line to:
collection: new Backbone.Collection( this.appsCollection.where( { state: 0 } ))
Does that help?
I was able to override the filter method in my Marionette CompositeView:
http://marionettejs.com/docs/v2.4.3/marionette.collectionview.html#collectionviews-filter

Backbone.js How to make collection only accept one class of model

I am new to backbone.js and I am trying to learn it. In the code below I want my collection called "JokesCollection" to only accept adding models of the class "Joke". How do I do achieve this? When setting "Collection" attribute "model" to a certain model, isn´t the collection supposed to only accept that model class and ensure homogeneity? Don´t seam so. When I assign attribute "model" in the "JokesCollection" class to "Joke" it still accepts adding models of class "Persson" witch is not what I want. I only want it to accept adding models of class "Joke".
Joke = Backbone.Model.extend ({
initialize: function(){
console.log("Joke was created");
},
defaults: {
joke : "",
date : "0",
}
});
JokesCollection = Backbone.Collection.extend({
initialize: function(){
console.log("JokesCollection was created");
},
model: Joke // <=== Isn´t this supposed to ensure that the collection only accepts models of class "Joke"?
});
Person = Backbone.Model.extend ({
initialize: function(){
console.log("Person was created");
},
defaults: {
username: "default",
password: "default",
email: "default"
}
});
var person1 = new Person({username:"masterMind"});
var joke1 = new Joke({joke:"Girls are cute and funny hahahaha"});
jokesCollection = new JokesCollection();
jokesCollection.add(joke1);
jokesCollection.add(person1); // This adds a model of class "Person" to the collection. Witch is not what I want. It is not supposed to work! I want "jokesCollection" to only accept models of class "Joke".
console.log(jokesCollection.length); // length gets increased by 1 after adding "person1" to "jokesCollection". Again, it is no supposed to work from my point of view. I want "jokesCollection" to only accept models of class "Joke".
console.log(jokesCollection);
From official docs:
model collection.model
Override this property to specify the model class that the collection
contains. If defined, you can pass raw attributes objects (and arrays)
to add, create, and reset, and the attributes will be converted into a
model of the proper type.
Looks like will have to re-write add method something like this :
add: function(models, options) {
var modelClass = this.model;
isProperIns = this.models.every.(function(model){
return model instanceof modelClass;
});
if (!isProperIns) {
throw new Error("Some of models has unacceptable type")
}
return this.set(models, _.extend({merge: false}, options, addOptions));
}
The purpose of a Collection's model property is not to limit which models the Collection can accept. Rather, that property defines the Model class which the Collection will use when it needs to create a new Model. For instance,when you pass an object literal of Model attributes (as opposed to an instantiated Model) to JokesCollection.add, or when you fetch models in to a JokesCollection, Backbone will use Joke as the Model to instantiate those new additions to the Collection.
There are two ways to ensure your JokesCollection is only populated with instances of Joke. The first way is to never add Model instances to the JokesCollection directly, and instead either:
A) Bring new Jokes in from the server by calling fetch on a JokesCollection
B) add only "raw" Model attributes to the JokesCollection; don't add instantiated Models
However, if you're concerned about a developer accidentally adding a non-Joke Model to the Collection, your other option (as first suggested by #Evgeniy) is to overwrite your JokesCollection's add method. Unlike #Evgeniy's answer though I would not recommend re-writing Backbone's internals. Instead, I would use a simple overwrite that just calls the base Backbone method if possible:
add: function(models, options) {
if (models instanceof Joke) {
// Use the normal Backbone.Collection add method
return Backbone.Collection.prototype.add.call(this, models, options);
}
var allModelsAreJokes = _(models).all(function(model) {
return model instanceof Joke;
));
if (allModelsAreJokes) {
// Use the normal Backbone.Collection add method
return Backbone.Collection.prototype.add.call(this, models, options);
}
// Handle the case where non-Jokes are passed in; either:
// A) convert whatever was passed in to be a Joke:
// var rawModels = _(models).isArray() ? _(models).invoke('toJSON') : model.toJSON();
// return Backbone.Collection.prototype.add.call(this, rawModels, options);
// B) just don't add anything
}

How to call fetch method of Backbone Collection passing Id

I want to fire fetch method on Backbone Collection which would pass an Id parameter similar to what happens in Model.fetch(id)
E.g.
var someFoo= new Foo({id: '1234'});// Where Foo is a Backbone Model
someFoo.fetch();
My Backbone collection:-
var tasks = backbone.Collection.extend({
model: taskModel,
url: '/MyController/GetTasks',
initialize: function () {
return this;
}
});
In my View when I try to fetch data:-
var _dummyId = 10; //
// Tried approach 1 && It calls an api without any `id` parameter, so I get 500 (Internal Server Error).
this.collection.fetch(_dummyId);
// Tried approach 2 && which fires API call passing Id, but just after that
// I am getting error as below:- Uncaught TypeError: object is not a function
this.collection.fetch({
data: {
id: _dummyId
}
});
Found it very late : To cut short the above story I want something like Get /collection/id in backbone.
Thank you for your answers, finally I got the solution from Backbone.js collection options.
Apologies that I couldn't explain the question properly while for same requirement others have done brilliantly and smartly.
Solution : I can have something like :-
var Messages = Backbone.Collection.extend({
initialize: function(models, options) {
this.id = options.id;
},
url: function() {
return '/messages/' + this.id;
},
model: Message,
});
var collection = new Messages([], { id: 2 });
collection.fetch();
Thanks to nrabinowitz. Link to the Answer
As mentioned by Matt Ball, the question doesn't make sense: either you call fetch() on a Collection to retrieve all the Models from the Server, or you call fetch() on a Model with an ID to retrieve only this one.
Now, if for some reason you'd need to pass extra parameters to a Collection.fetch() (such as paging information), you could always add a 'data' key in your options object, and it may happen that one of this key be an id (+add option to add this fetched model rather than replace the collection with just one model)... but that would be a very round-about way of fetching a model. The expected way is to create a new Model with the id and fetch it:
this.collection = new taskCollection();
newTask = this.collection.add({id: 15002});
newTask.fetch();
In your code however, I don't see where the ID is coming from, so I am wondering what did you expect to be in the 'ID' parameter that you wanted the collection.fetch() to send?

Backbone JS Models and Collection URLs

If I have a model named "Book" and a collection named "Library" defined as below:
Book
app.Book = Backbone.Model.extend({
defaults: {
title: 'No title',
author: 'Unknown'
}
});
Library
app.Library = Backbone.Collection.extend({
model: app.Book,
url: '/api/books'
});
When I call BookInstance.save() how does it come up with the URL to use? Does it derive it from the collection?
In Backbone.model there are 2 options: url and urlRoot. What is the purpose and difference between these?
In Backbone.collection, there is a single parameter url. Is this always the GET request for the RESTFUL api?
Basically, there are 3 possibilities to construct a model's url:
If the model object exists in a collection then its url method will return an address composed of the collection.url and model.id: [collection.url]/[id].
If you don't want to use a model inside the collection, then model.urlRoot's value can be used instead of the collection.url fragment, resulting in the following pattern: [urlRoot]/[id].
Finally, if you're NOT planning to persist more that one model of a given type to the server or will be defining URLs for each model upon their creation, you can directly assign a value to model.url.
Collections send only GET requests — to get an array of models' JSON data. For saving, removing, and updating, the individual model's save() (POST/PUT/PATCH) and destroy() (DELETE) methods are used.
Here's the source code of Backbone.Model.url, which should help you:
url: function() {
var base =
_.result(this, 'urlRoot') ||
_.result(this.collection, 'url') ||
urlError();
if (this.isNew()) return base;
var id = this.get(this.idAttribute);
return base.replace(/[^\/]$/, '$&/') + encodeURIComponent(id);
}
In model
urlRoot is used for the Model.
url is used for the instance of the Model.
So if urlRoot exists
then book.fetch() will fetch the data given id, for example
var Book = Backbone.Model.extend({urlRoot: 'books' });
var book = new Book({id: 1});
book.fetch(); // will get /books/1
var Book = Backbone.Model.extend({});
var book = new Book({url: 'books/1'});
book.fetch(); // will get /books/1
var Books = Backbone.Collection.extend({model: Book});
var books = new Books({ /*....*/ });
books.fetch(); // will get /books/
You can refer the backbone Model urlRoot source code here
I hope it makes sense to you, good luck.

adding a model to a relational-model's collection in an ajax event

I have a recipe model, and a recipe has an ingredientlist collection which stores a bunch of ingredients.
When I add an ingredient to the ingredient list from a form submit, I have to get an 'id' from the server, so I do an ajax request, get the id, and am trying to then add the ingredient to the model.
In my ingredientlist.view, I have
initialize: function(){
this.recipe = this.model;
},
get_ingredient: function(ingredient){
var ingredient_id = new MyApp.Models.Ingredient;
ingredient.url='/ingredients/?ing='+encodeURIComponent(ingredient_array[i]);
ingredient.fetch({
success: function() {
this.recipe('add:ingredients', function(ingredient,ingredientlist){
});
},
error: function() {
new Error({ message: "adding ingredient" });
}
});
}
I didn't include the function which triggers the 'get_ingredient', because it I am getting the ajax fine, so the problem isn't in triggering the 'get_ingredient'.
I get the errorUncaught TypeError: Property 'recipe' of object [object DOMWindow] is not a function
using the existing code.
what is the best way to accomplish something like this?
First of All i'm a newbie too with backbone.js!
So my thoughts is :
U need to bind your get_ingredient in your View : look bind to trigger your functions!
Try to pass the Context (this) to "get_ingredients"
This is just my 5 cents

Resources