Unable to reference Backbone model after model.set in Backbone View - backbone.js

I'm trying to get the view to self render by binding a change event to the views model. I've created a custom function on PageView which takes a model from the Pages collection as a paramater and uses this.model.set(data) to trigger the model change event. This is triggered when I pass it a Page model from the Pages collection in the AppView via this.page.load(Pages.at(index)). I'm running into a few problems though.
The change event is only fired once when switching back and forth between 2 different models, which I can resolve by running this.model.clear({silent:true}), but that's not ideal.
this.model is always undefined in any function other than the load() function within PageView which is really the main issue because I obviously can't fire this.render if the model is undefined. Hence the test:function().
Anyway here's the code for my AppView and PageView functions. Thanks in advance for any help.
define([
// These are path alias that we configured in our bootstrap
'jquery',
'underscore',
'backbone',
'handlebars',
'views/pageView',
'views/menuView',
'collections/pages'
], function($, _, Backbone, Handlebars,PageView,MenuView,Pages){
var AppView = Backbone.View.extend({
//Stores a reference to our main menu
mainMenu:{},
//Stores reference to current page
page:{},
//The parent div
el:'#app',
//Events that are being listened to on #app
events:{"click" : "active"},
//Process to run when this view is initialized
initialize:function(){
//Load our Pages Collection
Pages.reset(this.model.pages);
//Load the main menu
this.mainMenu = new MenuView;
this.mainMenu.model = this.model.main_menu;
this.mainMenu.render();
//Loads the page view
this.page = new PageView({model:Pages.at(0)});
},
//Currently just renders a page view with a designated model from the Pages collection
render:function(index){
this.page.load(Pages.at(index));
},
active:function(event){
$('.menu a').removeClass('active');
$('.menu a[href="' + event.target.hash + '"]').addClass('active');
}
});
return AppView;
// What we return here will be used by other modules
});
define([
// These are path alias that we configured in our bootstrap
'jquery',
'underscore',
'backbone',
'handlebars',
'text!templates/page.html',
], function($, _, Backbone, Handlebars, pageViewTemplate){
var PageView = Backbone.View.extend({
//Define the default template
template: Handlebars.compile(pageViewTemplate),
//The parent div for this element
el:'#content',
initialize:function(){
this.model.bind('change',this.test);
},
load:function(data){
this.model.set(data);
},
//Render function
render:function(){
this.$el.html(this.template(this.model.toJSON()));
return this;
},
test:function(){
console.log(this.model);
console.log('change');
}
});
return PageView;
// What we return here will be used by other modules
});

does setting the context in your bind call fix things for you?
initialize:function(){
this.model.bind('change',this.test, this);
},

Related

backbone epoxy.js with underscore text template

