I'm trying to add a language support in my website and I need to add this code so it will run before marionette render in all the views no matter which type.
onBeforeRender: function(){
var helpers = this.templateHelpers();
this.templateHelpers = function(){
return $.extend( (helpers), {
lang : function () {
return function(val, render) {
return lang(val);
}
}
});
}
}
I don't want to extend all the views and put this code in each of them,
I wonder if there is a way to just put this code in some place and it will run before every render
You should be able to extend the prototype with something like
_.extend(Marionette.View.prototype, {
onBeforeRender: function(){
var helpers = this.templateHelpers();
this.templateHelpers = function(){
return $.extend( (helpers), {
lang : function () {
return function(val, render) {
return lang(val);
}
}
});
}
}
})
Naturally, that means that if one of your marionette views defines its own onBeforeRender, you'll need to call the implementation on the View prototype "by hand".
I think you should create a view mixin with your code and extend every view with this mixin
var LangMixin = {
onBeforeRender: function(){
var helpers = this.templateHelpers();
this.templateHelpers = function(){
return $.extend( (helpers), {
lang : function () {
return function(val, render) {
return lang(val);
}
}
});
}
}
}
var YourView= Backbone.View.extend({
// ...
});
_.extend(YourView.prototype, LangMixin);
Related
I'm trying to render a partial view within a Backbone View with it's render method. I created a sort of helper to do this.
var DashboardPartial = (function(){
var _getPartialView = function() {
$.ajax({
url: _baseUrl + _url,
})
.done(function(response) {
_returnView(response);
})
.fail(function() {
console.log("error");
})
.always(function() {
console.log("complete");
});
};
var _returnView = function (response) {
return response;
};
return {
get: function (url) {
_url = url;
_baseUrl = '/dashboard/';
_getPartialView();
},
};
}());
So, what I want to do is call DashboardPartial.get('url') and use the response within the Backbones View render method. Something like the following:
render: function() {
partial = DashboardPartial.get('url');
this.$el.html(partial);
return this;
}
The problem is that the function does get the partial from the server, but I can't find a way to return the response. Doing console.log(response) inside the DashboardPartial function does show the partial, but I want to be able to return it and then pass it as a variable to "this.$el.html()".
You should return deferred ($.ajax returns it by default) from helper:
var DashboardPartial = (function(){
var _getPartialView = function() {
return $.ajax({
url: _baseUrl + _url,
});
};
return {
get: function (url) {
_url = url;
_baseUrl = '/dashboard/';
return _getPartialView();
},
};
}());
And then use it in your render:
render: function() {
var self = this,
dfd = $.Deferred();
DashboardPartial.get('url').done(function(partial){
self.$el.html(partial);
dfd.resolve();
});
return dfd; // use this outside, to know when view is rendered; view.render().done(function() { /* do stuff with rendered view */});
}
However, you could use requirejs for this, plus requirejs-text plugin to load templates, because your view has dependency on partial.
As I understood, you want render different partials using one backbone view.
You could create factory, smth like this:
var typeToTemplateMap = {};
typeToTemplateMap["backboneViewWithFirstPartial"] = firstPartial;
function(type) {
return typeToTemplateMap[type];
}
And then use it in your view:
initialize: function(options) {
this.partial = partialFactory(options.type);
},
render: function() {
this.$el.html(this.partial);
return this;
}
This is how it would look like using requirejs:
// factory.js
define(function(require) {
var partialOne = require("text!path/to/partialOne.htm"), // it's just html files
partialTwo = require("text!path/to/partialTwo.htm");
var typeToPartialMap = {};
typeToPartialMap["viewWithFirstPartial"] = partialOne;
typeToPartialMap["viewWithSecondartial"] = partialTwo;
return function(type) {
return typeToPartialMap[type];
}
});
// view.js
define(function(require){
var Backbone = require("backbone"), // this paths are configured via requirejs.config file.
partialFactory = require("path/to/factory");
return Backbone.View.extend({
initialize: function(options) {
this.partial = partialFactory(options.type);
},
render: function() {
this.$el.html(this.partial);
return this;
}
});
});
// Another_module.js, could be another backbone view.
define(function(require) {
var MyView = require("path/to/MyView"),
$ = require("jquery");
var myView = new MyView({type: "firstPartial"});
myView.render(); // or render to body $("body").append(myView.render().$el);
});
You should consider using requirejs, because you doing almost the same, but without dependencies handling.
Docs on requirejs can be found here: http://requirejs.org/docs/start.html
I have couple of modules that do their own thing, but need them to sometimes access a property of one another (not that intertwined, just one json obj). Like so
var Bananas = (function() {
// Bananas.properties would look like this
// Bananas.properties = { 'color' : 'yellow' };
var methodToGetProperties = function() {
API.get('bananas')
.done(function(data) {
Bananas.properties = data;
}
};
var publiclyReturnProperties = function() {
if (!Bananas.properties) {
methodToGetProperties();
} else {
return Bananas.properties;
}
};
var doSomethingBananas = function() {
bananas.doing.something;
bananaHolder.innerHTML = Bananas.properties;
}
var init = function() {
doSomethingBananas
}
return {
init: init,
properties: publiclyReturnProperties,
};
})();
var Apples = (function() {
var doSomethingApples = function() {
apple.innerHTML = Bananas.properties.color;
};
var init = function() {
doSomethingApples();
};
return {
init: init
};
})();
Bananas.init(); Apples.init();
Now, the way I do it now is by simply revealing the methodToGetProperties, which returns the API call, and then work on using jQueries deferred method wherever I call it. But I feel this ruins my code by putting .done everywhere.
I've been reading up to singleton pattern and feel it might be the solution to my problem, but I'm not sure how to implement it. Or maybe implement a callback function in methodToGetProperties, but again not confident as to how.
Would kindly appreciate advice on how to organise my app.
I am trying to get into backbone and have the following which is an attempt at doing an image gallery. I am trying to use render with a model in a collection. I will show the first element of the collection but I would like to add support for simply rerendering with the next element but I don't know how to do this .
I have implemented next and previous on my model like the following:
arc.Item = Backbone.Model.extend({
next: function () {
if (this.collection) {
return this.collection.at(this.collection.indexOf(this) + 1);
}
},previous: function () {
if (this.collection) {
return this.collection.at(this.collection.indexOf(this) - 1);
}
}
});
The problem here (there could be more than the one I am asking about though) is in the loadNext method. How would I get the current location in this collection and to increment it?
arc.ItemsGalleryView = Backbone.View.extend({
el: $('#mig-container'),
events: {'click .next-btn' : 'loadNext',
'click .previous-btn':'loadPrevious' },
template:_.template($('#mig-image-tmp').text()),
initialize: function() {
_.bindAll( this, 'render' );
// render the initial state
var thisModel=this.collection.first();
this.render(thisModel);
},
render: function(xModel) { // <- is it ok to do it this way?
var compiled=this.template(xModel.toJSON());
this.$el.html(compiled);
return this;
},
loadNext: function(){
console.log('i want to load Next');
this.render(this.collection.next()); // <- how would I do this
this.render(this.collection.first().next()); // <- this works by always giving me the second.
// I need to replace first with current
},
loadPrevious: function(){
console.log('i want to load Previous');
}
Or is there a better way to implement this?
thx in advance
edit #1
arc.ItemsGalleryView = Backbone.View.extend({
el: $('#mig-container'),
events: {'click .next-btn' : 'loadNext', 'click .previous-btn':'loadPrevious' },
template:_.template($('#mig-image-tmp').text()),
initialize: function() {
_.bindAll( this, 'render' );
this.render(this.collection.first()); // <- this works correct
},
render: function(xModel) {
console.log(xModel.toJSON());
var compiled=this.template(xModel.toJSON());
this.$el.html(compiled);
return this;
},
loadNext: function(){
console.log('i want to load next');
this.render(this.collection.next()); // <- this doesn't seem to do anything, event is called correctly but doesn't seem to move to next element
},
However if I adjust to this, it will load the 3rd element of the array
loadNext: function(){
console.log('i want to load Previous');
this.render(this.collection.at(2));
},
How would I use this.collection.next() to get this behavior?
thx
What it looks like you're looking for is a way to use the Collection to manipulate the next/prev stuff. What you currently have only puts it on the model. Here's a base Collection I use in my projects:
App.Collection = Backbone.Collection.extend({
constructor: function(models, options) {
var self = this;
var oldInitialize = this.initialize;
this.initialize = function() {
self.on('reset', self.onReset);
oldInitialize.apply(this, arguments);
};
Backbone.Collection.call(this, models, options);
},
onReset: function() {
this.setActive(this.first());
},
setActive: function(active, options) {
var cid = active;
if ( active instanceof Backbone.Model ) {
cid = active.cid;
}
this.each(function(model) {
model.set('current', model.cid === cid, options);
});
},
getActive: function() {
return this.find(function(model) {
return model.get('current');
});
},
next: function() {
return this.at(this.indexOf(this.getActive()) + 1);
},
prev: function() {
return this.at(this.indexOf(this.getActive()) - 1);
}
});
It's probably not perfect, but it works for me. Hopefully it can at least put you on the right track. Here is how I use it:
var someOtherCollection = App.Collection.extend({
model: MyModel
});
kalley's answer is right on, but I will throw in another example.
I would entertain the idea of keeping the current model inside of state model, and work from there. You can store other application information within the state model as well.
Your model declaration would look like the following. Notice I renamed previous to prev. Backbone.Model already has a previous method and we don't want to over-ride it.
var Model = Backbone.Model.extend({
index:function() {
return this.collection.indexOf(this);
},
next:function() {
return this.collection.at(this.index()+1) || this;
},
prev:function() {
return this.collection.at(this.index()-1) || this;
}
});
Have a generic Backbone.Model that holds your selected model:
var state = new Backbone.Model();
In the view you will listen for changes to the state model and render accordingly:
var View = Backbone.View.extend({
el: '#mig-container'
template:_.template($('#mig-image-tmp').html()),
events: {
'click .prev' : 'prev',
'click .next' : 'next'
},
initialize:function() {
this.listenTo(state,'change:selected',this.render);
state.set({selected:this.collection.at(0)});
},
render:function() {
var model = state.get('selected');
this.$el.html(this.template(model.toJSON()));
return this;
},
next:function() {
// get the current model
var model = state.get('selected');
/* Set state with the next model, fires a change event and triggers render.
Unless you are at the last model, then no event will fire.
*/
state.set({selected:model.next()});
},
prev:function() {
var model = state.get('selected');
state.set({selected:model.prev()});
}
});
Here is a demo. I like the state model approach because I'm not storing application-state information within my models.
If you don't like the state model approach, you can always just throw it on the floor:
/ .. code above ../
initialize:function() {
this.model = this.collection.at(0);
this.render();
},
render:function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
next:function() {
this.model = this.model.nxt();
this.render();
},
prev:function() {
this.model = this.model.prev();
this.render();
}
I have the following:
// Child Array is Cards, trying to add computed observable for each child
var CardViewModel = function (data) {
ko.mapping.fromJS(data, {}, this);
this.editing = ko.observable(false);
};
var mapping = {
'cards': { // This never gets hit, UNLESS I remove the 'create' method below
create: function (options) {
debugger;
return new CardViewModel(options.data);
}
},
create: function(options) {
var innerModel = ko.mapping.fromJS(options.data);
innerModel.cardCount = ko.computed(function () {
return innerModel.cards().length;
});
return innerModel;
}
};
var SetViewModel = ko.mapping.fromJS(setData, mapping);
debugger;
ko.applyBindings(SetViewModel);
However I can't get the 'cards' binding to work - that code isn't reached unless I remove the 'create' method. I'm trying to follow the example from the knockout site:
http://knockoutjs.com/documentation/plugins-mapping.html
They do this for the child object definition:
var mapping = {
'children': {
create: function(options) {
return new myChildModel(options.data);
}
}
}
var viewModel = ko.mapping.fromJS(data, mapping);
With the ChildModel defined like this:
var myChildModel = function(data) {
ko.mapping.fromJS(data, {}, this);
this.nameLength = ko.computed(function() {
return this.name().length;
}, this);
}
I've spent the past day on this and cannot for the life of me figure out why this isn't working. Any tips would be awesome.
EDIT: Here's a fiddle of what I'm working with. It's only showing SIDE 1 in the result because "editing" isn't recognized here:
<div data-bind="visible: !$parent.editing()" class="span5 side-study-box">
http://jsfiddle.net/PTSkR/1/
This is the error I get in chrome when I run it:
Uncaught Error: Unable to parse bindings. Message: TypeError: Object
has no method 'editing'; Bindings value: visible: !$parent.editing()
You have overridden the create behavior for your view model. The mapping plugin will not call any of the other handlers for the properties for you. Since you're mapping from within the create method, move your cards handler in there.
var mapping = {
create: function(options) {
var innerModel = ko.mapping.fromJS(options.data, {
'cards': {
create: function (options) {
debugger;
return new CardViewModel(options.data);
}
}
});
innerModel.cardCount = ko.computed(function () {
return innerModel.cards().length;
});
return innerModel;
}
};
updated fiddle
you didnt needed to have parenthesis. I just changed from
!$parent.editing()
to
!$parent.editing
See the updated fiddle here
In the following code, I want to be able to call bindClickEvents() like so:
App.Utils.Modal.bindClickEvents();
However, I don't understand the syntax necessary to do this.
Current code:
var App = new Object;
App.Modal = {
bindClickEvents: function() {
return $('a.alert-modal').click(function(e) {
return console.log('Alert Callback');
});
}
};
$(document).ready(function() {
return App.Modal.bindClickEvents();
});
You can do it in one go:
var App = {
Modal : {
bindClickEvents : function () {/* ... */}
}
}
or if you want to break that up to separate steps:
var App = {};
App.Modal = {};
Modal.bindClickEvents = function () {/* ... */};
BTW, in reference to your original question title, this is not object chaining. This is object composition. Object chaining is being able to call methods in an object multiple times in a single statement.
Is this what you're trying to do?
var App = {};
App.Utils = {};
App.Utils.Modal = {
bindClickEvents: function() {
return $('a.alert-modal').click(function(e) {
return console.log('Alert Callback');
});
}
};
$(document).ready(function() {
return App.Utils.Modal.bindClickEvents();
});
Prefer the object literal syntax to the Object constructor; some authors go so far as to call the latter an anti-pattern
Here's the simplest way to set up App.Utils.Modal.bindClickEvents();
var App = {
Utils: {
Modal: {
bindClickEvents: function() {
return $('a.alert-modal').click(function(e) {
return console.log('Alert Callback');
});
}
}
}
};
Or you can piece it together one step at a time:
var App = {};
App.Utils = {};
App.Utils.Modal = {};
App.Utils.Modal.bindClickEvents = function() {
return $('a.alert-modal').click(function(e) {
return console.log('Alert Callback');
});
};