Using BackFire.js with Chaplin.js - javascript

I want to use Firebase's backfire.js in my chaplin.js application.
Is it possible to use both Chaplin.js Collection and backfire.js Collection?
Maybe extending the Chaplin.js Collection and using the backfire.js Collection as a mixin, something like this?
Chaplin = require 'chaplin'
Model = require 'models/base/model'
BackBone = require 'backbone'
module.exports = class Collection extends Chaplin.Collection
_(#prototype).extend BackBone.FireBase.Collection
model: Model
Firebase seems to add itself to the Backbone object, in the above example how I should reference it from my code?

[Update]
Backfire's special Firebase Collection object makes several references to Backbone.Collection, which tripped me up for awhile. I replaced this special object entirely to overwrite these references, but there's probably a much better way that my fragile mind cannot yet handle.
..a gist! https://gist.github.com/dustinlarimer/6800730
[Prev]
One quick observation.. drop the 2nd syllable capitalization on Backbone and Firebase:
_(#prototype).extend Backbone.Firebase.Collection
You also shouldn't need to include Backbone:
Model = require 'models/base/model'
module.exports = class FirebaseModel extends Model
_(#prototype).extend Backbone.Firebase.Model
initialize: ->
console.log 'init FirebaseModel'
super

Related

Backbone ignore posting specific attributes on save? [duplicate]

I'm using Backbone with Rails. I have a model that I can create and destroy just fine. When I edit, though, I get this error:
Can't mass-assign protected attributes: created_at, id, updated_at
That makes sense. Those attributes are protected and they should be protected. Backbone shouldn't be trying to update these attributes, but Backbone doesn't know better.
One option, of course, would be to remove params[:created_at], etc. in my Rails controller, but I can imagine that getting really un-DRY pretty quick, and plus it just seems wrong to have to do that.
Is there a way for me to tell Backbone not to include these attributes in its forms?
Either don't send them to the client so that your Backbone model never knows about them or override toJSON in your model to exclude them.
The default toJSON implementation is very simple:
toJSON: function() {
return _.clone(this.attributes);
}
so you can replace it with this:
toJSON: function() {
var attrs = _(this.attributes).clone();
delete attrs.created_at;
delete attrs.updated_at;
return attrs;
}
You could even monkey patch that right into Backbone.Model.prototype if that made sense to you.
The downside of altering toJSON is that toJSON tends to do double duty in Backbone:
toJSON is used to serialize models and collections for the server.
toJSON is used to serialize models and collections for views.
If you still want to use updated_at and created_at in views then I'd recommend adding another method, say serialize_for_view, that does what the standard toJSON does:
serialize_for_view: function() {
return _(this.attributes).clone();
}
and then use things like var html = this.template({m: this.model.serialize_for_view()}) to build your view's HTML. You could also monkey patch serialize_for_view into Backbone.Model.prototype if you wanted to use it everywhere.
I found that putting
model.unset("created_at");
model.unset("updated_at");
model.save()
fixed the problem. This won't work if you need those attributes, but if they are not needed, this works.

Dependency Injection in a distributable Javascript library?

We are using Backbone to create reusable components. We create controllers in order to setup bindings between models and views. We wish to offer people the ability to replace the models and views with their own implementations.
Since the majority of people will use the the components we provide, I don't want to force developers to configure or create anything that isn't different from the defaults.
This means that someone should be able to pass an instance of an object they want to use as a model or view to the controller, all configured and setup and ready to go, or they can pass in some configuration to what the controller will use by default.
I am not sure what the best approach is.
// Idea #1
var controller = new Controller({
dependencyA: {
conf: { // config for depedencyA }
},
dependencyB: {
conf: { // config for dependencyB }
class: custom.implement.Class
}
});
In this approach, the user doesn't have control over how to instantiate the object. What's bad about this is, for example, Backbone models take two arguments in the constructor while views only take one.
// Idea #2
var controller = new Controller({
dependencyA: {
args: ['arg1',{
opt1: 'opt-value',
}]
},
dependencyB: {
args: ['a','b','c']
class: custom.implement.Class
}
});
Args would be the arguments passed to a constructor. This means the controller calls the constructor with the args array, and again this only really benefits you if you're passing in custom configuration for default dependencies. If you want to pass your own implementation it's more awkward.
// Idea #3
var controller = new Controller({
dependencyA: new outOfBoxModel({ // configuration }),
dependencyB: new custom.imeplement.Class('a','b','c')
});
In this approach, the user is forced to instantiate the out of box model. If the model's default settings are all appropriate though, then the user is doing unnecessary work. The only bit they HAVE to do is create an instance of their own custom implementation.
I am not sure what the best approach would be here?
Of the three approaches, I most prefer approach number 3. Here is why:
It is more consistent than the other approaches. In the 3rd approach, the user only has to learn to pass in constructed instances of dependencies into the controller. In the other approaches, the user has to pass in either args, or args and a class name.
It does not violate the Single Responsibility Principle. In the first two approaches, your controller is made responsible for constructing and configuring its dependencies. This doesn't feel like dependency injection at all! I think it's better, and simpler, to leave the construction of dependencies to the user or another part of your application. In my opinion, its not a terrible thing to force the user to construct their own implementations - it gives them the freedom to define their constructors however they want, rather than forcing you to define and maintain constructor APIs for the Controllers dependencies, and forcing the user to conform to them.
A different idea:
If you have this freedom in your application, I would consider putting your Controller construction logic in a factory class or method:
var createController = function(config) {
// Parse, validate, extract relevant config items
// var a = create dependency a
// var b = create dependency b
return new Controller(a, b);
}
This approach allows you to be as fancy as you want with your definition of config - you could support all three of the config definitions you provided in your original post - although I wouldn't recommend that :-). At a minimum, I would have the factory method support a zero args invocation (in which case it would return the default construction of Controller) and one of your preferred config definitions.

Storing model or collection-specific views inside model or collection constructors

I realize there is many solutions to this but I was wondering what the community's opinion is.
I have a series of models and collections. Each model has a number of views like details, edit, print, aside, help, etc. Collections have views that often have the same names (ie: aside, help, etc).
One requirement I have is that I need to structure my code in modules. The application should have no trace of a module's functionality if the module is NOT loaded. This may happen if, say, a user has no permissions to view, edit, etc other users. So the "Users" module would not even be loaded.
So...
I thought a good place to store view definitions for a model could be the model's constructor and for a collection in the collection's constructor. For example:
var User = (function(){ // module definition
// model definition
var Model = Backbone.Model.extend({
initialize: function() {
// ...
}
},{
Views: {
Details: Backbone.View.extend({
// ...
}),
Aside: Backbone.View.extend({
// ...
}),
Help: Backbone.View.extend({
// ...
})
}
});
// collection definition
var Collection = Backbone.Collection.extend({
model: Model,
initialize: function() {
// ...
}
},{
Views: {
Aside: Backbone.View.extend({
// ...
}),
Help: Backbone.View.extend({
// ...
})
}
});
// add more code here
return { // make model and collection public
Model: Model,
Collection: Collection
};
})(); // end module definition
I realize I could have my views live elsewhere but would this approach have any considerable drawbacks that I may not be aware of? Perhaps memory leaks or something less obvious?
Thank you!
I think you would be better off not adding your views as "class methods" onto your models and collections. Because of the nature of JavaScript's prototypical inheritance, you aren't really adding class methods so much as properties to the constructor functions for your model types. As to whether or not this is going to cause you issues like memory leaks, I can't say.
I would instead say that, unless you have an unlisted compelling reason for using this structure, you are better off just grouping your views on simple objects.
If the goal is to modularize your code I would take advantage of something like require.js or Marionette modules or just grouping "related" code in an IIFE.
If you are interested in knowing more about what exactly happens to the classProperties that are passed into the Backbone.Model.extend method then I would recommend looking directly at the annotated source.
Have a look at require.js. With it you should be able to add logic that deals with module loading. In general you should still have a look at it, works great for organising backbone applications, especially with the text plugin.

Why is Backbone model sending duplicate attributes to server on save?

I'm writing a practice Backbone app, with Rails backend API, and I'm confused about the behavior of save on Backbone models.
Let's say a Team has many Players, and I want to save a team with numerous players in a single POST.
So in Rails I have:
class Team < ActiveRecord::Base
has_many :players
accepts_nested_attributes_for :players
end
class Player < ActiveRecod::Base
belongs_to :team
end
and for backbone client, I have a Player model and a Players collection defined (not shown)
and then the containing Team model (NOTE: no Teams collection)
Demo.Models.Team = Backbone.Model.extend({
urlRoot: '/teams',
defaults: {
'team_size': 12
},
initialize: function() {
this.players = new Demo.Collections.Players());
},
toJSON: function() {
var json = _.clone(this.attributes);
json.players_attributes = this.players.map(function(player) {
return player.toJSON();
});
return json;
}
}
When I examine my stringified JSON in the browser, everything looks good:
{"team_size":12, "players_attributes":[{"name":"Fred"},{"name":"Jim" },{"name":"Mark"}]}
Checking the server logs, the lone top level attribute ('team size') is repeated, once at the top level, and then repeated under a root key.
Started POST "/teams" for 127.0.0.1 at 2012-06-07 13:39:40 -0400
Processing by TeamsController#create as JSON
Parameters: {
"team_size"=>12, "players_attributes":[{"name":"Fred"},{"name":"Jim" },{"name":"Mark"}]},
"team"=>{"team_size"=>12}
}
I have a few questions:
What's the best way to ensure the player_attributes are nested inside the root key? I (So that I can do a nested save inside TeamController, in the standard rails manner: (i.e. Team.create(params[:team]) ) I can accomplish this with some javascript hackery inside toJSON, but I'm guessing there's an easier, cleaner way.
Is this standard, desirable behaviour? To send duplicates of attributes like this? I guess there's no harm, but it doesn't smell right.
Am I not defining the url / urlRoot correctly or some such?
thanks
1- You have to override the toJSON method in order to include the model name as the root of the JSON element sent to the server.
toJSON: function() {
return { team: _.clone( this.attributes ) }
},
Since you are already messing and overriding this method I don't see any reasons not to go this way.
2- This is a very strange behavior you're describing. Try:
class Team < ActiveRecord::Base
self.include_root_in_json = false
end
It will probably eliminate Rails duplicate params parsing. Another advantage you get from this is that Rails won't include the team as a root element of its generated JSON to the client.
3- Your definition of urlRoot is just fine.
I arrived here while looking for same issue. So even it's an old question I think it's worth giving the answer.
I actually found a Rails setting that explain these duplicate attributes: wrap_parameters
http://apidock.com/rails/v3.2.13/ActionController/ParamsWrapper/ClassMethods/wrap_parameters
Just set it to an empty array, and rails won't try to wrap parameters coming from your JSON requests.
Although you can use the toJSON hack mentioned by others, this is actually not such a good idea. For one, it produces an inconsistent result between sync and save with {patch: true} (this inconsistency is because the sync method calls toJSON if you don't patch, but doesn't call toJSON if you have patch set to true)
Instead, a better solution is to use a patched version of Backbone that overload the sync method itself. The backbone-rails gem does this automatically, or you can pull backbone_rails_sync.js into your own app. A more complete answer to this question can be found here: Backbone.js and Rails - How to handle params from Backbone models?

How can I structure a Backbone.View based plugin so that its nested views can be extended individually?

I am designing a generic object browser plugin which functions similar to OS X's Finder in column view. I have divided up the interface in to several nested views, the browser, the columns and the objects.
I will be using this plugin in several scenarios where the browser view, object view and column view may or may not need to be customised. Sometimes the objects will be files and folders for example.
This is OS X's Finder in column view in case you don't know what it looks like.
At the moment I am using RequireJS to pass around the dependencies however in order to simply inherit and extend the ObjectView, I must replace the entire stack.
Is there any better structure where the plugin can be extended but only part of?
BrowserView.js
var BrowserView = Backbone.View.extend({
open: function () {
var collectionView = new CollectionView( {collection: objects} );
}
});
CollectionView.js
var CollectionView = Backbone.View.extend({
render: function () {
this.collection.each( function (object) {
var objectView = new ObjectView( {model: objects} );
objectView.bind('click', this.select, this);
this.container.append( objectView.el );
objectView.render();
this.objectViews.push(objectView);
}, this );
},
});
ObjectView.js
var ObjectView = Backbone.View.extend({
});
I would put these views in the same module.
The purpose of a module - whether you're using RequireJS or just plain old JavaScript modules - is to encapsulate a set of related objects and functions, for a specific purpose. In this case, your purpose is the Finder View.
By keeping all of the related objects in the same file, you'll have more freedom and flexibility for how you make the objects work together.
As a side note, but related to what you're doing, you might be able to get some ideas for how to make this work from the "CompositeView" of my Backbone.Marionette plugin. I've built a hierarchical tree-view of folders and files with it before, and the column view of Finder would be fairly easy to build with it, too.
Note that I'm not suggesting you need to use my plugin. Rather, I think it might be helpful in figuring out how you want to structure your code.
I've got a blog post that talks about it here: http://lostechies.com/derickbailey/2012/04/05/composite-views-tree-structures-tables-and-more/
You can find the code and docs here: https://github.com/derickbailey/backbone.marionette
And the annotated source code for the composite view is here: http://derickbailey.github.com/backbone.marionette/docs/backbone.marionette.html#section-26

Categories

Resources