Whats the problem have to face calling view to another view?...when i call the menu view to home view. Menu view not rendering properly. and big problem here that events not working !! but if call the menu view individualy then works fine. i have question that whats the problem exacty in events and render template??
define([
"jquery",
"underscore",
"backbone",
"models/menu/MenuModel",
"collections/menu/MenuCollections",
"text!templates/menu/menuTemplate.html",
], function ($, _, Backbone, MenuModel, MenuCollections, menuTemplate) {
var MenuView = Backbone.View.extend({
model: new MenuModel,
el:".sidebar_menu",
template: _.template(menuTemplate),
events:{
"click #sidebar .has-sub > a":"main_menu"
},
initialize:function(){
this.render();
},
render: function () {
this.$el.html(this.template());
},
main_menu:function(e){
alert("rakib");
var last = $('.has-sub.open', $('#sidebar'));
last.removeClass("open");
$('.arrow', last).removeClass("open");
$('.sub', last).slideUp(200);
var sub = $(e.target).next();
if (sub.is(":visible")) {
$('.arrow', $(e.target)).removeClass("open");
$(e.target).parent().removeClass("open");
sub.slideUp(200);
} else {
$('.arrow', jQuery(e.target)).addClass("open");
$(e.target).parent().addClass("open");
sub.slideDown(200);
}
}
});
return MenuView;
});
When i call menu view to home view..event is not working. whats the problem exactly ??
define([
"jquery",
"underscore",
"backbone",
"views/menu/MenuView",
"models/home/HomeModel",
"text!templates/home/homeTemplate.html",
], function ($, _, Backbone, MenuView, HomeModel, homeTemplate) {
var HomeView = Backbone.View.extend({
model: new HomeModel,
el: $("#container"),
template: _.template(homeTemplate),
initialize:function(){
this.render();
this.menu_render();
},
render: function () {
this.$el.html(this.template());
},
menu_render:function()
{
(new MenuView).render();
}
});
return HomeView;
});
Well, you need to add your new view inside an html element of your current view to display it.
I would suggest to add your MenuView to an html element defined in your template file of your HomeView using an append function.
Like this:
$('#yourHomeViewHTMLElementDefinedInTemplate').append((new MenuView).render().el);
in your code try this:
menu_render:function()
{
$("#container").append((new MenuView).render().el);
}
I hope it helps.
Related
I just started to lean Backbone, and underscore template, not sure if the structure suitable for it.
The question is, when I reload a template, how to re-bind event from Backbone which is re-run the events function.
The example is simply load an index page, insert main_option template into the page, and jump between main_option, and role_view template.
Here is the app.js which I put router in there:
define(['jquery', 'underscore', 'backbone', 'views/role_view', 'views/main_options'], function ($, _, Backbone, rolePage, mainOptions) {
var appRouter = Backbone.Router.extend({
$el: $('.container'),
initialize: function () {
this.mainOptionPage = mainOptions;
this.loginView = rolePage;
},
routes: {
"": "mainOption",
"views/role_view": "login"
},
mainOption: function () {
this.$el.html(this.mainOptionPage.render().$el);
},
login: function () {
this.$el.html(this.loginView.render().$el);
}
});
var router = new appRouter();
Backbone.history.start();
});
Here is the main_option.js
define(['jquery', 'underscore', 'backbone'], function($, _, Backbone){
var Person = Backbone.Model.extend({
defaults: {
name: 'Guest Worker',
age: 23,
occupation: 'worker'
}
});
var testView = Backbone.View.extend({
$el: $('#indexPage'),
initialize: function () {
var self = this;
$.get('/test/templates/mainOptions.html').success(function (data) {
self.template_loaded(data);
template = _.template(data, {name: "Test"});
}, 'html');
},
events: {
'click .signButton': 'pageToSign'
},
pageToSign: function (e) {
e.preventDefault();
Backbone.history.navigate("views/role_view", {trigger: true});
},
template_loaded: function (html) {
var template = _.template(html, {name: "Test"});
this.$el.html(template);
return this;
}
});
var person = new Person;
return new testView({model: person});
});
and final page is role_view.js
define(['jquery', 'underscore', 'backbone'], function($, _, Backbone){
var role = Backbone.View.extend({
initialize: function(){
var self = this;
$.get('/test/templates/chooseRole.html').success(function(html){
self.template_loaded(html);
});
},
events: {
'click .parentButton': 'parentClick'
},
template_loaded: function(html) {
var template = _.template(html, {name: "Test"});
this.$el.html(template);
return this;
},
parentClick: function(e) {
e.preventDefault();
Backbone.history.navigate("", {trigger: true});
}
});
return new role();
});
Thanks.
You real problem is that you're reusing views rather than destroying and creating them as needed. In your router, you have this:
mainOption: function () {
this.$el.html(this.mainOptionPage.render().$el);
},
login: function () {
this.$el.html(this.loginView.render().$el);
}
You call this.$el.html the first time, the view goes up, and everything seems to be okay. Then you switch views by calling this.$el.html and everything still seems to be okay. But the next time you switch views, your events are gone. This happens because of the way jQuery's html function works; from the fine manual:
When .html() is used to set an element's content, any content that was in that element is completely replaced by the new content. Additionally, jQuery removes other constructs such as data and event handlers from child elements before replacing those elements with the new content.
Emphasis mine. Calling this.$el.html will destroy the event bindings on the previous content (such as this.mainOptionsPage.el or this.loginView.el).
If you create and destroy views as needed:
define(['jquery', 'underscore', 'backbone'], function($, _, Backbone){
// Your Person model goes in its own file or possibly in the router file for now...
var TestView = Backbone.View.extend({
//...
});
return TestView; // Return the view "class", not an instance.
});
define(['jquery', 'underscore', 'backbone'], function($, _, Backbone){
var Role = Backbone.View.extend({
//...
});
return Role;
});
define(['jquery', 'underscore', 'backbone', 'views/role_view', 'views/main_options', 'models/person'], function ($, _, Backbone, Role, TestView, Person) {
var person = new Person; // The person model is managed here.
var appRouter = Backbone.Router.extend({
//...
initialize: function () {
// Don't need anything in here anymore.
},
//...
mainOption: function () {
// Create a new view when we need it.
this.switchTo(new TestView({ model: person }));
},
login: function() {
// Create a new view when we need it.
this.switchTo(new Role);
},
switchTo: function(view) {
// Destroy the old view since we don't need it anymore.
if(this.currentView)
this.currentView.remove();
// Keep track of the new current view so that we can
// kill it alter and avoid leaks.
this.currentView = view;
this.$el.html(this.currentView.render().el);
}
});
//...
});
I'm having troubles handling events between my views and collections. In the below example you can find a short version of what does my webapp look like and how are the events being handled now.
What happens here is that when switching from menu1 to menu2 or even when going backwards, it causes that the "APP:change_city" event listener is stacked up. So when I then trigger this event , it calls the method OnCityChange() as many times as I switched between the menus.
I'm now not really sure whether I'm using the event aggregator (eMgr) correctly.
Can anyone please assist?
eMgr.js
define(['backbone.wreqr'],function(Wreqr){
"use strict";
return new Wreqr.EventAggregator();
})
AppRouter.js
define(['marionette'], function (Marionette) {
"use strict";
var AppRouter = Marionette.AppRouter.extend({
appRoutes: {
'menu1' : 'showMenu1',
'menu1' : 'showMenu2'
}
});
return AppRouter;
});
AppControler.js
define(['underscore', 'backbone', 'marionette', '../eMgr'], function (_, Backbone, Marionette, eMgr) {
"use strict";
var Controller = Marionette.Controller.extend({
initialize: function(){
console.log("AppRouter - Init")
},
showMenu1: function (city) {
console.log(" [Info] [AppControler] opening Menu1");
eMgr.trigger("APP:open_menu", { menu: "Menu1", city: city});
},
showMenu2: function (city) {
console.log(" [Info] [AppControler] opening Menu2");
eMgr.trigger("APP:open_menu", { menu: "Menu2", city: city});
}
});
return Controller;
});
App.js
define([ 'backbone', 'underscore', 'marionette', 'eMgr',
'layouts/MainMenu/layoutview.menu1',
'layouts/MainMenu/layoutview.menu2',
'controllers/AppController', 'routers/AppRouter'],
function (Backbone, _, Marionette, eMgr,
lv_mainmenu1, lv_mainmenu2,
AppController, AppRouter) {
"use strict";
var MyApp = new Marionette.Application();
var controller = new AppController();
MyApp.addRegions({
.....
mainmenu: '#main_menu',
.....
});
MyApp.listenTo(eMgr, "menu_changed",function(eData){
switch(eData.menu){
case "Menu1":
MyApp.mainmenu.show(new lv_mainmenu1(eData));
break;
case "Menu2":
MyApp.mainmenu.show(new lv_mainmenu2(eData));
break;
}
});
MyApp.addInitializer(function(options) {
var router = new AppRouter({
controller : controller
});
});
MyApp.on("start", function(){
if (Backbone.history){
Backbone.history.start();
}
});
$(document).ready(function() {
MyApp.start();
});
});
layoutview.menu1.js
define([ 'backbone', 'underscore', 'marionette',
'templates/template.mainmenu',
'layouts/MainMenu/collectionview.categories'],
function (Backbone, _, Marionette, t_Menus, c_Categories, cv_Categories) {
"use strict";
var Menu1LayoutView = Marionette.LayoutView.extend({
template: t_Menus['menu1'],
regions: {
menu : '#menu'
},
initialize: function(options){
this.city = options.city
},
onRender: function(){
},
onShow: function(){
this.menu.show(new cv_Categories({city:this.city}));
}
});
return Menu1LayoutView;
});
collectionview.categories.js
define([ 'backbone', 'underscore', 'marionette',
'layouts/MainMenu/compositeview.subcategories',
'collections/MainMenu/MM.collection.categories'],
function (Backbone, _, Marionette, cv_Subcategories, c_Categories) {
"use strict";
var CategoriesCollectionView = Marionette.CollectionView.extend({
initialize: function(options){
this.collection = new c_Categories([], {city: options.city});
},
getChildView: function(model){
return cv_Subcategories;
},
onRender: function(){
},
onShow: function(){
}
});
return CategoriesCollectionView;
});
This is where all the categorie's data are fetched from , it also re-fetches the data once the APP:change_city event is being triggered.
MM.collection.categories.js
define([ 'underscore', 'backbone', 'eMgr','models/MainMenu/MM.model.category'], function(_, Backbone, eMgr, m_Category){
var CategoriesCollection = Backbone.Collection.extend({
model: m_Category,
initialize: function(attr, opts) {
this.city = opts.city;
this.fetch();
eMgr.once("APP:change_city", this.OnCityChange, this)
},
url: function(){
return 'AF_GetCategories?city='+this.city;
},
OnCityChange: function(eData){
/// this is the part which is being called multiple times !!!!! ////
/// when checking eMgr's events , it shows that the events are stacking up ..
this.url= 'AF_GetCategories?city='+eData.city;
this.fetch();
}
});
return CategoriesCollection;
});
compositeview.subcategories.js
define([ 'backbone', 'underscore', 'marionette',
'templates/template.mainmenu',
'layouts/MainMenu/itemview.subcategory'],
function (Backbone, _, Marionette, t_MainMenu, iv_Subcategory) {
"use strict";
var SubcategoriesCompositeView = Marionette.CompositeView.extend({
template: t_Menus['subcategorieslayout'],
childViewContainer: "#category-wrapper",
getChildView: function(model){
return iv_Subcategory;
},
initialize: function(){
this.collection = this.model.get("subcategories");
},
onRender: function(){
},
onShow: function(){
this.$el.find("#loading").fadeOut(150);
}
});
return SubcategoriesCompositeView;
});
I have come to a conclusion that keeping the event listeners this.listenTo("custom_event", this.do_something) in either model or collection isn't a good idea. The events weren't cleaned up properly while switching between menu1 and menu2. It only worked when I manually called eMgr.stopListening() before loading any views.
So I tried moving all the event listeners from models/collections to their views and Voila!...it all worked! Events are no longer being triggered multiple times as before.
I am trying to get to grips with Backbone.js and have been following the 'modular' approach outlined by Thomas Davis here.
I seem to be stuck when trying to 'include' a view within another view as follows:
SettingsView.js
define([
'jquery',
'underscore',
'backbone',
'text!templates/settings/settingsTemplate.html'
], function($, _, Backbone, settingsTemplate){
var SettingsView = Backbone.View.extend({
el: $("#interface"),
render: function() {
this.$el.html(settingsTemplate);
}
});
return SettingsView;
});
ScriptView.js
define([
'jquery',
'underscore',
'backbone',
'views/settings/SettingsView',
'models/script/ScriptModel',
'collections/scripts/ScriptsCollection',
'text!templates/script/scriptTemplate.html'
],
function($, _, Backbone, SettingsView, ScriptModel, ScriptsCollection, scriptTemplate) {
var ScriptView = Backbone.View.extend({
el : $("#container"),
render : function() {
this.$el.html(scriptTemplate);
//add the settings view
var settingsView = new SettingsView();
settingsView.render();
}
});
return ScriptView;
});
UPDATED: I have managed to fix the error which I have just realised as to why that was happening (params were in the wrong order - DOH!!)
I no longer get the error but my 'SettingsView' is still not appearing. When I console log this inside 'ScriptView.js' I see that 'el' is undefined so my thinking is that this is where the issue may be...?
Thanks
I think you have to append the SettingsView to the el of the ScriptView:
var ScriptView = Backbone.View.extend({
el : $("#container"),
render : function() {
this.$el.html(scriptTemplate);
//add the settings view
var settingsView = new SettingsView();
settingsView.render();
this.$el.append(settingsView.el);
}
});
And a great post about subviews is this one.
I hope it helps!
I'm using Backbone and in my code I have a lot of text/template content that I'd like to push to an external file (templates\name-of-template.js?) and then load dynamically. Is there an easy way to do this? When I try to link to the file with <script type="text/template src='whatever'> it does not work.
Probably the easiest thing to do is load the file w/ Ajax.
var MyView = Backbone.View.extend({
initialize: function() {
this.getTemplate();
},
getTemplate: function() {
var self = this;
$.ajax({ url: "some/html/template.html" }).done(function( content ) {
self.template = _.template( content );
self.render();
});
},
render: function() {
this.$el.html( this.template({}) );
}
});
Also, if you were using something like require.js you can use the text! plugin...
define( [ "underscore",
"backbone",
"text!some/html/template.html" ],
function( _, Backbone, HtmlTemplate ) {
var MyView = Backbone.View.extend({
initialize: function() {
this.template = _.template( HtmlTemplate );
this.render();
},
render: function() {
this.$el.html( this.template({}) );
}
});
});
You can put all templates in a separate js file, something like:
App.Templates.Product = [
"<span><%= screen %></span>"
].join("");
Or start learning RequireJs. That way you can have a file for each model, collection, view, router and template.
Hope it helps
I'm using Require.js with Backbone.js and Underscore.js, and I have a nested view that is coming up as undefined when called as a dependency, but when I have the two views in the same module, they work fine. I'm wondering what I'm doing wrong. Here's an example:
child-view.js
define([
'jQuery',
'Underscore',
'Backbone',
], function ($, _, Backbone) {
var ChildView = Backbone.View.extend({
initialize: function () {
_.bindAll(this, 'render');
this.render();
},
});
return ChildView;
});
parentview.js
define([
'jQuery',
'Underscore',
'Backbone',
'src/views/child-view'
], function ($, _, Backbone, ChildView){
var ParentView = Backbone.View.extend({
initialize: function () {
_.bindAll(this, 'render');
this.render();
},
render: function () {
child = new ChildView({});
}
});
return ParentView;
});
I receive a "Uncaught TypeError: undefined is not a function" when trying to call the new ChildView. If I reference the ChildView outside of the Parentview but inside of parentview.js, it displays the view, but as an object.
Just from your code, there should be no problem, I tested your code actually did not find the problem.
This is my test code,you can try it:
http://files.cnblogs.com/justinw/test_byfejustin.zip
I think it might be your “require.js” have a problem,you can replace your "require.js" with my "test_byfejustin\js\libs\require\require.js" in my code package,and try again.
Variable names are case-sensitive. In child-view.js you are returning "ChildView" which is undefined (you've assigned childView).