Clear localStorage and change the view Backbone - javascript

Hey so I am using backbone localstorage and every time someone hits the search button I want to clear the localstorage so I can just add the new data to the localStorage.
Also, trying to figure out how to then redirect the user to a new view after the success callback in for the localstorage being set, I know there is view.remove() but I am not sure how to use that being that the callback is within the view and also, where/how to render the new view...
Let's say the new view is PageView...
Here is the code for the current search view:
define([
'jquery',
'underscore',
'backbone',
'models/search',
'text!templates/search.html',
], function($, _, Backbone, SearchM, SearchT){
var Search = Backbone.View.extend({
model: SearchM,
el: $("#Sirius"),
events: {
'submit #searchMusic': 'search'
},
search: function (e) {
e.preventDefault();
//create new instance of the model
searchM = new SearchM();
//post instance to the server with the following input fields
searchM.save({
channel: this.$('#channel').val(),
week: this.$('#week').val(),
year: this.$('#year').val(),
filter: this.$('#filter').val()
},{success: storeMusic});
// on success store music on client-side localStorage
function storeMusic (model, response, options) {
console.log('store');
//create new instance of the localStorage with the key name
searchM.localStorage = new Backbone.LocalStorage("music");
clearLocalStorage();
saveToLocalStorage(response);
};
function clearLocalStorage () {
console.log('clear');
//removes the items of the localStorage
this.localStorage.clear();
//pops out the first key in the records
searchM.localStorage.records.shift();
};
function saveToLocalStorage (response) {
console.log('save');
searchM.save({music: response}, {success: nextPage});
};
function nextPage () {
console.log('entered next page');
searchM.set('display', true);
};
},
render: function () {
}
});
return Search;
});
Container view:
define([
'jquery',
'underscore',
'backbone',
'views/search',
'text!templates/search.html'
], function($, _, Backbone, SearchV, SearchT){
var Container = Backbone.View.extend({
el: $("#Sirius"),
render: function () {
var search = new SearchV();
this.$el.html( SearchT );
this.listenTo(searchM, 'change:display', console.log('changed MODEL'));
}
});
return Container;
});
Here is the model:
define([
'underscore',
'backbone'
], function(_, Backbone) {
var Search = Backbone.Model.extend({
url: '/music',
defaults: {
display: false
}
});
return Search;
});
----------------EDIT Confused with below
This is the container and SearchM(model), SearchV(view), SearchT(template)...
var Container = Backbone.View.extend({
el: $("#Sirius"),
render: function () {
//Model CREATED
searchM = new SearchM();
//VIEW Created
var search = new SearchV();
this.$el.html( SearchT );
}
});
return Container;
});
This is the search View - so I took out the model from here, but calling this or this.model actually does not work, as searchM is not defined and the model does not seemed to be passed in... I only added the two methods so ignore the rest for now, if I can make these work then everything can follow suit
var Search = Backbone.View.extend({
el: $("#Sirius"),
events: {
'submit #searchMusic': 'search'
},
search: function (e) {
e.preventDefault();
//post instance to the server with the following input fields
searchM.save({
channel: this.$('#channel').val(),
week: this.$('#week').val(),
year: this.$('#year').val(),
filter: this.$('#filter').val()
},{success: storeMusic()});
function nextPage () {
console.log('entered next page');
searchM.set('display', true);
this.listenTo(searchM, 'change:display', console.log('changed MODEL'));
console.log(searchM.display);
};

Try this to get rid of the model:
searchM.destroy();
That's basically the same as in my answer here, but for a single model.
As for the view changing, i would recommend adding a 'display' or 'loaded' variable to the model, which is false by default and set to true, when the data is ready. Then, have the view listen to the 'change:display' event, triggering the render() method when ready. You can delete the old view, as soon as you know the data has changed and replace it with some loading spinner, which then will be replaced by the new data view.
Hope this helped.
Confused parts:
var Container = Backbone.View.extend({
el: $("#Sirius"),
render: function () {
//Model CREATED
searchM = new SearchM();
//VIEW Created
var search = new SearchV({model: searchM});
this.$el.html( SearchT );
}
});
var Search = Backbone.View.extend({
el: $("#Sirius"),
events: {
'submit #searchMusic': 'search'
},
initialize: function () {
this.listenTo(this.model, 'change:display', this.displayChanged);
},
displayChanged: function () {
console.log('display changed');
},
search: function (e) {
e.preventDefault();
//post instance to the server with the following input fields
searchM.save({
channel: this.$('#channel').val(),
week: this.$('#week').val(),
year: this.$('#year').val(),
filter: this.$('#filter').val()
},{success: storeMusic()});
},
nextPage: function () {
console.log('entered next page');
searchM.set('display', true);
console.log(searchM.display);
},

I haven't used Backbone.LocalStorage before, and the documentation doesn't specify how you should clear the data, however, in the source code there is a _clear() method that should do the trick:
function listStore (model, response, options) {
searchM.localStorage = new Backbone.LocalStorage("music");
searchM.localStorage._clear();
searchM.save({music: response}, {success: console.log('success')
});
As for switching to a new View, that is generally handled using a Backbone.Router which will handle redirecting your users to any area of your application you wish.
var MyRouter = Backbone.Router.extend({
routes: {
"search/:query": "search", // #search/kiwis
"page": "page" // #page
},
page: function() {
new PageView(); //etc...
},
search: function(query) {
...
}
});
//this line is required to tell Backbone that your routes are ready
Backbone.history.start();
Once you have the appropriate routes established, you can navigate to the desired location by calling:
function listStore (model, response, options) {
//check to see if the LS exists, and clear it if so
if(searchM.localStorage){
searchM.localStorage._clear();
}
searchM.localStorage = new Backbone.LocalStorage("music");
searchM.save({music: response}, {success: console.log('success');
searchM.on('sync', function(){
MyRouter.navigate("page", {trigger: true});
});
});

Related

Uncaught Type Error: View is not a constructor

I have Uncaught Type Error : UserRegisterView is not a constructor.I dont understand this error.I looked all code but i dont find it.
Sorry of my bad english.Please help me
Thanks for answer
UPDATED
UserRegisterView is here
var UserRegisterView = Backbone.View.extend({
model: User,
el: '#form',
events: {
'click input[id="infoWeek"]': 'infoWeek',
'click input[id="infoMonth"]': 'infoMonth'
},
infoWeek: function() {
this.$el.find("#dayOfMonth").hide();
this.render();
},
infoMonth: function() {
this.$el.find("#dayOfWeek").hide();
this.render();
}
});
var AddUserView = Backbone.View.extend({
el: $(".page"),
events: {
'click #saveUser': 'saveUser'
},
saveUser: function() {
var user = new User();
user.set({
username: $("#username").val(),
lastName: $("#lastName").val(),
regNumber: $("#regNumber").val(),
password: $("#password").val(),
departmentName: $("#departmentName").val(),
email: $("#email").val(),
role: $("#role").val()
});
user.save();
if (document.getElementById('isOpen').checked) {
user.set("isOpen", $("#isOpen").val("1"));
user.save();
} else {
user.set("isOpen", $("#isOpen").val("0"));
user.save();
}
if (document.getElementById('dayOfWeek').checked) {
user.set("dayOfWeek", $("#dayOfWeek").val());
user.save();
} else if (document.getElementById('dayOfMonth').checked) {
user.set("dayOfMonth", $("#dayOfMonth").val());
user.save();
}
$("#username").val("");
$("#firstName").val("");
$("#lastName").val("");
$("#regNumber").val("");
$("#password").val("");
$("#deparmentName").val("");
$("#email").val("");
$("#isOpen").val("");
$("#dayOfWeek").val("");
$("#dayOfMonth").val("");
},
render: function() {
var that = this;
var template = Handlebars.compile(UserRegister);
var myHtml = template(that.model.toJSON());
that.$el.html(myHtml);
return this;
}
});
return {
AddUserView: AddUserView,
UserRegisterView: UserRegisterView
};
});
router user func.
define([
'jquery',
'underscore',
'backbone',
'handlebars',
'spin',
'app/models/LoginModel',
'app/views/LoginView',
'app/views/UserRegisterView'
], function($,
_,
Backbone,
Handlebars,
Spinner,
Login,
LoginView,
UserRegisterView
) {
var Router = Backbone.Router.extend({
routes: {
'search': 'search',
'login': 'login',
'travels': 'travels',
'user': 'user',
'menu': 'menu',
'': 'home'
},
user: function() {
disposeView(new UserRegisterView().render());
}
dispose.view on util.js
function disposeView(view) {
Backbone.View.prototype.close = function() {
this.unbind();
this.undelegateEvents();
};
/* Şu anki viewi yok et */
if (this.currentView !== undefined) {
this.currentView.close();
}
/* Yeni view oluştur. */
this.currentView = view;
this.currentView.delegateEvents();
return this.currentView;
}
What's happening
Your UserRegisterView module returns an object which contains two constructors.
return {
AddUserView: AddUserView,
UserRegisterView: UserRegisterView
};
When using this module, what you're getting is the object above.
define([
// ...
'app/views/UserRegisterView'
], function(
// ...
UserRegisterView // value of the return in the module
) {
So you're kind of misleading yourself by calling it UserRegisterView as it's not the constructor, but the object containing the constructor.
To get a new UserRegisterView view instance with the current way your module is setup, you'd need to call it like so:
var userView = new UserRegisterView.UserRegisterView();
Or to create a AddUserView instance:
var addView = new UserRegisterView.AddUserView();
Solutions
Split up the module, one for each view constructor.
Change the name so at least it's not misleading (like UserViewsModule)
Other improvements
That being said, there are other improvements that could be made to your Backbone code.
var UserRegisterView = Backbone.View.extend({
// that's useless (if not used) and not a view property.
// model: User,
// don't use `el` like that, especially when using the view as a shared Constructor
el: '#form',
events: {
'click input[id="infoWeek"]': 'onInfoWeekClick',
'click input[id="infoMonth"]': 'onInfoMonthClick'
},
initialize: function() {
// Cache jQuery object of the view's element
this.$dayOfMonth = this.$("#dayOfMonth");
this.$dayOfMonth = this.$("#dayOfMonth");
// also use the shortcut function instead of `this.$el.find()`
}
onInfoWeekClick: function(e) {
this.$dayOfMonth.hide();
// calling render here is useless unless your using it as a parent
// view, where the child view overrides the render function.
},
onInfoMonthClick: function(e) {
this.$dayOfMonth.hide();
}
});
The disposeView function could be simplified:
function disposeView(view) {
var current = this.currentView;
if (current) current.close();
current = this.currentView = view;
current.delegateEvents();
return current;
}
Don't change the default Backbone view prototype each time the function is called. Instead, add the function once.
_.extend(Backbone.View.prototype, {
close: function() {
this.unbind();
this.undelegateEvents();
},
// any other function you want to add can go here.
});
In another answer, I go into details on how to extend Backbone's core classes with requirejs transparently.
You're already using jQuery, so don't use JavaScript DOM API document.getElementById('isOpen') interspersed with jQuery selectors $('#isOpen').
I made some improvements to the following view. Take the time to create yourself some utility functions (like reset and getValues) to simplify the flow of the code and encapsulate the complexity.
var AddUserView = Backbone.View.extend({
el: $(".page"),
events: {
'click #saveUser': 'saveUser'
},
// compile the template once while creating the view class
template: Handlebars.compile(UserRegister),
// get the selector string out of the code and place them in one place
// easy to change and maintain.
fields: {
username: "#username",
firstName: "#firstName",
lastName: "#lastName",
regNumber: "#regNumber",
password: "#password",
deparmentName: "#deparmentName",
email: "#email",
isOpen: "#isOpen",
dayOfWeek: "#dayOfWeek",
dayOfMonth: "#dayOfMonth",
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
// cache jQuery object of every field once after a render
this.field = _.reduce(this.fields, function(fields, selector, key) {
fields['$' + key] = this.$(selector);
return fields;
}, {}, this);
return this;
},
reset: function() {
// reset all the fields once without repeating code.
_.each(this.field, function($field) {
$field.val("");
});
return this;
},
getValues: function(keys) {
// get the value of multiple fields returned in a nice object
// ready to be sent to a Backbone model.
return _.reduce(keys, function(data, key) {
data[key] = this.field[key].val();
return data;
}, {}, this);
},
saveUser: function() {
var field = this.field,
user = new User(this.getValues([
'username',
'lastName',
'regNumber',
'password',
'departmentName',
'email',
'role',
]));
user.set({ isOpen: field.$isOpen.is(':checked') });
if (field.$dayOfWeek.is(':checked')) {
user.set("dayOfWeek", field.$dayOfWeek.val());
} else if (field.$dayOfMonth.is(':checked')) {
user.set("dayOfMonth", field.$dayOfMonth.val());
}
user.save();
this.reset();
},
});
In the following snippet, you're putting the context (this) into a local variable. I see that a lot and I could say that 90% of the times I see it on Stack Overflow questions, it makes no sense. It clearly screams copy-pasted.
render: function() {
var that = this;
// ...
that.$el.html(myHtml);
return this;
}
Please tell me you see that you're putting this into that, then using that throughout the function, then you still return this?!
Putting the context into a local variable is useful when the object is needed in a dynamically created callback.
render: function() {
var that = this; // this is available here
setTimeout(function() {
// here this is not available.
that.handleCallback();
}, 10);
// here we are in the same context as the first line.
return this;
}

Rendering the view returns undefined

I've got a collection view with two filter methods, and a render method which takes a parameter. The problem I'm stuck with is that when rendering the view for the first time it returns me an error. Here's my collection:
var ResumeCollection = Backbone.Collection.extend({
url: 'http://localhost:3000',
filterActive: function () {
var active = this.where({interviewed: false});
return new ResumeCollection(active);
},
filterInterviewed: function () {
var interviewed = this.where({interviewed: true});
return new ResumeCollection(interviewed);
}
});
And my view:
var ResumeList = Backbone.View.extend({
events { // hash array of filter events },
initialize: function () {
this.collection.fetch();
},
render: function (filtered) {
var self = this;
var data;
if (!filtered) {
data = this.collection.toArray();
} else {
data = filtered.toArray();
}
_.each(data, function (cv) {
self.$el.append((new ResumeView({model: cv})).render().$el);
});
return this;
},
showActive: function (ev) {
var filtered = this.collection.filterActive();
this.render(filtered);
},
showInterviewed: function (ev) {
var filtered = this.collection.filterInterviewed();
this.render(filtered);
},
showAll: function (ev) {
this.render(this.collection);
}
});
This view gets rendered for the first time in my router by passing a collection:
var AppRouter = Backbone.Router.extend({
routes: {
'': 'home'
},
initialize: function () {
this.layout = new LayoutView();
}
home: function () {
this.layout.render(new ResumeList({
collection: new ResumeCollection()
}));
}
});
And this is the layout view within which all the other views are rendered:
var LayoutView = Backbone.View.extend({
el: $('#outlet'),
render: function (view) {
if (this.child && this.child !== view) {
this.child.undelegateEvents();
}
this.child = view;
this.child.setElement(this.$el).render();
return this;
}
});
When I just refresh my page, I get filtered.toArray is not a function error and nothing is rendered respectively. After inspecting everything in the debugger, I found out that when the view gets rendered for the first time, the filtered attribute receives an empty collection, assigns it to data variable, which becomes an empty array and goes to the body of render function, becoming undefined after that. The mysteries go here: whenever I click items, that are bound to my show* events, they act exactly as expected and render either models where interviewed === false, or true or the whole collection. This looks kinda magic to me and I haven't got the faintest idea what can I do with that.
ADDED: GitHub repo with this project
Your home function on the AppRouter has a typo. You have an extra semi-colon.
home: function () {
this.layout.render(new ResumeList({
collection: new ResumeCollection();
}));
}
Should be
home: function () {
this.layout.render(new ResumeList({
collection: new ResumeCollection()
}));
}
I needed to remove it to get the JSFiddle working: https://jsfiddle.net/4gyne5ev/1/
I'd recommend adding some kind of linting tool into your IDE or Build process (http://eslint.org/)
You need to add home url content to your db.json file like this
"" : [
{
'somthing': 'somthing'
}
]
After a piece of advice from my mentor I realized that the core of the problem was in asynchronous origin of fetch method -- as I passed this.collection.fetch in my initialize function, it executed after my render method, not before it, so my render method had just nothing to render when the view was called for the first time. So, this fix worked:
var ResumeList = Backbone.View.extend({
initialize: function (options) {
this.collection = options.collection();
// removed .fetch() method from here
},
render: function (filtered) {
var self = this;
var data;
// and added it here:
this.collection.fetch({
success: function (collection) {
if (!filtered) {
data = collection.toArray();
} else {
data = filtered.toArray();
}
self.$el.html(self.template(collection.toJSON()));
_.each(data, function (cv) {
self.$el.append((new ResumeView({model: cv})).render().$el);
})
}
});
}
});
And this worked perfectly and exactly as I needed.

Backbone collection fetch render runs twice with router on first load

I am fairly new to Backbone and am trying to get my head around routers and calling a collection from a database.
I have the following
Collection:
var Scorecards = Backbone.Collection.extend({
model:Scorecard,
url:"http://localhost:3002/api/scorecards",
initialize:function(){
this.fetch({
success: this.fetchSuccess,
error: this.fetchError
});
},
fetchSuccess: function (collection, response) {
console.log("results");
if(collection.length>0) {
var view = new ScorecardsView({el:'#scorecards-container', model:scorecards});
view.render();
}
else{
var view = new NoScorecardsView({el:'#scorecards-container'});
view.render();
}
},
fetchError: function(collection, response) {
throw new Error("Failed to get scorecards");
}
});
Router:
var ScorecardRouter = Backbone.Router.extend ({
routes: {
'' : 'home',
'create': 'createScorecard',
'edit': 'editScorecard'
},
home: function () {
console.log("Home view");
var view = new ScorecardsView({el:'#scorecards-container', model:scorecards});
view.render();
},
createScorecard: function () {
console.log('Create view');
var view = new CreateScorecardView({el:'#scorecards-container'});
view.render();
}
});
Scorecards view:
var ScorecardsView = Backbone.View.extend({
initialize: function(){
this.model.on('destroy', this.render, this);
},
render: function() {
console.log("Scorecard render");
var self = this;
this.$el.html(ScorecardContTemp);
this.model.each(function(scorecard){
var scorecardView = new ScorecardView({model:scorecard});
self.$('.scorecards-items tbody').append(scorecardView.render().$el);
});
},
events: {
"click #scorecard-create-btn" : "createScorecardView",
},
createScorecardView: function(e) {
e.preventDefault();
scorecardRouter.navigate('create', {trigger: true});
}
});
and I start things off with this
var scorecards = new Scorecards;
var scorecardRouter = new ScorecardRouter();
Backbone.history.start();
My problem is, when I first hit the home route, I'm getting the view render function running twice. Because firstly the fetch is calling it and also the route is calling it.
I need to remove the call from either the fetch success or the route, but when I do I get no results on initial load and I have to navigate to a different route and back to.
How are you supposed to achieve this? So I can fetch the results once and then display them via the route the fetch is successful but also show them in the route when a user navigates to it.
I hope that makes sense?
Any help would be great.
First of all, your data shouldn't know how it is rendered, so new View() anywhere within a Model or a Collection is a sure sign of a problem. Your views should watch their data and update themselves.
Your other possible source of confusion is passing {trigger: true} to your router navigate method. What kind of trouble that brings is elaborately explained in this classic Backbone article: Don’t Execute A Backbone.js Route Handler From Your Code.
For now, you definitely should remove the view rendering from the collection. Instead, your view should be aware of the collection and update itself when the data changes.
Here's an example of how I would setup my view to watch the collection:
/** Scorecard model */
var Scorecard = Backbone.Model.extend({
defaults: {
name: '',
email: ''
}
});
/** Scorecard View (I know it totally doesn't look like a scorecard, just an example view) */
var ScorecardView = Backbone.View.extend({
template: _.template('<%=name%>, <em><%=email%></em>'),
render: function(){
this.$el.html( this.template( this.model.toJSON() ) );
return this;
}
});
/** Collection */
var Scorecards = Backbone.Collection.extend({
model: Scorecard,
/** using fake api for the sake of this example to work */
url: "http://jsonplaceholder.typicode.com/users",
initialize:function(){
this.fetch({
success: this.fetchSuccess,
error: this.fetchError
});
},
fetchSuccess: function (collection, response) {
console.log("results:", collection);
},
fetchError: function(collection, response) {
throw new Error("Failed to get scorecards");
}
});
/** Scorecards view: */
var ScorecardsView = Backbone.View.extend({
initialize: function(){
this.collection.on('destroy', this.render, this);
/** render one added item whenever it comes to collection */
this.collection.on('add', this.addOne, this);
},
render: function() {
console.log("Scorecard render");
/** clean the items container,
which will be useful when items get destroyed
and we'll want to re-render whole collection */
this.$el.find('.scorecards-items').empty();
/** in case collection already has data, let's render it */
this.collection.each(this.addOne, this);
},
addOne: function(scorecard){
var scorecardView = new ScorecardView({ model: scorecard });
this.$('.scorecards-items').append(scorecardView.render().$el);
}
});
/** Router: */
var ScorecardRouter = Backbone.Router.extend ({
routes: {
'' : 'home'
},
home: function () {
console.log("Home view");
var view = new ScorecardsView({
el:'#scorecards-container',
collection: scorecards
});
view.render();
}
});
/** starting things off */
var scorecards = new Scorecards();
var scorecardRouter = new ScorecardRouter();
Backbone.history.start();
<script src='http://code.jquery.com/jquery.js'></script>
<script src='http://underscorejs.org/underscore.js'></script>
<script src='http://backbonejs.org/backbone.js'></script>
<div id="scorecards-container">
<div class="scorecards-items"></div>
</div>

Backbone.js and router.navigate

I'm trying to improve the navigation of my little backbone application. Right now I just have some simple navigation using html links that use to #path/to/page in the href element.
What I'm running into is when I click on one of these and then click the back button, the page doesn't refresh properly, and the HTML content doesn't change. So I'm trying to incorporate the navigate functionality into my code.
The issue I'm running into is that I can't find an example that matches the code layout I'm currently using, and I don't understand how backbone works enough to adapt the things I find into something useful.
Here's what I've got:
app.js - called from the index.html file
require.config({
baseUrl: 'js/lib',
paths: {
app: '../app',
tpl: '../tpl',
bootstrap: 'bootstrap/js/',
},
shim: {
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
'underscore': {
exports: '_'
}
}
});
require([
'jquery',
'backbone',
'app/router',
], function ($, Backbone, Router) {
var router = new Router();
Backbone.history.start();
});
app/router.js - instantiated in app.js
define(function (require) {
"use strict";
var $ = require('jquery'),
Backbone = require('backbone'),
WindowView = require('app/views/Window'),
breadcrumbs = {"Home": ""},
$body = "",
$content = "",
windowView = "";
return Backbone.Router.extend({
initialize: function () {
require([], function () {
$body = $('body');
windowView = new WindowView({el: $body}).render();
$content = $("#content", windowView.el);
});
},
routes: {
'' : 'home',
'profile/login(/)' : 'candidateProfileLogin',
'profile/manage(/)' : 'candidateProfileLogin',
'profile/manage/:id(/)' : 'candidateProfileHome',
'profile/manage/:id/questionnaire/:page(/)' : 'candidateProfileQuestionnaire',
'profile/manage/:id/:section(/)' : 'candidateProfileSection',
},
home: function (){
},
candidateProfileLogin: function () {
require(['app/views/CandidateLogin'], function (CandidateLoginView) {
console.log(Backbone.history.fragment);
var view = new CandidateLoginView({el: $content});
view.render();
});
},
candidateProfileHome: function (id) {
require(["app/views/Candidate", "app/models/candidate"], function (CandidateView, models) {
var candidate = new models.Candidate({id: id});
candidate.fetch({
success: function (data) {
var view = new CandidateView({model: data, el: $content});
view.render();
},
error: function (data) {
var view = new CandidateView({model: data, el: $content});
view.render();
}
});
});
},
candidateProfileSection: function (id, section) {
require(["app/views/Candidate", "app/models/candidate"], function (CandidateView, models) {
var candidate = new models.Candidate({id: id});
candidate.fetch({
success: function (data) {
var view = new CandidateView({model: data, el: $content});
view.render(section);
},
error: function (data) {
//Output the data to the console. Let the template take care of the error pages
console.log(data);
var view = new CandidateView({model: data, el: $content});
view.render();
}
});
});
},
candidateProfileQuestionnaire: function (id, page) {
require(["app/views/Candidate", "app/models/candidate"], function (CandidateView, models) {
var candidate = new models.Candidate({id: id});
candidate.fetch({
success: function (data) {
var view = new CandidateView({model: data, el: $content});
view.render(page);
},
error: function (data) {
//Output the data to the console. Let the template take care of the error pages
console.log(data);
var view = new CandidateView({model: data, el: $content});
view.render();
}
});
});
},
});
});
app/views/Candidate.js - My view I'm trying to process the clicks
define(function (require) {
"use strict";
var $ = require('jquery'),
_ = require('underscore'),
Backbone = require('backbone'),
tpl = require('text!tpl/Candidate.html'),
template = _.template(tpl),
CandidateErrorView = require('app/views/CandidateError'),
errtpl = require('text!tpl/CandidateError.html'),
errTemplate = _.template(errtpl);
return Backbone.View.extend({
events: {
'submit #voters-guide-personalInfo': 'savePersonalInfo',
'submit #voters-guide-essay' : 'saveEssay',
'submit #voters-guide-survey' : 'saveSurvey',
'submit #voters-guide-endorsements': 'saveEndorsements',
'submit #voters-guide-photo' : 'savePhoto',
'click #table-of-contents a' : 'navTOC',
},
savePersonalInfo: function (event) {
console.log(event);
},
saveEssay: function (event) {
console.log(event);
},
saveSurvey: function (event) {
console.log(event);
},
saveEndorsements: function (event) {
console.log(event);
},
savePhoto: function(event) {
console.log(event);
},
navTOC: function (event) {
console.log(event.target);
var id = $(event.target).data('candidate-id');
var path = $(event.target).data('path');
//router.navigate("profile/manage/" + id + "/" + path, {trigger: true});
},
render: function (page) {
//Check to see if we have any errors
if (!this.model.get('error')) {
var dataToSend = {candidate: this.model.attributes};
switch(page) {
case 'personalInfo':
template = _.template(require('text!tpl/Candidate-personalInfo.html'));
break;
case 'essay':
template = _.template(require('text!tpl/Candidate-essay.html'));
break;
case 'survey':
template = _.template(require('text!tpl/Candidate-survey.html'));
break;
case 'endorsements':
template = _.template(require('text!tpl/Candidate-endorsements.html'));
break;
case 'photo':
template = _.template(require('text!tpl/Candidate-photo.html'));
break;
default:
break;
}
this.$el.html(template(dataToSend));
return this;
} else {
this.$el.html(errTemplate({candidate: this.model.attributes}));
return this;
}
}
});
});
Now, in an attempt to stop the 'the page content doesn't reload when I hit the back button' issue, I've been looking into the navigate function that backbone has available (this: router.navigate(fragment, [options]);). There are lots of examples of how this is used, but none of them seem to have anything similar to the file setup that I'm using, so I'm not exactly sure how best to access this functionality from my view. If I include the router file in the view and instantiate a new version of it, the page breaks b/c it tries to run the initialize function again.
I'm just really at a loss on how this is supposed to work.
Can someone point me in the right direction?
Thanks!
--Lisa
P.S. If someone has any better ideas, I am all ears!
You should have access to the Backbone object, which within it, has access to navigate around using the history.navigate function. If you call that passing in trigger: true you'll invoke the route. For instance:
Backbone.history.navigate("profile/manage", { trigger: true });

Rendering and delete multiple views…

I hope i am clear enough, otherwise ask me for clarifications.
I would like to delete end create view in agreement to a main template…
I did try the following implementation, unsuccessfully.
// main-template.html
<div id=""project>
<div class="main-template">
<div id="view1"></div>
<div class="foldable-block">
<div id="view2"></div>
<div id="view3"></div>
</div>
</div>
</div>
//mainView.js
define([
"js/views/view1",
"js/views/view2",
"js/views/view3",
"text!templates/main-template.html"
], function (View1, View2, View3, mainTemaplte) {
var MainView = Backbone.View.extend({
initialize: function ()
{
this.$el.html(Mustache.render(mainTemaplte));
this.render();
},
el: $("#project"),
render: function ()
{
var options = this.options;
this.view1 = new View1(options);
this.view2 = new View2(options);
this.view3 = new View3(options);
}
});
return MainView;
});
//view1.js
define([… ], function (..) {
var View1 = Backbone.View.extend({
el: $("#view1"),
initialize: function () {
console.log(this.$el) // []
setTimeout( function () {
console.log(this.$el) // []
}, 2000);
}
});
return View1;
});
The issues as you can see from the comments is in view1.js
(this.$el) // []
from my js console it works:
$("#view1") // <div>….</div>
My goals is:
1) when I load the mainView.js module I would like to create a template to which attach my views (view1, view2, view3)
2) when I trigger delete view, every DOM, to which are attached the view, should be deleted.
3) when I call again the mainView.js module the template should be recreated.
if you have other ideas to suggest, please post.
Thanks to #nikoshr advise this.$el, in view1.j, is defined and when I call render in view1.js the this.$el is fill properly
but it is not attached to the body of document.
How can I make it without using append or similar jquery methods to my main-template.html ?
Here my render function:
render: function ()
{
this.$el.html(Mustache.render(myTemplate, this.view);
}
You are attaching your subviews to elements that do not exist at the time you require them. Something like this may be a step in the right direction:
mainView.js
define([
"js/views/view1",
"js/views/view2",
"js/views/view3",
"text!templates/main-template.html"
], function (View1, View2, View3, mainTemaplte) {
var MainView = Backbone.View.extend({
initialize: function ()
{
this.render();
},
render: function ()
{
// render from template and assign this.el to the root of the element
// e.g #project
var html=Mustache.render(mainTemaplte);
this.setElement( $(html) );
// create each view with its el set to the correct descendant
this.view1 = new View1( _.extend( {el:this.$("#view1")} , this.options) );
this.view2 = new View2( _.extend( {el:this.$("#view2")} , this.options) );
this.view3 = new View3( _.extend( {el:this.$("#view3")} , this.options) );
}
});
return MainView;
});
view1.js
define([… ], function (..) {
var View1 = Backbone.View.extend({
initialize: function () {
console.log(this.$el);
}
});
return View1;
});
And you can recreate your view with something like
require(["js/views/mainView"], function(MainView) {
var view=new MainView();
console.log(view.$el);
//attach the view to the DOM
$("body").append(view.$el);
});

Categories

Resources