Using Backbone.View I used to get external template in this way (1)
What if I would like to get the template from external file using Backbone.Marionette.Layout? (2)
(1)
define(['myTemplate.html'], function (myTemplate) {
var MyView = Backbone.View.extend({
render: function ()
{
this.$el.html(myTemplate, this.getView());
}
});
return MyView;
});
(2)
define(['myTemplate.html'], function (myTemplate) {
var Layout = Backbone.Marionette.Layout.extend({
template: "#container",
regions: {
top: "#top",
main: "#main"
}
});
return Layout;
});
Take a look at this: https://github.com/marionettejs/backbone.marionette/wiki/Using-marionette-with-requirejs
this.$el.html(myTemplate, this.getView());
Related
Alright people,
I am using the RequireJs text plugin to pull in underscore templates (.html) in my backbone application. Unfortunately my underscore code in my templates are being rendered as plain text.
define(['Backbone', 'text!Templates/BlogIndex.html', 'text!Templates/Elements/Blog/List.html'], function(Backbone, Template, ElementList){
var BlogPostIndexView = Backbone.View.extend({
initialize: function () {
this.template = _.template($(Template).html(), {posts : this.collection});
this.render();
},
render: function (Template) {
this.$el.html(this.template);
return this;
}
});
return BlogPostIndexView;
});
Here is my code for my view, you can see i am pulling-in two templates and setting them. However it getting rendered as ...
Globall Coach Blog Posts
<% _.each(posts, function(post){ %>
<%= _.escape(post.title) %>
<% }); %>
Has anybody ever had this issue?
Seems like you're not using the underscore template function correctly. Underscore compiles the string into a function in which you can pipe in data. so your code should look like this:
define(['Backbone', 'text!Templates/BlogIndex.html', 'text!Templates/Elements/Blog/List.html'], function(Backbone, Template, ElementList){
var BlogPostIndexView = Backbone.View.extend({
initialize: function () {
this.template = _.template($(Template).html())({posts : this.collection});
this.render();
},
render: function () {
this.$el.html(this.template);
return this;
}
});
return BlogPostIndexView;
But I would refactor this further because you usually want to dynamically re-render with the latest data so I would put the piping in the data to the template in the "render" method instead of "initialize".
So preferably I would do this:
define(['Backbone', 'text!Templates/BlogIndex.html', 'text!Templates/Elements/Blog/List.html'], function(Backbone, Template, ElementList){
var BlogPostIndexView = Backbone.View.extend({
initialize: function () {
this.template = _.template($(Template).html())
this.render();
},
render: function () {
this.$el.html(this.template({posts : this.collection}));
return this;
}
});
return BlogPostIndexView;
Turns #mu-is-to-short was correct, requireJs text module returns the the raw html.
Here it `define(['Backbone', 'text!Templates/BlogIndex.html', 'text!Templates/Elements/Blog/List.html'], function(Backbone, Template, ElementList){
var BlogPostIndexView = Backbone.View.extend({
initialize: function () {
this.template = _.template(Template);
},
render: function (Template) {
this.$el.html(this.template({posts : this.collection.toJSON()}));
return this;
}
});
return BlogPostIndexView;
});
Expanding my original question located here
If my Userview is in Userview.js file and I want to inherit from that class in AdminView.js file, how would I go about it.
I tried this, but would not fit my need as I don't have a class.
UPDATE 1:
define([
'modules/userdetail'
],
function(UserView) {
var adminView
adminView.Views.Content = UserView.Views.Content.extend({
initialize: function() {
//looking to override the fn that is declared in UserView
console.log("AAA");
},
});
}
UPDATE 2:
So digging deep, the User Detail is
define(
[ 'modules/baseClass'],
function(BaseClass) {
// Create a new module
//Create Model
//Create View
UserDetails.Views.Content = Backbone.View
.extend({
template :
initialize : function() {
this.model = new UserDetails.Model();
},
events : {
},
render : function(LayOut) {
return LayOut(this).render().then(this.pageReady);
},
pageReady : function() {
},
});
UserDetails.activate = function() {
app.router.navigate('UserDetails', true);
};
UserDetails.configureRouting = function() {
app.router.route('UserDetails', 'UserDetails',
function() {
layoutmanager.setView('#content',
new UserDetails.Views.Content())
.render();
});
};
return UserDetails;
});
ADMIN:
define([
'modules/baseclass',
'modules/UserDetail'
],
function(BaseClass, UserDetails) {
UserDetail.Views.Content = UserDetail.Views.Content.extend({
render:function(){
console.log("rendering");
UserDetail.Views.Content.prototype.render();
}
});
//create admin model
//admin view
AdminView.Views.Content = Backbone.View.extend({
template: "admin-template",
events: {
},
initialize: function() {
this.model = new AdminModel.Model();
},
render: function(manage) {
return manage(this).render().then(this.pageReady);
},
pageReady: function() {
});
},
AdminView.activate = function() {
app.router.navigate('adminview', true);
};
AdminView.configureRouting = function() {
app.router.route('adminview', 'adminview', function() {
layoutmanager.setView('#content', new AdminView.Views.Content()).render();
layoutmanager.setView('#userDetials', new UserDetials.Views.Content()).render();
});
};
if(app.router && app.router.route) {
AdminView.configureRouting();
}
return AdminView;
});
Now if I have to call the render of the userdetails from admin view, the render method fails as the param is undefined.
I am not well versed with where the para in render is defined as I looked through my code and have not found anything
Either include the script tag for Userview.js before the script tag for AdminView.js, or using a module system like requirejs or browserify where you can specify the two modules as dependencies.
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
}
});
So I have this current situation:
app.Ui.ModalView = Backbone.View.extend({
events: {
},
initialize: function() {
},
render: function() {
var that = this;
var model = this.model.toJSON();
that.$el.html(that.template(_.extend(this.params || {}, {
model: model,
})));
return this;
}
});
and then the inherited view:
app.Views.childView = kf.Ui.ModalView.extend({
template: JST["templates/app/blah/blah-edit.html"],
events: {
},
initialize: function() {
var that = this;
this.events = _.extend({}, app.Ui.ModalView.prototype.events, this.events);
app.Ui.ModalView.prototype.initialize.apply(this, arguments);
},
render: function(){
// add extra logic in this render function, to run as well as the inherited render function?
}
});
So, I don't want to override the parent's render(), but to add extra functionality to it, how would I go about doing that?
Two ways to achieve this: Either you can add explicit support for overriding the behaviour by creating a "render hook" in the base class, or you'll have to call the overridden base method from the superclass method:
Render hook in base class:
app.Ui.ModalView = Backbone.View.extend({
render: function() {
//if this instance (superclass) defines an `onRender` method, call it
if(this.onRender) this.onRender();
//...other view code
}
}
app.Views.childView = kf.Ui.ModalView.extend({
onRender: function() {
//your custom code here
}
});
Call base class method from super class:
app.Views.childView = kf.Ui.ModalView.extend({
render: function() {
//your custom code here
//call the base class `render` method
kf.Ui.ModalView.prototype.render.apply(this, arguments);
}
});
I'm having a problem with rendering a backbone.js view successfully from a route handler (browser application).
My javascript module is currently setup like this:
$(function () { // DOM ready
myModule.Init();
});
var myModule = (function () {
// Models
var DonorCorpModel = Backbone.Model.extend({ });
// Collections
var DonorCorpsCollection = Backbone.Collection.extend({ model : DonorCorpModel });
// Views
var DonorCorpsListView = Backbone.View.extend({
initialize : function () {
_.bindAll(this, 'render');
this.template = Handlebars.compile($('#pre-sort-actions-template').html())
this.collection.bind('reset', this.render);
},
render : function () {
$(this.el).html(this.template({}));
this.collection.each(function (donorCorp) {
var donorCorpBinView = new DonorCorpBinView({
model : donorCorp,
list : this.collection
});
this.$('.donor-corp-bins').append(donorCorpBinView.render().el);
});
return this;
}
});
var DonorCorpBinView = Backbone.View.extend({
tagName : 'li',
className : 'donor-corp-bin',
initialize : function () {
_.bindAll(this, 'render');
this.model.bind('change', this.render);
this.template = Handlebars.compile($('#pre-sort-actions-donor-corp-bin-view-template').html());
},
render : function () {
var content = this.template(this.model.toJSON());
$(this.el).html(content);
return this;
}
})
// Routing
var App = Backbone.Router.extend({
routes : {
'' : 'home',
'pre-sort' : 'preSort'
},
initialize : function () {
// ...
},
home : function () {
// ...
},
preSort : function () {
if (donorCorps.length < 1) {
donorCorps.url = 'http://my/api/donor-corps';
donorCorps.fetch();
}
var donorCorpsList = new DonorCorpsListView({ collection : donorCorps }).render().el;
$('#document-action-panel').empty().append(donorCorpsList);
// ...
}
});
// Private members
var app;
var donorCorps = new DonorCorpsCollection();
// Public operations
return {
Init: function () { return init(); }
};
// Private operations
function init () {
app = new App();
Backbone.history.start({ root: '/myApp/', pushState: true });
docr.navigate('/', { trigger: true, replace: true});
}
}(myModule || {}));
Everything runs just fine when I run the app...it navigates to the home view as expected. I have links setup with handlers to navigate to the different routes appropriately, and when I run app.navigate('pre-sort', { trigger: true, replace: true}) it runs just fine, and renders the expected items in the list as expected.
The problem comes when I try to go back to the home view, and then back to the pre-sort view again...the items in the list don't get displayed on the screen as I am expecting them to. I'm just getting the empty pre-sort-actions-template rendered with no child views appended to it. I've stepped through the code, and can see that the collection is populated and the items are there...but for some reason, my code isn't rendering them to the view properly, and I can't seem to figure out why or what I'm doing wrong. Any ideas?? I'm pretty new to backbone, so I'm sure this code isn't written totally right...constructive feedback is welcome. Thanks.
how is the code rendering it to the view after going back home, then to pre-sort again? could you provide some details on that? duplicate items? empty view?
Also, I like to keep an index of my sub-views when I render them so the parent view can always access them regardless of scope. I found a really nice technique for this here: Backbone.js Tips : Lessons from the trenches
A quick overview of the pattern I'm referring to is as follows:
var ParentView = Backbone.View.extend({
initialize: function () {
this._viewPointers = {};
},
render: function (item) {
this._viewPointers[item.cid] = new SubView ({
model: item
});
var template = $(this._viewPointers[item.cid].render().el);
this.$el.append(template);
}
});
var SubView = Backbone.View.extend({
initialize: function () {
this.model.on("change", this.render, this);
},
render: function () {
this.$el.html( _.template($('#templateId').html(), this.model.attributes) );
return this;
}
});
I realize this answer is rather "broad," but it will be easier to answer with more specifics if I can understand the exact issue with the rendering. Hope its of some help regardless :)