About using functions in view in Backbone Marionette - backbone.js

What I want to do is this:
events : {
"click" : "doSomething"
},
doSomething(event) : function() {
this.doSomeFurtherAction1();
this.doSomeFurtherAction2();
},
doSomeFutherAction1: function() {
//some actions
},
doSomeFutherAction2: function() {
//some actions
}
However; my secondary functions are not attached to the view as I was expecting. Is there a way of doing this other than putting the code in the app or controller?
Thanks
--Justin Wyllie

This code:
doSomething(event) : function() {
this.doSomeFurtherAction1();
this.doSomeFurtherAction2();
},
Should Be:
doSomething: function(event) {
this.doSomeFurtherAction1();
this.doSomeFurtherAction2();
},
This should solve your problem.
You might want to bind all your function to the view for other use cases:
initialize: function(){
_.bindAll(this)
}

Related

Fullcalendar Custom Views via JS classes destroy throws errors

I have implemented Fullcalendar Custom View by referring document Custom Views via JS classes. It seems to worki fine, but the destroy method throws an error: View.prototype.destroy.apply is not function.
I have used the below code:
var FC = $.fullCalendar;
var View = FC.View;
var BlankView;
BlankView = View.extend({
initialize: function() {
// View.prototype.initialize.apply(this, arguments);
},
setHeight: function(height, isAuto) {
},
render: function () {
this.title = this.title || 'TestView';
if (this.el[0].children.length === 0) this.el.addClass('fc-basic-view').html(this.renderHtml());
},
destroyEvents: function() {
// responsible for undoing everything in renderEvents
},
destroy: function() {
View.prototype.destroy.apply(this, arguments);
},
renderHtml: function () {
this.el.html('<div></div>');
},
});
FC.views.blank = BlankView;
Any clues to achieve the destroy method in custom views?

why the drawSomething doesn't work

I use the backbone to test something, but i don't know why the drawSomething just no show ##"
initialize: function() {
setInterval(function() {
//alert("Hello");
this.drawSomething();
}, 1000);
},
drawSomething: function() {
alert('hi');
},
The problem is that inside setInterval callback context this is not what you expect (it's global object window). Simplest fix is to save proper object reference in variable:
var self = this;
setInterval(function() {
//alert("Hello");
self.drawSomething();
}, 1000);
as you are using backbone, so possible you are using underscore too. Bind should help:
initialize: function () {
var foo = function () { this.drawSomething(); };
foo = _.bind(foo, this);
setInterval(foo, 1000);
}
or jQuery analog Proxy:
foo = $.proxy(foo, this);
as quick solution
initialize: function() {
setInterval(function() {
//alert("Hello");
this.drawSomething();
}.bind(this), 1000);
},
drawSomething: function() {
alert('hi');
},
but I would prefer to use additional variable as dfsq methioned, because some old browsers doesn't support bind

Backbone.js model.save() fire a "too much recursion" error in underscore

I've got a problem trying to use backbone on saving my Model from a form. Here I want my my view to actually be an editing form:
(function() {
'use strict';
var YachtEditor = {};
window.YachtEditor = YachtEditor;
var template = function(name) {
return Mustache.compile($('#' + name + 'Template').html());
};
YachtEditor.Tank = Backbone.Model.extend({
defaults : {
dCapacity : "",
sType : ""
}
});
YachtEditor.Tanks = Backbone.Collection.extend({
// url: "/rest/tanks",
localStorage: new Store("tanks"),
model : YachtEditor.Tank
});
YachtEditor.TankView = Backbone.View.extend({
template: template("tank"),
events: {
'click .save' : 'save',
'click .remove' : 'remove'
},
initialize: function() {
console.log("initialize tank View :");
console.log(this.model.get("id"));
},
render: function() {
this.$el.html(this.template(this));
return this;
},
save: function() {
console.log('change');
var self = this;
var values = {
sType: self.$("#sType").val(),
dCapacity: self.$("#dCapacity").val()
};
console.log("dCapacity : " + values.dCapacity);
console.log("sType : " + values.sType);
this.model.save(values);
},
remove: function() {
this.model.destroy();
},
dCapacity : function() {
return this.model.get("dCapacity");
},
sType : function() {
return this.model.get("sType");
}
});
YachtEditor.TanksView = Backbone.View.extend({
el: $("div.tankZone"),
template: template("tanks"),
events: {
"click .add" : "addTank",
"click .clear" : "clear"
},
initialize: function() {
this.tanks = new YachtEditor.Tanks();
// this.tanks.on('all', this.render, this);
this.tanks.fetch();
this.render();
},
render: function() {
this.$el.html(this.template(this));
this.tanks.each(this.renderTank, this);
return this;
},
renderTank: function(tank) {
var view = new YachtEditor.TankView({model: tank});
$(".tanks").append(view.render().el);
return this;
},
addTank: function() {
this.tanks.create({});
this.render();
},
clear: function() {
this.tanks.each(function(tank) {
tank.destroy();
});
this.render();
}
});
...
})();
Here is the mustache template i use for each tank
<script id="tankTemplate" type="text/x-mustache-template">
<div class="tankView">
<h1>Tank</h1>
<select id="sType" value="{{ sType }}">
#for(option <- Tank.Type.values().toList) {
<option>#option.toString</option>
}
</select>
<input id="dCapacity" type="text" value="{{ dCapacity }}">
<button class="destroy">x</button>
</div>
</script>
My problem here is that this.model.save() triggers a 'too much recursion' in underscore. js. (chrome is displaying an error also.
Here is the call stack on error:
_.extend
_.clone
_.extend.toJSON
_.extend.save
_.extend.update
Backbone.sync
_.extend.sync
_.extend.save
YachtEditor.TankView.Backbone.View.extend.save
st.event.dispatch
y.handle
I suspect the save to recall the blur event but i cannot find a way to explicit it... Maybe I'm not using backbone as i should?
My problem, aside of some pointed out by Yurui Ray Zhang (thank you), was that I was using a backbone-localstorage.js from an example I found here : git://github.com/ngauthier/intro-to-backbone-js.git
The "too much recursion error" stopped to appear as soon a I replaced it with a storage I found here : https://github.com/jeromegn/Backbone.localStorage
a few things. you defined your tank model as
app.Tank = ...
but in your collection you are referencing it as:
model : YachtEditor.Tank
and in your view, you are trying to assign elements before they are rendered on the page:
this.input = {}
this.input.sType = this.$("#sType");
this.input.dCapacity = this.$("#dCapacity");
I'm not sure how your view is rendered to the page, some people, like me, like to use render() to render the template directly to the page:
render: function() {
this.$el.html(this.template(this));
//done, you should be able to see the form on the page now.
},
some others, will use something else to insert the el, eg:
//in another view
tankView.render().$el.appendTo('body');
but either way, if you want to cache your elements, you need to do it after they are rendered to the page, not in initialize.
//this method is only called after render() is called!
cacheElements: function() {
this.input = {}
this.input.sType = this.$("#sType");
this.input.dCapacity = this.$("#dCapacity");
}
I'd suggest, first, try to fix this things, and then, try to add some console log or debuggers in your readForm method to see if the values are grabbed correctly:
readForm: function() {
var input = this.input;
console.log(input.sType.val());
console.log(input.dCapacity.val());
this.model.save({
sType: input.sType.val(),
dCapacity: input.dCapacity.val()
});
},

Backbone defaults view events

Is it possible to make a set of default events which exist in every view? For example if every view in my application includes a settings button
events: {
"click #settings" : "goSettings"
},
...
goSettings: function() {
// settings show function
});
How can I can package this event to be included in every view in my application?
The problem is that View#extend simply overwrites existing properties so you can't put your 'click #settings' in a base class and subclass that. However, you can easily replace extend with something of your own that merges events. Something like this:
var B = Backbone.View.extend({
events: {
'click #settings': 'goSettings'
}
}, {
extend: function(properties, classProperties) {
properties.events = _({}).extend(
properties.events || { },
this.prototype.events
);
return Backbone.View.extend.call(this, properties, classProperties);
}
});
And then extend B instead of Backbone.View for your views.
Demo: http://jsfiddle.net/ambiguous/Kgh3V/
You can create a base view with the event(s) and functions, then make your other views inherit from it. I like the pattern described here, because it's simple to set up and easy to override as needed: http://www.scottlogic.com/blog/2012/12/14/view-inheritance-in-backbone.html
A base view looks like this:
var BaseSearchView = function(options) {
this.inheritedEvents = [];
Backbone.View.call(this, options);
}
_.extend(BaseView.prototype, Backbone.View.prototype, {
baseEvents: {},
initialize: function(options) {
// generic initialization here
this.addEvents({
"click #settings" : "goSettings"
});
this.initializeInternal(options);
},
render: function() {
// generic render here
this.renderInternal();
return this;
},
events: function() {
var e = _.extend({}, this.baseEvents);
_.each(this.inheritedEvents, function(events) {
e = _.extend(e, events);
});
return e;
},
addEvents: function(eventObj) {
this.inheritedEvents.push(eventObj);
},
goSettings: function() {
// settings show function
}
});
BaseView.extend = Backbone.View.extend;
And your child classes like this:
var MyView = BaseView.extend({
initializeInternal: function(options) {
// do something
// add event just for this child
this.addEvents({
"click #differentSettings" : "goSettings"
});
},
renderInternal: function() {
// do something
}
});