Does anyone have pointers for using Backbone.Epoxy ( epoxy.js ) with HTML passed in via template.
aka given a View such as
define([
'jquery',
'underscore',
'backbone',
'epoxy',
'text!views/MyTemplate.html'
], function($, _, Backbone, Epoxy, Template ){
var MyView = Epoxy.View.extend({
template: _.template(Template),
bindings: {
".brand-name": "text:name",
".brand-name": "text:count",
},
// Perhaps render not needed given that epoxy bindings
render : function() {
var data = {
item: this.model,
_: _
};
this.$el.html( this.template(data) );
return this;
}
return MyView;
});
which renders the template but without any bindings.
and does one need a render method anymore..!
No you don't need the render method, just instantiate you view.
var view = new BindingView({model: bindModel});
Here is an example
But when you use the render method and replace the view.el you lost those bindings, in that case try to do :
this.$el.html( this.template(data) );
this.applyBindings();

Issue related to rendering multiple views through a single view

Question Updated
I am implementing a search and result for the same in the same page. i
have two seperate html views and views for search and result.
Here is what is happening..
From my router the below view is loaded. This view will load two other views called search and result. I have included the respective html views in the seperate views. but when i run the proj, it displays nothing.
Am i rendering the view in this file wrong ??
if i am rendering it wrong can somebody please tell me where is the mistake and how to rectify it ??
should i include the respective html templates of search and result also in this file ??
views.driver.search.js :
define(
[ 'jquery',
'underscore',
'backbone',
'app/models/model.driver',
'app/collections/collection.driver',
'app/models/model.driver.searchform',
'app/dashboard/views.driver.searchForm',
'app/models/model.driver.searchresult',
'app/dashboard/views.driver.searchResults',
],
function ($, _, Backbone, DriverModel,SearchResultCollection ,searchFormModel, searchFormView, SearchResultModel,SearchResultView) {
var DriverSearchView = Backbone.View.extend({
el: $('#content'),
initialize: function(){
this.render();
},
render: function () {
// var compiledTemplate = _.template(searchFormTemplate, {});
// this.$el.html(compiledTemplate);
this.searchFormInitModel = new searchFormModel();
var searchformView = new searchFormView({model:this.searchFormInitModel});
this.$("#content").html(searchformView.render());
var driver1 = new DriverModel({id:1,firstName:"driver1",lastName:'last',dob:'12/12/12001',});
var driver2 = new DriverModel({id:2,firstName:"driver2",lastName:'last',dob:'12/12/12001',});
var driver3 = new DriverModel({id:3,firstName:"driver3",lastName:'last',dob:'12/12/12001',});
var driver4 = new DriverModel({id:4,firstName:"driver3",lastName:'last',dob:'12/12/12001',});
this.searchResultCollection= new SearchResultCollection([driver1, driver2, driver3, driver4]);
var searchResultView = new SearchResultView({el:this.$el.find("#searchResult"),collection:this.searchResultCollection});
this.$("#content").html(searchResultView.render());
//this.$el.find("#searchResult").html(searchResultView.render());
}
}); // Our module now returns our view
return DriverSearchView;
});
I don't see an issue with your workflow. Reason you are not seeing your console.logs, is because you are returning an object literal containing your initialize function without actually calling the function. Is there a reason you need to return an object from your initialize module?
Anyhow, I am not entirely sure how you calling your app.js file, but try the following if you do not need to return an object back from your initialize module
define(
[
'jquery',
'underscore',
'backbone',
'app/router',
// 'datatable'
],
function ($, _, backbone, router){ //,datatable) {
var initialize = (function () {
console.log("App1");
debugger;
console.log("App2");
router.initialize();
console.log("App3");
})();
// Depending what you want to do here, you could just
// console.log("Router initialized...");
// router.initialize();
});

Why won't my model get passed?

I have this collection view
define([
'jquery',
'underscore',
'backbone',
'views/project',
'collections/project-collection',
'templates'
], function ($, _, Backbone, ProjectView, ProjectCollection, JST) {
'use strict';
var ProjectListView = Backbone.View.extend({
template: JST['app/scripts/templates/projectList.ejs'],
el: $('#content'),
render: function() {
var projectCollection = new ProjectCollection();
projectCollection.fetch();
projectCollection.each(this.addOne(),this);
return this;
},
addOne: function(project) {
console.log('addOne function');
var projectView = new ProjectView({model: project});
this.$el.html( projectView.render().el);
}
});
return ProjectListView;
});
No matter what I try the model never gets passed through to the addOne function so the in the view that is instantiated by this method the call to
this.model.toJSON()
results in the old 'cannot call method .toJSON of undefined' error. I tried to inject the collection when this collection view was instantiated and that didn't work either. Obviously here it is in the dependency array and that doesn't work either. The model is definitely there as I can log projectCollection.model to the console inside the render function. I'm stumped.
I see two problems with your render: one you know about and one you don't.
The first problem is right here:
projectCollection.each(this.addOne(), this);
The parentheses on this.addOne() call the addOne method right there rather than passing the this.addOne function to each as a callback. You want this:
projectCollection.each(this.addOne, this);
The second problem is that you have to wait for the collection's fetch to return before anything will be in the collection. You can use the fetch's callbacks:
var _this = this;
projectCollection.fetch({
success: function() {
projectCollection.each(_this.addOne, _this);
}
});
or you can use the various events that fetch will fire, see the fetch documentation for details.

require.js / js file loads before DOM loaded

I am new in MVC framework like backbone.js. I am trying to make an application using backbone.js, require.js and underscore.js. I also added jQuery UI. There is a js file named widget.js which actually adjusts widgets like fixing position, firing some events when window re-sizes, firing ui events like button(), tab() etc.
Now the problem is when I open a page using a url directly (like http:://localhost/web/#/login), everything works well. But when I open that page from another page using navigation menu, there occurs a mass. The page looks ugly. I think this is because when I load the page using a url than widget.js loads after DOM is loaded but when I open the page using navigation, then widget.js loads before DOM is loaded. Wasted 3 days to find a solution .But no luck. Here is the code..
define([
'jQuery',
'Underscore',
'Backbone',
'text!templates/loginForm.html',
'text!templates/home.html',
'libs/custom/widget',
], function($, _, Backbone, loginForm, homeTemplate, widget){
var loginView = Backbone.View.extend({
el: $("#page"),
status: 'Failed',
serverMsg:'Mail doesn\'t match',
events:{
"click .login":"login"
},
isLogedIn:function(){
if(this.status=='Failed' || this.status==''){
console.log("error");
}else{
console.log("succss");
}
},
login:function(e){
e.preventDefault();
var info = $(".loginForm").serialize();
var url = 'http://rest.homeshop.dev/login/';
var This = this;
$.ajax({
url:url,
type:"POST",
data:info,
success:function(data){
data = $.parseJSON(data);
This.serverMsg = data.msg;
This.status = data.status;s
This.isLogedIn();
}
});
return false;
},
initialize: function(){
},
render: function(){
var data = {};
var compiledTemplate = _.template( loginForm, data);
this.el.find('.carrier').html(compiledTemplate);
}
});
return new loginView;
});
Also I did not added jquery Ui library here as I have added that library in application.js .Here is the codes for application.js
define([
'jQuery',
'Underscore',
'Backbone',
'router',
'libs/custom/jqueryUi-min',
'libs/custom/widget',
'libs/custom/layoutSwitcher'
], function($, _, Backbone, Router){
var initialize = function(){
Router.initialize();
}
return {
initialize: initialize
};
});
Can any one help me ?
I think what you want is the domReady! plugin. http://requirejs.org/docs/api.html#pageload
define(['jQuery', 'Backbone', 'Router', 'libs/custom/widget', 'domReady!']
, function($, Backbone, Router, widget, dom) {
//execute your code knowing that the DOM has loaded.
}
)
Hope this helps!

When is the right time to fetch a collection in Backbone js?

So I'm working on a backbone app, and trying to modularize things as much as I can using require.js. This has been my guide.
I'm having trouble getting my view to always fetch my collection. If I access my app from the base url (myapp.com/), and go to the route of my view, the collection is fetched. If I do not go to the view, and instead access it from myapp.com/#/campaigns, then the collection is not fetched.
Here is some relevant code.
router.js
define([
'jQuery',
'Underscore',
'Backbone',
'views/home/main',
'views/campaigns/list'
], function($, _, Backbone, mainHomeView, campaignListView ){
var AppRouter = Backbone.Router.extend({
routes: {
// Define some URL routes
'campaigns': 'showCampaigns',
// Default
'*actions': 'defaultAction'
},
showCampaigns: function(){
campaignListView.render();
},
defaultAction: function(actions){
// We have no matching route, lets display the home page
//mainHomeView.render();
}
});
var initialize = function(){
var app_router = new AppRouter;
Backbone.history.start();
};
return {
initialize: initialize
};
});
collections/campaigns.js
define([
'jQuery',
'Underscore',
'Backbone',
'models/campaigns'
], function($, _, Backbone, campaignsModel){
var campaignsCollection = Backbone.Collection.extend({
model: campaignsModel,
url: '/campaigns',
initialize: function(){
}
});
return new campaignsCollection;
});
views/campaigns/list.js
define([
'jQuery',
'Underscore',
'Backbone',
'collections/campaigns'
], function($, _, Backbone, campaignsCollection){
var campaignListView = Backbone.View.extend({
el:$('#container'),
initialize:function(){
this.collection = campaignsCollection;
this.collection.fetch();
},
render: function(){
var data = {
campaigns: this.collection,
_: _
};
$('#container').html('Campaigns length: '+data.campaigns.models.length);
}
});
return new campaignListView;
});
Any ideas on what I'm doing wrong? I believe it has something to do with calling this.collection.fetch() in the initalize function of the view. If that is the issue, where should I put fetch()?
The problem in your code is that your campaignListView fetch the collection when it is initialized and it is initialized only once. Your render method which is actually called from the router doesn't call your initialize method of campaignListView, when you change theurl your second time.
You have two options here :
1. return the class not the instance of your campaignListView and initialize it in the router :
// in your router code
showCampaigns: function(){
var campaignListViewInstance = new campaignListView(); // don't forget the brackets after your initialize function
campaignListViewInstance.render();
},
// in your campaignListView code :
return campaignListView; // without "new"
This will initialize the code everytime the router is hit.
2. place your fetch in the render method
// in your campaignListView code :
initialize:function(){
this.collection = campaignsCollection;
},
render: function(){
this.collection.fetch({success: function(collection) {
var data = {
campaigns: collection,
_: _
};
$('#container').html('Campaigns length: '+data.campaigns.models.length);
}); // this will be fetched everytime you render and also it has success callback
}
Also be aware that you should replace all your instance creating functions with brackets at the end
return new campaignListView; --> return new campaignListView();
return new campaignsCollection; --> return new campaignsCollection();
Another problem you will face is the async work of fetch. You should use success or event driven rendering of your collection, because fetch works in the background and you will have no data when you immediately call render after calling fetch.
+1 for your AMD approach.
I should really update the tutorial but it's better to not return instantiated modules. Maybe try checking out http://backboneboilerplate.com

Resources