How do i overwrite the default Backbone Model? - javascript

I want to overwrite the default Backbone Model variable (Backbone.Model) to use my own custom validation methods (isValid, validate) and to add some properties.
_.extend(Backbone.Model, {
isValid: function() { // custom function },
validate: function() { // custom logic }
});
var myModel = Backbone.Model.extend({
// this adds for example properties to my modified Backbone model.
});
Unfortunatly this doesn't work... when I load the "wrapper, extending"-module with requirejs and create a new Model instance and than call validate. It says that it doesn't know any validate function...

you have to extend Backbone.Model.prototype rather then Backbone.Model itself as all the methods are prototype methods of the constructor function rather then properties on the model. Though it might be better idea to create a custom BaseModel that will extend the Backbone.Model and implement your custom logic so that if backbone gets updated etc. you will avoid possible conflicts even if in this case they are rather unlikely it's still considered a better practice to extend base Backbone classes rather then to modify them.

I prefer to do custom Models for the project instead of "overwrite" that is not OOP. (at the end of the way could be the same but if we try to emulate OOP I feel that is more easy to understand).
Check my example, I define YourAbstractModel that it is the kind of extension of the Backbone.Model, then all of my Models extend that abstract Model instead of the Backbone Model.
var YourProject = {};
YourProject.YourAbstractModel = Backbone.Model.extend({
customProperty1 : null,
customProperty2 : null,
isValid : function(){ },
specificUtility : function(){ }
});
var YourModel1 = YourProject.YourAbstractModel.extend({
customProperty1 : 'aaaaa'
});
var YourModel2 = YourProject.YourAbstractModel.extend({
customProperty1 : 'bbbbb'
});
console.log( new YourModel1().customProperty1 );
console.log( new YourModel2().customProperty1 );
My way is also flexible to have more than one abstract Model, does not conflict with updates of Backbone and I feel is more close to inheritable process.

You can use the Backbone.Validation plugin by thedersen
https://github.com/thedersen/backbone.validation
Its pretty helpful and easy to use. You can also bind your view to the model, so that your view has your custom-error attribute for the specific model property which failed validation.

Related

How to set attribute of an instance of a model that is the sibling of a parent object?

Newish to Backbone, and having some trouble. Going to try to ask this in a generic, no-code way since the application I'm tasked with maintaining is several thousand lines long... hope I can be clear.
I have a method myMethod(), that belongs to a model App.Person.
I have a collection App.PersonList that holds several instances of App.Person.
I have an instance (myPersonList) App.PersonList that I'm creating within an object (myDonationForm) that is an instance of an object App.DonationForm (and here we roam even further outside my comfort zone: App.DonationForm extends an object named Controller which extends an object called Base which seems to be a base.js thing and I have very little idea what's happening here but I hope it doesn't matter for my immediate need).
Also in App.DonationForm, I have an instance (myErrorMsg) of a model App.Errors. I would like to be able to set an attribute of myErrors from myMethod() but can't work out the syntax to refer to myErrors, traversing up the tree of nested objects and then back down a parallel step.
I hope that made sense. To visualize it:
myDonationForm, inst of App.DonationForm, ext Controller
|--myPersonList, inst of App.PersonList, ext Collection
| |--myPerson[1], inst of App.Person, ext Model // I want to change from here
| | +---myMethod()
| |--myPerson[2], inst of App.Person, ext Model // or from here
| +---myMethod()
+myErrorMsg, inst of App.Errors, ext Model // an attribute of this.
Thank you in advance for any pointers you can offer.
Edited to add a code snippet (and I accidentally tried to edit Hoyen's answer, not my own question! didn't realize it til I got the peer review screen, ugh)
App.SpecialDonationForm = App.DonationForm.extend({
[...]
initialize: function(options){
App.DonationForm.prototype.initialize.call(this, options);
[...]
},
start: function(){
App.DonationForm.prototype.start.call(this);
[...]
this.myPersonList = new App.PersonList(this.initialData);
this.myErrorMsg = new App.Errors();
[...]
Basically since myErrorMsg is an instance of a Backbone model you need to use Backbone's model methods to set the attributes. So, it should look something like this:
App.Person = Backbone.Model.extend(
{
[...]
defaults: {
[...],
errorMsg: new App.Errors()
},
[...]
myMethod: function(){
var value = ""; // set it to what ever you like
this.get("errorMsg").set("message",value); // "message", is the attribute you want to update with the value
this.trigger('change:errorMsg'); // not sure if this is needed. but this will insure that this will trigger any event listeners on errorMsg
}
});
App.SpecialDonationForm = App.DonationForm.extend({
[...]
initialize: function(options){
App.DonationForm.prototype.initialize.call(this, options);
[...]
},
start: function(){
App.DonationForm.prototype.start.call(this);
[...]
this.myPersonList = new App.PersonList(this.initialData);
this.myErrorMsg = new App.Errors();
this.myPersonList.on('change:errorMsg',showError.bind(this));
function showError(model,val,options){
this.myErrorMsg.set(_.extend(this.myErrorMsg.defaults,model.toJSON());
}
[...]
An event aggregator may work in this scenario.
Setup to extend Backbone events
App.eventAgg = _.extend({object}, Backbone.Events);
During App.Errors initialize, listenTo an event
this.listenTo(App.eventAgg, 'someEvent', this.doSomeUpdate);
In App.Person's myMethod, trigger the event
App.eventAgg.trigger('someEvent');
To answer your generic question even more generically,
Based on your hierarchy, a Person object doesn't know anything at all about the Errors object. Child objects don't (and shouldn't) know anything about their parents.
A Person object does (presumably) know, however, that an "error" has occurred and someone might want to know about it.
Since the Person object doesn't know any more than that, all it can do is trigger a custom event.
Some other object needs to listen for this custom event. Based on your hierarchy, the only other object that has access to the Person object is the PersonList collection.
The PersonList collection is now in the same situation. It has learned (from the custom event) that an error has occurred, but it can't do anything about it and it doesn't know about its parents. So all it can do is trigger another custom event.
The DonationForm object has to listen for the custom events from the PersonList collection. When it receives the event, it can then pass it to the Errors object.
Voila.

Multiple inheritance in Javascript with prototype chain

I have a JavaScript MVC design, implemented using prototypes, where different items may be displayed in different ways as decided by the controller. For example an 'Event' item may be displayed with the views ViewTabs or ViewSimple. Class hierarchy:
ViewBase
- ViewTabs
-- EventViewTabs, which implements EventViewTabs.Validate
- ViewSimple
-- EventViewSimple, which implements EventViewSimple.Validate
Deciding whether to use EventViewTabs or EventViewSimple is done by a EventController. My problem is: I have a Validate method for checking inputs from the Event views, but this method is identical for the EventViewTabs and the EventViewSimple views. Where should I put Validate in order to avoid duplication? I cannot put it in ViewBase, as other items (e.g. User) also inherit from this class.
Seems I need multiple inheritance for this, but is there a smarter way to do it? I have a feeling I'm overlooking something obvious.
You're missing composition. Inheritance isn't the answer to all issues about code reuse to avoid copy-paste programming.
Let's say you've a View base prototype:
function View() { }
If you want this view to support validation, you can inject the validation dependency in the constructor function:
function View(validator) {
this.validator = validator;
}
View.prototype = {}; // A lot of functions here
That is, now any view which inherits View's prototype will have an associated validator. In other words: you don't need to derive two prototypes in your concrete view (you don't need and you can't do it anyway).
In the other hand, in terms of object-oriented programming, it wouldn't make sense to derive from Validator to create a View.
When you say a view has a validator, since you're using has as verb, you're talking about an association (a form of composition). Alternatively, when you say my main screen is as view, we're talking about an inheritance, because a specific view must be also a view, so it needs base view's members to act like a view.
Basically your validator could be tailor-made with the type it has to work with. In UML, it's called composition. I figure out your code as follows:
function Validator {}
Validator.prototype.validate = function(arg) {
//arg is no longer inputs
return true|false; //the ultimate output along with additional information;
}
function EventViewTabsValidator() {}
EventViewTabsValidator.prototype = Object.extend(Validator.prototype); //inheritance
EventViewTabsValidator.prototype.constructor = EventViewTabsValidator; //enforce the constructor to point to your derived type
EventViewTabsValidator.prototype.validate = function() {
var inputs = $('inputs');
var param = 'do some stuff specific to EventViewTabsValidator based on the inputs';
return Validator.prototype.validate.call(this, param); //pass param, not inputs
}
function EventViewSimpleValidator() {}
EventViewSimpleValidator.prototype = Object.extend(Validator.prototype); //inheritance
EventViewSimpleValidator.prototype.constructor = EventViewSimpleValdiator; //enforce the constructor to point to your derived type
EventViewSimpleValidator.prototype.validate = function() {
var inputs = $('inputs');
var param = 'do some stuff specific to EventViewSimpleValidator based on the inputs';
return Validator.prototype.validate.call(this, param); //pass param, not inputs
}
function EventViewTabs() {
this.validator = null; //see init
}
EventViewTabs.prototype.init = function() {
this.validator = new EventViewTabsValidator();
}
function EventViewSimple() {
this.validator = null; //see init
}
EventViewSimple = function() {
this.validator = new EventViewSimpleValidator();
}
Your could abstract up both types to a base EventView, which could expose this.validator.
Your instance of EventController will call:
var simple = new EventViewSimple();
simple.validator.validate();
var tabs = new EventViewTabs();
tabs.validator.validate();
Whatever the EventView instance, they implement their own specific validator that can be called in a generic way.
One approach is to use mixins to add the other behavior (this is the ruby approach, and is also used by react.js and react.rb) You can google for javascript+mixins and find some excellent tutorials like this one: http://raganwald.com/2014/04/10/mixins-forwarding-delegation.html
For your specific case validate (or perhaps validator) would be the mixin.
Why not to do something like this:
ViewBase
ViewBase
-EventValidator, which implements Validate
--ViewTabs
---EventViewTabs
--ViewSimple
---EventViewSimple.
Also consider to use composition over inheritance see this video

defineProperty on Backbone model

If i want to add a custom property to my backbone model, is this the best was to do this? Is there a better way or a completely different approach to the functionality i want to achieve?
var myModel = Backbone.Model.extend({
defaults:{
monthly_amount: 100
},
initialize: function(model, options){
var m = this;
Object.defineProperty(this,"yearly_amount",{
get: function() {
return (m.get("monthly_amount") * 12);
},
set: function(value) {
m.set("monthly_amount", (value/12) );
}
});
}
});
Thanks!
Edit: The property is just "virtual", i do not want it to be within the model attributes when saving the model to the server.
So the general problem here is often referred to as "computed properties" and there are plugins for backbone that provide this (see below). Backbone uses get/set method calling style as opposed to defineProperty style so your approach would make the value computation not transparent to views and thus be a pretty strong departure from backbone's design. A plugin that maintains the proper get/set and change interfaces is going to maintain the basic Model API so the view doesn't have to treat this particular model attribute differently.
See also the backbone wiki of plugins.
Available plugins:
Backbone.ModelMorph
Backbone.ComputedModel
Backbone.Spark
Backbone.ComputedFields
When you set your property to defaults oject, it goes to attributes, but it doesn't suit us.
Since Backbone model is an regular object, you can access to it's properties as to objects properties:
var myModel = Backbone.Model.extend({}),
model = new myModel();
model.monthly_amount = 50;
console.log(model.monthly_amount)
or create setter and getter like this:
var myModel = Backbone.Model.extend({
setMonthlyAmount: function (value) {
this.monthly_amount = value;
},
getMonthlyAmount: function () {
return this.monthly_amount;
}
});
//access with getter/setter
var model = new myModel();
model.setMonthlyAmount(20);
var result = model.getMonthlyAmount();
console.log(result);
Working example on jsfiddle

In Backbone, how do I have an after_render() on all views?

I am maintaining a javascript application and I would like there to be a jquery function invoked on pretty much every view. It would go something like this:
SomeView = Backbone.Marionette.ItemView.extend
initialize: ->
#on( 'render', #after_render )
after_render: ->
this.$el.fadeOut().fadeIn()
Clearly there is a better way to do this than have an after_render() in each view? What is the better way to do it? If you can give an answer that includes jasmine tests, I'll <3 you ;)
The event you are looking for is onDomRefresh. See here for the documentation:
https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.view.md#view-domrefresh--ondomrefresh-event
Create your own base view class and put your afterRender code in it. When you create a view, inherit from this class.
var MyApp.ItemView = Backbone.Marionette.ItemView.extend({
afterRender: function() {
// This will be called after rendering every inheriting view.
}
});
var SpecificItemView = MyApp.ItemView.extend({
// this view will automatically inherit the afterRender code.
});
In general, it seems to be considered good practice to define your own base views for all 3 view types. It will enable you to easily add global functionality later.
There is a common pattern used across all Backbone frameworks, normally they have a render method which in turn calls beforeRender, renderTemplate and afterRender methods.
render:function(){
this.beforeRender();
this.renderTemplate();// method names are just indicative
this.afterRender();
return this;
}
In your Base view you can have these methods to be empty functions, and implement them wherever you want it. Not sure this answer applies to Marionette
Combining thibaut's and Robert Levy's answer, the correct solution would be:
var baseView = Backbone.Marionette.ItemView.extend({
onDomRefresh: function() {
// This will be triggered after the view has been rendered, has been shown in the DOM via a Marionette.Region, and has been re-rendered
// if you want to manipulate the dom element of the view, access it via this.$el or this.$('#some-child-selector')
}
});
var SpecificItemView = baseView.extend({
// this view will automatically inherit the onDomRefresh code.
});

Using inheritance in meteor.js

I've been hoping to use inheritance in Meteor, but I couldn't find anything about it in the documentation or on Stack Overflow.
Is it possible to have templates inheriting properties and methods from another abstract template, or class?
I think the short answer is no, but here's a longer answer:
One thing I've done to share functionality among templates is to define an object of helpers, and then assign it to multiple templates, like so:
var helpers = {
displayName: function() {
return Meteor.user().profile.name;
},
};
Template.header.helpers(helpers);
Template.content.helpers(helpers);
var events = {
'click #me': function(event, template) {
// handle event
},
'click #you': function(event, template) {
// handle event
},
};
Template.header.events(events);
Template.content.events(events);
It's not inheritance, exactly, but it does enable you to share functionality between templates.
If you want all templates to have access to a helper, you can define a global helper like so (see https://github.com/meteor/meteor/wiki/Handlebars):
Handlebars.registerHelper('displayName',function(){return Meteor.user().profile.name;});
I've answered this question here. While the solution doesn't use inheritance, it allow you to share events and helpers across templates with ease.
In a nutshell, I define an extendTemplate function which takes in a template and an object with helpers and events as arguments:
extendTemplate = (template, mixin) ->
helpers = ({name, method} for name, method of mixin when name isnt "events")
template[obj.name] = obj.method for obj in helpers
if mixin.events?
template.events?.call(template, mixin.events)
template
For more details and an example see my other answer.
Recently, I needed the same functionality in my app so I've decided to create my own package that will do that job out of the box. Although it's still work in progress, you can give it a go.
Basically, the entire method is as follows:
// Defines new method /extend
Template.prototype.copyAs = function (newTemplateName) {
var self = this;
// Creating new mirror template
// Copying old template render method to keep its template
var newTemplate = Template.__define__(newTemplateName, self.__render);
newTemplate.__initView = self.__initView;
// Copying helpers
for (var h in self) {
if (self.hasOwnProperty(h) && (h.slice(0, 2) !== "__")) {
newTemplate[h] = self[h];
}
}
// Copying events
newTemplate.__eventMaps = self.__eventMaps;
// Assignment
Template[newTemplateName] = newTemplate;
};
In your new template (new_template.js) in which you want to extend your abstract one, write following:
// this copies your abstract template to your new one
Template.<your_abstract_template_name>.copyAs('<your_new_template_name>');
Now, you can simply either overwrite your helpers or events (in my case it's photos helper), by doing following:
Template.<your_new_template_name>.photos = function () {
return [];
};
Your will refer to overwritten helper methods and to abstract ones that are not overwritten.
Note that HTML file for new template is not necessary as we refer to abstract one all the time.
Source code is available on Github here!

Categories