How to automatically save on change in Backbone using localstorage? - backbone.js

Code:
class Session extends Backbone.Model
initialize: ->
#bind 'change', #save
console.log 'init'
class SessionList extends Backbone.Collection
model: Session
localStorage: new Store 'sessions'
sessions = new SessionList
a = new Session x: 'test'
sessions.add a
console.log a.get 'x'
a.set x: 'new'
console.log a.get 'x'
When loaded in a page with Backbone.localstorage, the console gives:
init
test
Uncaught TypeError: Cannot read property 'localStorage' of undefined
backbone-localstorage.js:70
Backbone.sync
_.extend.save
backbone-localstorage.js:70
Backbone.Events.trigger
backbone.js:304
_.extend.change
backbone.js:117
And when I comment out the #bind call, I get the expected:
init
test
new
I can also save manually successfully after a has been added to sessions with a call to a.save().
I guess the problem is that the Session constructor triggers the change event, and save() doesn't know what to do before a has been added to sessions? So I could instead do something like this:
class Session extends Backbone.Model
set: (fields, ops) ->
super fields, ops
if (this has been added to a Collection)
#save()
Is this the best way to do it? If yes, how do I fill in the if statement?

My suggestion would be to just call save instead of set. so replace this:
a.set x: 'new'
with
a.save x: 'new'
hope that works for you

Related

How to manually trigger a `change` event on a Backbone model?

