I'm trying to find best option to make Backbone views reusable. I goggled and found many different solutions but not sure which one suits my requirements. Basically I'm going to have many widgets filled with real time data and I need a base component that will handle service subscriptions
Is following best solution for this problem:
App.View.Base = Backbone.View.extend({
baseMethod: function( params ) {};
});
App.ExtendedView.Base = App.View.Base.extend({
// new stuff here
// overriding App.View.Base.baseMethod
baseMethod: function( params ) {
// overriding stuff here
App.View.Base.prototype.baseMethod.call(this, params); // calling super.baseMethod()
}
});
Is there any better approach? or should I use mixins?
I might be inclined to favour composition over inheritance here, and create a spinner view, and use instances of it in other views that require spinner functionality.
More info: Prefer composition over inheritance?
The typical rule-of-thumb I use for stuff like this is if there are any immutable methods in the base class that provide a common context for all your sub-classes, then inheritance makes sense. For instance, I've created a BaseView class for my Backbone application that looks something like this:
define(function() {
return Backbone.View.extend({
/**
* show() and hide() are immutable
*/
show : function() {
this.beforeShow();
this.doShow();
this.afterShow();
},
hide : function() {
this.beforeHide();
this.doHide();
this.afterHide();
},
doShow : function() {
this.$el.show();
this.trigger('displayComplete', {action : 'show'});
},
doHide : function() {
this.$el.hide();
},
//Override the following to extend behavior of the view
//before and/or after the view is shown/hidden.
beforeShow : function() {},
beforeHide : function() {},
afterShow : function() {},
afterHide : function() {}
});
});
This is a pretty simple example, but it has proven to make things much easier for development of my application, as my central controller object is given a common interface for showing and hiding views. I suppose you could use composition here as well, but that requires doing an explicit extend() at runtime. You get the same result in either case, but I just prefer to have the functionality available when I instantiate my views.
Another thought is that it really depends upon what you want to accomplish. Inheritance is much more rigid than composition, but again, it depends upon what you ultimately want to accomplish, and sometimes enforcing rigidity to maintain a context is a good thing.
Related
I'd like to set a variable or object in main.js which I can then reference from any backbone view.
I considered using localStorage, but the data is dynamic and a little sensitive so I wouldn't like to have it stored in localStorage as it could be manipulated by the user very easily.
Since you said "main.js" I think you're confused between RequireJS and Backbone.js. RequireJS is not part of Backbone. It is an AMD module loader which happens to be used a lot with backbone projects.
Looks like you need a RequireJS module like:
define(function (require) {
var someData;
var singleton = function () {
return {
getMyData = function(){},
setMyData = function(data){},
};
};
return singleton();
});
P.S: Above code can be made object literal, an instance of proper constructor function, es6 class of whatever. I just posted something as an example.
#TJ already gave what's needed to achieve what I call a Service within my app, borrowed from AngularJS services. I have a couple services, like i18n which is just a i18next instance.
Like you, I wanted to manage certain data relative to the app that could be shared everywhere, without putting it in the window object.
I came up with a AppState service which is just a Backbone model instance.
define(['underscore', 'backbone', 'color'], function(_, Backbone, Color) {
var State = Backbone.Model.extend({
setBrandColor: function(hexColor, options) {
return this.set({ color: new Color(hexColor) }, options);
},
getBrandColor: function() {
return this.get('color');
},
});
return new State(); // return a new instance instead of the constructor.
});
What's cool with a Backbone model is that anything within the app can listen to its events. This is inspired from React.
this.listenTo(AppState, 'change:color', this.onBrandColorChange);
Note that I prefer to use PascalCase for services even though they're instances, they're closely related to a static type in other languages.
This way, when the app state changes, other parts of the app may or may not react accordingly. Without the events, the app would need to be more coupled which is undesirable.
I would like to use mixins with my BackBone Views.
Here is my mixin:
var mixin = {
events: {
"click" : "doStuff"
},
doStuff: function() { alert("bah!"); }
}
Here is how I mix it into two separate views:
var view1 = Backbone.View.Extend({ ... });
_.extend(view1.prototype, mixin);
var view2 = Backbone.View.Extend({ ... });
_.extend(view2.prototype, mixin);
The trouble I am running into is that the click event only seems to work in view1. If I initialize view2 first, then the click event only works in view2.
Any ideas what I am doing wrong?
Thanks (in advance) for your help.
You can override the extend method on backbone to due the sort of inheritance and merging you expect. You just need to dig through the documentation a little bit and the objects to find what you want.
This way you have a BaseObject then extend off of that your two objects.
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.
If a viewmodel is already defined, either manually or automatically with mapping plugin, is there any problem to "extend" (add properties/functions) the viewmodel later in the code by using prototype?
I'm aware of the create callback of mapping plugin, I just want to know if there are any implications of using prototype instead? The reason I'm asking is because I'm generating large parts of the viewmodels from server side code, but sometimes need to extend the viewmodel later than at initial generation.
I don't think there is any problem with this, I work with a large deep view model graph instantiated via the mapping plugin from correspondingly structured JSON and I use prototypes to define an "AbstractViewModel" with useful properties and toJSON "overrides" among other things.
There is no problem with this. Just make sure that the view responds appropriately when there's no data in that particular field in the viewModel.
There seems to be a couple ways of going about this.
For one, you can take a single object view model and utils.extend them via prototype:
ko.utils.extend(ViewModelClass.prototype, {
newPrototype1: function () { ... },
newPrototype2: function () { ... }
}
Or, you can add an extender to knockout and invoke it via the observable object itself:
(http://knockoutjs.com/documentation/extenders.html)
ko.extenders.numeric = function(target, precision) {
...details in link above...
}
...
self.NumericProperty = ko.observable(data).extend({ numeric: 0 });
...
Or create a function that is available to all instances of an observable, observableArray, computed...
(http://knockoutjs.com/documentations/fn.html)
ko.observable.fn.filterByProperty = function(propName, matchValue) {
return ko.computed(function() {
...details in link above...
}, this);
}
I tend to use combinations of these. I like extending prototypes of View Models in a separate file from the VMs and Mappings, so I have a structure like
ViewModel(s).js
ViewModel(s).Mappings.js
ViewModel(s).Prototypes.js
As you'll see, the timespan between these 'answers' is rather large, so some things have changed yet some things remain the same.
I am using Base2 as a means to allow us to easily do inheritance in our system, aswell as using KnockoutJS for some UI interactions.
We have defined a base class for our ViewModels
BaseViewModel = Base.extend({
...
});
Which we then extend for our view models:
ExampleViewModel = BaseViewModel.extend({
text: ko.observable("")
});
However there seems to be a problem. When you create 2+ instances of the view model (say if you are pushing them in to an observableArray and using templates to build up a UI) it seems like any changes made to a bound field, updates all view models rather than just the one it's bound to.
Does anybody know why this might be?
Because the extension is not actually instantiating a new observable, its just copying the reference.
I think you can do something like this:
ExampleViewModel = BaseViewModel.extend({
constructor: function() {
this.text = ko.observable("");
}
});
Not as nice though as normal Base2 syntax, but just a limitation in how Knockout is implemented due to issues with properties.