access function in one view from another in backbone.js

I have this structure of views:
window.templateLoaderView = Backbone.View.extend({});
window.PopupView = templateLoaderView.extend({
initialize: function () {
this.PopupModel = new PopupModel();
this.event_aggregator.bind("tasks_popup:show", this.loadTaskPopup);
},
render: function() {
template= _.template($('#'+this.PopupModel.templateName).html());
$(this.el).html(template(this.PopupModel.toJSON()));
$('#'+this.PopupModel.containerID).html(this.el);
},
loadTaskPopup: function() {
this.PopupModel.loadTemplate("popupTask_templateHolder", "/js/templates/popup_task.html", "1", "container_dialog");
this.render();
}
});
window.TaskbarView = templateLoaderView.extend({
initialize: function () {
this.TaskbarModel = new TaskbarModel();
this.PopupModel = new PopupModel();
},
loadTaskbarPopup: function() {
this.event_aggregator.trigger("tasks_popup:show");
}
});
So I would like to runf function in one view form another. As far as I understand, I need to bind them somehow. Is it possible to bind them in initialize function?
I saw here example: Backbone.js - Binding from one view to another? . They creating both objects and than somehow binding them.
Thanks in advance,
I am kind of a fan of using the "Event Aggregator" pattern. I make sure that every view is given a copy of the same event aggregator object and they can all talk to each other through it... kind of like a CB radio :)
Do this before you create any views:
Backbone.View.prototype.event_aggregator = _.extend({}, Backbone.Events);
Now, you can publish/subscribe from anywhere:
window.PopupView = Backbone.View.extend({
initialize: function() {
_.bindAll(this, "loadTaskPopup");
this.model = new PopupModel();
this.event_aggregator.bind("tasks_popup:show", this.loadTaskPopup);
},
loadTaskPopup: function() {
// do something with this.model
}
});
window.TaskbarView = Backbone.View.extend({
loadTaskbarPopup: function() {
this.event_aggregator.trigger("tasks_popup:show")
}
});

Resources