I am trying to manually trigger a change event on a Backbone model that belongs to a collection. Here is a minimal example: (JSFiddle)
var PersonModel = Backbone.Model.extend({});
var PersonCollection = Backbone.Collection.extend({
model: PersonModel
});
var person = new PersonModel({ name: 'Spike' });
var people = new PersonCollection([person]);
person.trigger('change'); // throws error
console.log('triggered event successfully');
But I get the following error when I trigger the event: (In JSFiddle, open the Javascipt Console to see the full error)
Uncaught TypeError: Cannot read property 'previousAttributes' of undefined
How can I fix this?
(The reason I'm doing this is that in the actual code the model has a collection -- for example person.things = new ThingCollection(); -- and I want to manually trigger the change event on person when its collection changes)
The change event is expected to have parameters, as described in the event catalog.
The event should have (model, options) as parameters, where model is the model itself and options is the options object used with set. I can't recall off the top of my head if options is optional.
In your case, since you're just triggering the event manually you'll have to provide those arguments. The error your seeing is from some other code trying to access model.previousAttributes with model being undefined.
TL;DR:
person.trigger('change', person, {});
And you might be able to omit the {}.

Backbone Model is a function?

I have the following function, which fails when called:
getAll: function() {
return _todos.toJSON();
}
_todos.toJSON is not a function it tells me.
Printing _todos gives me a function for some reason function child().
Why is that the case?
Here's my Backbone.Model:
var _todos = Backbone.Model.extend();
The getAll() function is sitting in my Collection:
var TodoCollection = Backbone.Collection.extend({
model: _todos,
getAll: ...
});
Edit:
I'm actually connecting Backbone with React, so that might change how I do this.
In addition to getAll I have this:
areAllComplete: function() {
return _.every(this.pluck('complete'), true);
}
An example I've been following seems to put getAll and areAllComplete in the Model and doesn't use Collection at all. I couldn't make sense of it, and since I want this to be restful down the road, I added the Collection and moved getAll and other one inside of it.
Perhaps, this is not what I want.
Edit 2:
According to a warning, the output of getAll is expected to be an Object not an array. I should probably add those function to Model not Collection. Need to think about this more.
I think this turned into another question...
The collection model property is used to specify what model class the collection contains and is used to create the proper models when you pass the raw data to your collection. Additionally based on your code if it did work you would have had a collection with just one model.
Aside from that in order to get the JSON of all the models in your collection you can call it's toJSON method
for example todoCollection.toJSON();
Or if you specifically want it in a getAll function (maybe you want to do something else before returning the data) you can do the following
var TodoCollection = Backbone.Collection.extend({
model: Todo,
getAll: function () {
//do something
return this.toJSON();
}
});
//here we are passing in the data directly, but you might get it with a fetch call
var todoStore = new TodoCollection(models);
var todosJson = todoStore.getAll();
Another thing to note is the backbone naming convention is to use PascalCase for classes and camelCase for instances .
That's because Backbone.Model.extend returns a constructor function. When you pass it to a collection via the model property you're just letting the collection know which kind of models it should hold.
To get the JSON for an entire collection, call toJSON() on the collection instance.
var collection = new TodosCollection();
// add models
collection.toJSON();
If you want JSON for one specific model then get a reference to it via the collection API (at, findWhere, get etc) and call toJSON() on that reference.
var model = collection.at(0);
model.toJSON();

how to get model with backbone if i have it's URI?

I'm using django+tastypie+backbone.js with backbone-relational.
Let's say i have model(coffee script):
class Track extends Backbone.RelationalModel
And somehow i get the first object's URI:
api/track/1
Result in JSON have to be something like:
{
'title': 'Mytrack',
'length': '120'
}
How can i get full model JSON with all attributes using this URI?
You need to set the model's urlRoot (/api/track), then create a new model with the id you want (1), and call .fetch on the model. The fetch call will be asynchronous, so you need to wait for the success callback before you can access the full properties:
class Track extends Backbone.RelationalModel
urlRoot:"/api/track"
track = new Track id:1
track.fetch
success:(model) -> console.log model

How to reduce request to my database?

i have a partial view with a gridview in it. so when user clicks add button request will redirect to Add method of unitsController. After add it to database I should refetch all data from database. Is there a way to prevent controller from get all database records?
Below is my current controller
public class UnitsController : Controller
{
TList<Units> model=null;
public ActionResult UnitsPartial()
{
if(model==null)
model = database.GetAll();
return PartialView(model);
}
[HttpPost]
public ActionResult Add(Units unit)
{
if (ModelState.IsValid)
{
database.Save(unit);
model.Add(unit);
}
return PartialView("UnitsPartial", model);
}
In the last line I want to use return PartialView("UnitsPartial", model) instead of return database.GetAll() to prevent a database query. But model is null in Add method.
Is my approach correct or not? And why is model is null in add() method?
--UPDATED
first of all control redirect to UnitsPartial() and will fill model object correctly. after press add button, control will redirect to Add(...) method but this time model is equal to null !!!
what is the problem with it? i tried to pass model.Clone() to partial view
return PartialView("UnitsPartial", model.Clone());
but the result is the same
You can't cache values in the controller class like this using member variables. HTTP is stateless and MVC 3 follows that approach. Each individual call to an action method is going to have a brand new instance of Controller class with the model set to null.
So start by calling database.GetAll() in both action methods and then ask the question "How do I make this more efficient".
I don't know if it is the right approach or not, since I do not fully understand what you try to do, but your model is null because you initialize it as null when the controller is created. you should do a model = new TList() before adding something to it....

Codeigniter Undefined property: xxxx_model::$db only from Model

First the Model class:
class Xxxx_model extends Model
{
function XxxxModel()
{
parent::Model();
$this->load->database();
}
function isInDatabase()
{
// Please ignore the sql query, it's just to show some random sql code with results
11. $result = $this->db->query('SELECT * FROM someTable WHERE ...');
$numberOfRows = $result->num_rows();
...
return $test;
}
}
Now the controller:
function someLogic()
{
$this->load->model('xxxx_Model', 'xxxxModel'); // not necessary to specify
$this->xxxxModel->isInDatabase();
}
When I run this I get the error:
Severity: Notice --> Undefined property: Xxxx_model::$db .../xxxx_model.php line 11
I have no idea why this is. If I put the db code in the controller it seems to work, it's only with this setup in the model that it fails. I can't for the life of me figure out where the code is astray...
You have to load the db library first. In autoload.php add below code,
$autoload[‘libraries’] = array(‘database’);
add library 'datatabase' to autoload.
/application/config/autoload.php
$autoload['libraries'] = array(
'database'
);
Propably you're started new project, like me ;-)
To add to atno's answer:
class Xxxx_model extends Model
{
function XxxxModel() //<--- does not match model name Xxxx_model
{
parent::Model();
$this->load->database();
}
Basically, you are not constructing the class or the parent class Model. If you are on PHP5, you may use __construct(), otherwise you must match the class name exactly, regardless of what alias you load it with in your controller. Example:
class Xxxx_model extends Model
{
function __construct()
{
parent::__construct(); // construct the Model class
}
}
I may be mistaken (haven't used 1.x in a while), but if you construct the Model class, there's no need to load the database if you are using the default connection settings in config/database.php, it should already be loaded for you.
If function XxxxModel() isn't your constructor, you're not loading the database by calling $this->xxxxModel->isInDatabase();
Try autoloading the database library from within autoload.php, or create a proper constructor in your model.

Resources