Background
I am trying to pass data to a precompiled handlebars template using Backbone.Marionette.
These template renders but data is not populated in template. Can anyone say what I am doing wrong?
Using the backbone.Marionette.handlebars plugin.
Code
mws.app = new Backbone.Marionette.Application();
mws.app.addRegions({
page: "#page",
contentContainer: "#mws-content-container"
});
mws.views.pageView = Backbone.Marionette.ItemView.extend({
template:{
type: 'handlebars',
template: Handlebars.templates.page,
serializeData: function(){
return {
'baseurl': "http://www.overstock.com/yo",
'content': "content...?",
'current_year': new Date().getFullYear(),
}
}
}
});
mws.views.oasisRender = Backbone.Marionette.ItemView.extend({
template:{
type: 'handlebars',
template: Handlebars.templates.oasis,
serializeData: function(){
return {
'img1src': mws.homeDir+'img/oasis_1.png',
'img2src': mws.homeDir+'img/oasis_2.png',
'img3src': mws.homeDir+'img/oasis_3.png'
};
}
}
});
mws.app.router = Backbone.Marionette.AppRouter.extend({
appRoutes: {
'home' : 'homeRender'
},
homeRender: function(){
mws.app.contentContainer.show(new mws.views.oasisRender());
}
});
mws.app.addInitializer(function(){
mws.app.page.show(new mws.views.pageView());
mws.app.contentContainer.show(new mws.views.oasisRender());
});
mws.app.start();
Related
I have a backbonejs application that contains a router file and some views , and also i'm using requirejs to add views to routes and add templates to views. here is my codes :
routes.js
var AppRouter = Backbone.Router.extend({
routes: {
"": "getLogin",
"login": "getLogin",
"register": "getRegister",
"forget-password": "getForgetPassword"
},
getLogin: function() {
require(['views/auth/loginView'], function(view) {
view = new this.LoginView();
});
},
getRegister: function() {
require(['views/auth/registerView'], function() {
view = new this.RegisterView();
});
},
getForgetPassword: function() {
require(['views/auth/forgetPasswordView'], function() {
view = new this.ForgetPasswordView();
});
},
});
var route = new AppRouter();
Backbone.history.start();
loginView.js
var LoginView = Backbone.View.extend({
el: '#wrapper',
initialize: function() {
NProgress.start();
this.render();
},
render: function() {
require(['text!partials/auth/login.html'], function(t) {
var json = { title: 'title', formName: 'frmLogin' };
var template = _.template(t);
$('#wrapper').html(template(json));
});
NProgress.done();
},
events: {
"click #btnLogin": "login"
},
login: function(e) {
e.preventDefault();
alert('some message');
}
});
also registerView.js and forgetPasswordView.js are similar to loginView.js.
now! when i change routes multiple times and hit #btnLogn it fires alert('some message'); function multiple times...!
Have you tried un-delegating the events in the view, on route change?
You could override the route method (annotated source) in your AppRouter and run it before each route is rendered.
route: function(route, name, callback) {
view.undelegateEvents();
return Backbone.Router.prototype.route.apply(this, arguments);
}
Note: Just an idea, not tested with your code
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.
I have created a model like this
define(['backbone', 'text_dictionary'], function(Backbone, Text_Dict) {
var IndexPageModel = Backbone.Model.extend({
defaults:{
val_btn_gotohomepage : Text_Dict.val_btn_gotohomepage,
val_btn_gotologinpage : Text_Dict.val_btn_gotologinpage,
val_btn_gotolistpage : Text_Dict.val_btn_gotolistpage
}
});
return IndexPageModel;
});
and instantiated this model with 'new' in my page code like this
define([ 'page_layout',
'panel_itemview',
'header_itemview',
'content_itemview',
'footer_itemview',
'templates',
'text_dictionary',
'indexpage_model',
'indexpage_logic'],
function( Page,
Panel,
Header,
Content,
Footer,
Templates,
Text_Dict,
IndexPageModel,
IndexPage_BusnLogic) {
console.log("Success..Inside Index Page.");
var Page_Index = {};
Page_Index.page = (function(){
var _pageName = Text_Dict.indexpage_name;
var _pageModel = new IndexPageModel();
return _pageLayout = Page.pageLayout({
name:_pageName,
panelView: Panel.panelView({name:_pageName, pagetemplate: Templates.simple_panel}),
headerView: Header.headerView({name:_pageName, title: Text_Dict.indexpage_header, pagetemplate: Templates.header_with_buttons}),
contentView: Content.contentView({name:_pageName, page_model:_pageModel, pagetemplate:Templates.content_index, busn_logic:IndexPage_BusnLogic.HandleEvents}),
footerView: Footer.footerView({name:_pageName, title: Text_Dict.indexpage_footer, pagetemplate: Templates.simple_footer})
});
})();
return Page_Index;
});
my page gets created using the page layout
define([ 'underscore', 'marionette' ], function( _, Marionette ) {
console.log("Success..Inside Index View.");
var Page = {};
var _ReplaceWithRegion = Marionette.Region.extend({
open: function(view){
//Need this to keep Panel/Header/Content/Footer at the same level for panel to work properly
this.$el.replaceWith(view.el);
}
});
Page.pageLayout = function (opts) {
var _opts = _.extend ({ name: 'noname',
panelView: null,
headerView: null,
contentView: null,
footerView: null,
}, opts);
return new ( Marionette.Layout.extend({
tagName: 'section',
attributes: function() {
return {
'id': 'page_' + _opts.name,
'data-url': 'page_' + _opts.name,
'data-role': 'page',
'data-theme': 'a'
};
},
template: function () {
return "<div region_id='panel'/><div region_id='header'/><div region_id='content'/><div region_id='footer'/>";
},
regions: {
panel: {selector: "[region_id=panel]", regionType: _ReplaceWithRegion},
header: {selector: "[region_id=header]", regionType: _ReplaceWithRegion},
content: {selector: "[region_id=content]", regionType: _ReplaceWithRegion},
footer: {selector: "[region_id=footer]", regionType: _ReplaceWithRegion},
},
initialize: function(){
$('body').append(this.$el);
this.render();
},
onRender: function() {
if (this.options.panelView) {
this.panel.show (this.options.panelView);
};
if (this.options.headerView) {
this.header.show (this.options.headerView);
};
if (this.options.contentView) {
this.content.show(this.options.contentView);
};
if (this.options.footerView) {
this.footer.show (this.options.footerView);
};
},
}))(_opts);
};
return Page;
});
but in my itemview when i am passing model reference like this
define([ 'underscore', 'marionette', 'event_dictionary', 'app' ], function(_,
Marionette, Event_Dict, App) {
console.log("Success..Inside Content Index View.");
var Content = {};
Content.contentView = function(opts) {
return new (Marionette.ItemView.extend({
tagName : 'div',
attributes : function() {
console.log('options name==' + opts.name);
console.log("page model=="+opts.page_model);
return {
'region_id' : 'content',
'id' : 'content_' + opts.name,
'data-role' : 'content'
};
},
initialize : function() {
_.bindAll(this, "template");
},
template : function() {
return opts.pagetemplate;
},
model : function() {
return opts.page_model;
}
}))(opts);
};
return Content;
});
It's giving me error
Uncaught TypeError: Object function () {
return opts.page_model;
} has no method 'toJSON'
The model property of a view cannot be a function. Backbone allows this for some things like url (by way of the _.result helper function), but not in this case. Change your view code to not have a model function and just do this in initialize:
initialize: function (options) {
this.model = this.page_model = options.page_model;
}
UPDATE since you won't just take my word for it, here is the Marionette source that is almost certainly the top of your exception stack trace. Once again: view.model has to be a model object not a function. Fix that and the error will go away.
The accepted answer is correct, but it took a bit of messing about to find out why I had that error coming up, so I'm offering what the solution for my personal use-case was in case it helps anyone else stumbling upon this page in the future.
I had this:
app.module 'Widget.Meta', (Meta, app, Backbone, Marionette, $, _) ->
Meta.metaView = Backbone.Marionette.ItemView.extend
model: app.Entities.Models.meta
template: '#meta-template'
... when I should have had this:
app.module 'Widget.Meta', (Meta, app, Backbone, Marionette, $, _) ->
Meta.metaView = Backbone.Marionette.ItemView.extend
model: new app.Entities.Models.meta()
template: '#meta-template'
It's just a matter of instantiating the function definition.
I am developing my backbone application using require.js. all were fine. up to I integrate the routers. after I integrate my router I'm getting errors saying:
TypeError: appView is undefined
[Break On This Error]
that.$el.append(new appView.getView({model:data}).render());
and in the view.js I'm unable to route using this line(i intentionally commented)
listTrigger:function(){
myApp.navigate("/student");
}
and i post my all codes here... anyone can suggest or correct my code. or give me the reasons what i am doing wrong here?
main.js
require.config({
baseUrl: 'js/',
paths:{
'jquery' :"lib/jquery.min",
'underscore' :"lib/underscore-min",
'backbone' :"lib/backbone-min",
'appModel' :"app/model/model",
'appView' :"app/views/view",
'appViews' :"app/views/views",
'appRoute' :"app/router/router"
},
shim:{
underscore:{
exports:"_"
},
backbone:{
exports:"Backbone",
deps:["jquery","underscore"]
}
}
});
require(["appRoute"], function(appRoute) {
var myApp = new appRoute.getRoute();
Backbone.history.start();
});
model.js
define("appModel", ['backbone'], function(Backbone){
"use strict"
var appModel = Backbone.Model.extend({});
var appCollection = Backbone.Collection.extend({
model:appModel,
initialize:function(){
// console.log("initialized from collection");
}
});
return {
model: appModel,
collect:appCollection
}
});
view.js
define("appView", ["backbone","appViews"], function(Backbone,appViews){
"use strict"
var appView = Backbone.View.extend({
tagName:"li",
template:_.template($("#listTemp").html()),
events:{
"click" : "listTrigger"
},
initialize:function(){
this.render();
},
render:function(){
return this.$el.html(this.template(this.model.toJSON()));
},
listTrigger:function(){
// myApp.navigate("/student");
}
});
return{
getView: appView
}
})
views.js with some json data:
define('appViews', ["backbone","appModel","appView"], function(Backbone,appModel,appView){
"use strict";
var students = [
{"name":"student1"},{"name":"student2"},
{"name":"student3"},{"name":"student4"},
{"name":"student5"},{"name":"student6"},
{"name":"student6"},{"name":"student8"},
{"name":"student9"},{"name":"student0"}]
var appViews = Backbone.View.extend({
el:$("#app").find('ul'),
initialize:function(){
this.collection = new appModel.collect(students);
this.collection.on('reset', this.renderAll);
this.renderAll();
},
render:function(){
console.log("render called from views");
},
renderOne:function(){
console.log("render one")
},
renderAll:function(){
var that = this;
this.collection.each(function(data,i){
that.$el.append(new appView.getView({model:data}).render());
})
}
});
return {
appViews : appViews
}
})
router.js
define('appRoute', ["backbone","appModel","appView","appViews"], function(Backbone,appModel,appView,appViews){
var appRouter = Backbone.Router.extend({
routes:{
"":"initiate"
},
initialize:function(){
// console.log("called from routersssss");
},
initiate:function(){
new appViews.appViews();
}
})
return {
getRoute : appRouter
}
})
across this all are working correct up to using routers. after I'm not getting the result. Am I using routers incorrectly?
#TomasKirda: you can new-up without the trailing parentheses, but I wouldn't recommend it! Also, see here.
Having said that, you have identified the problem in this instance!
This code:
new appView.getView(...);
Is trying to create a new appView.getView which isn't a reference to a constructor (it would be if the appView constructor had a property getView).
So in this case, you are quite right that the parentheses are required:
new appView().getView(...);
Here's my Backbone.js:
(function() {
window.App = {
Models: {},
Collections: {},
Views: {},
Router: {}
};
window.template = function(id) {
return _.template( $('#' + id).html() );
};
var vent = _.extend({}, Backbone.Events);
App.Router = Backbone.Router.extend({
routes: {
'' : 'index',
'*other' : 'other'
},
index: function() {
},
other: function() {
}
});
App.Models.Main = Backbone.Model.extend({
defaults : {
FName: ''
}
});
App.Collections.Mains = Backbone.Collection.extend({
model: App.Models.Main,
initialize: function() {
this.fetch({
success: function(data) {
console.log(data.models);
}
});
},
url: '../leads/main_contact'
});
App.Views.Mains = Backbone.View.extend({
tagName: 'ul',
initialize: function() {
this.collection.on('reset', this.render, this);
console.log(this.collection);
},
render: function() {
return this.collection.each(this.addOne, this);
},
addOne: function(main) {
var mainC = new App.Views.Main({ model: main});
this.$el.append(mainC.render().el);
return this;
}
});
App.Views.Main = Backbone.View.extend({
tagName: 'li',
template: template('mainContactTemplate'),
render: function () {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
mains = new App.Collections.Mains();
main = new App.Views.Main({ collection: mains});
new App.Router;
Backbone.history.start();
})();
What I want to do is have the data returned in the ul to be bound to a DOM element called $('#web-leads'). How do I do that, given this code? Incidentally, I've already posted about this here and tried to follow the first answer and the second answer combined. But I still don't get HTML and data bound to the DOM. The data is returning from the server correctly in my collection, so I know that's not the problem. Don't worry about the router stuff. That's for later.
Generally in Backbone you don't put data on DOM elements: you put it in views that wrap that DOM element.
That being said, if you really want to store data on the element, jQuery has a function for that:
$('#web-leads').data('someKey', 'yourData');
Then you can retrieve that data with:
$('#web-leads').data('someKey');
* EDIT *
In a comments discussion with the OP it became apparent that the real goal was simply to append a view's element to an element on the page. If the element being appended to is #web-leads, then this can be accomplished with:
$('#web-leads').append(theView.render().el);