Backbone.Advice
/**
* MIXIN
* adds on a selected paramenter for a view as well as methods to deal with
* selected state and changes to the state.
*/
Mixin.view.makeSelectable = function() {
this.setDefaults({
selectable: true,
selected: false,
select: function(sel) {
if (!this.isSelectable())
return;
var temp = this.selected;
this.selected = sel === undefined || !!sel;
if (temp != this.selected) {
if (this.selected)
this.onSelect();
else
this.onDeselect();
}
},
deselect: function() {
this.select(false);
},
toggleSelect: function() {
this.select(!this.selected);
},
isSelected: function() {
return this.selected && this.isSelectable();
},
isSelectable: function() {
return this.selectable;
},
onSelect: function() {},
onDeselect: function() {}
});
};
/**
* MIXIN
* toggle selection state on click.
*/
Mixin.view.clickSelect = function() {
this.mixin(Mixin.view.makeSelectable);
this.addToObj({
events: {
'click': 'onClick'
}
});
this.after('onClick', function(event) {
if (!document.getSelection().isCollapsed)
return;
if (this.selectableElement(event.target))
this.toggleSelect();
});
this.around('selectableElement', function(fn, el) {
return fn(el) ||
el == this.el ||
this.$el.contains(el);
});
};
Now all I have to do is add the click to select to a view to make it selectable by clicking:
var ClickableView = Backbone.View.extend().mixin(Mixin.view.clickSelect);
The great thing about these mixins is that we don’t have to recode our old code base to add them in, I can just mix them in on existing code! Want to see something even more excellent? well there are mixins to make a list navigatable by keyboard. So I can just do something like this:
var NavigatableList = Backbone.View.extend().mixin(Mixin.view.keyboardNavigatableList);
And that’s it! I now have a list that I can navigate with the keyboard and it’s going to select the appropriate child. The keyboardNavigatableList mixin actually uses other mixins that you could use on their own which include getChildren (allows you to return the child views of a view – its made to work with layoutManager and componentView but can easily be swapped out for whatever view manager you use),selectableChildren (keep a record of the selected children), singleSelectChild (only allow one child to be selected at a time) and keyboardNavigation (which actually extends focusable and gives you functions like onKeydown and onKeyup that you can mixin with instead of creating new events and potentially overwriting functionality as you extend your view).
So this was all pretty sweet but my biggest annoyance was that there was no way to just have a list, so I made a mixin for that as well. It was made to work with layoutManager but I was soon finding problems with layoutManager so I made another plugin…
Backbone.ComponentView
var List = Backbone.ComponentView.extend({
itemView: SelectableItem
}).mixin([
Mixin.view.keyboardNavigatableList,
Mixin.ComponentView.autolist
]);
Backbone.ModelRegistry
To make this work though I would need to override the Backbone.Collection.prototype._prepareModels to first check for the existence of a model and pass that back instead. So to do this I just wrote a mixin that would register a collection then augment it’s _prepareModel method to check the registry for an existing model. So it became as simple as this:
var registery = new Backbone.ModelRegistry();
var Coll = Backbone.Collection.extend().mixin([
Mixin.collection.modelStore
], {
register: registry
});
Then I can extend coll, or mixin more things but any new instances will be registered with the registry and share model instances. I could even put that mixin on different constructors and as long as I pass the same register object through they will all share model instances. In fact we have two panes in our product that have lists that need to share their models but we don’t want to share the models between panes, so we just create two model registers and use one for the top pane and the other for the bottom. Now we have the same models between them, but we were told that now we want to hide the models in the second collection if they are endorsed. So basically we want to update our collection to remove models that are endorsed – the problem is that there may have been a mistake and we need to put them back in. What we really needed was a filtered collection to make things easy for us.
Backbone.CollectionFilter
The main issue was that I don’t want to know that the collection I’m getting is a filtered version. I want to still be able to interact with it as if it’s the original collection with all it’s original function calls. So what we have to do is create a new collection that extends our old one and then bind all of it’s functions to the original context. Then we need to do have a look at the events getting passed through and choose whether they should fire at all (a model being added to the original but not passing a filter should not fire any event). And so Backbone.CollectionFilter came in to being. To use it we just need to pass in the original collection which will be used as a basis for inheritance, a filter function and an optional comparator that will allow you to sort your collection in a different way if necessary. This meant that to get the collection we just need to do this:
var NonEndorsed = new Backbone.CollectionFilter(endorsed, function(mod) {
return !mod.get('endorsed');
});
And that’s it, we can just pass along that collection and use it as normal. And that’s it, we’ve got a great scalable system that we can mix and match to get what we need.
The Future
Nerve
Underscore.Extras
HandlebarRivets
a quick note on CI
Powered by WPeMatico