Backbone.js back button not loading proper view - javascript

In my Backbone application, I'm trying to figure out why when I click the back button on a page, the URL changes appropriately but the actual browser display does not.
Here's one flow:
Go to application: elections/
Starting page: elections/#profile/manage/:id/
Page clicked to: elections/#profile/manage/:id/personalInfo/
Back button clicked. Should end up displaying elections/#profile/manage/:id/
However when the back button is clicked, the page display doesn't change, just the URL.
I'm not sure what's causing this nor how to get around this. I've read some things about options to the Backbone.history.start() command, but whenever I add anything to it, nothing displays.
Not really sure how to go about fixing this problem. Can someone point me in the right direction? (I can expand the code samples if need be, I just thought this might be easier to read)
elections/app.js - called from elections/index.html
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({pushState:true});
});
elections/js/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 = "",
return Backbone.Router.extend({
routes: {
'' : 'home',
'profile/login(/)' : 'candidateProfileLogin',
'profile/manage(/)' : 'candidateProfileLogin',
'profile/manage/:id(/)' : 'candidateProfileHome',
'profile/manage/:id/:section(/)' : 'candidateProfileSection',
},
initialize: function () {
require([], function () {
$body = $('body');
windowView = new WindowView ({el: $body}).render();
$content = $("#content", windowView .el);
});
},
candidateProfileHome: function (id) {
require(["app/views/Candidate", "app/models/candidate"], function (CandidateView, models) {
var candidate = new models.Candidate({id: id});
console.log('router: candidateProfileHome');
candidate.fetch({
success: function (data, response) {
var view = new CandidateView({model: data, el: $content});
view.render();
},
error: function (data, response) {
var view = new CandidateView({model: data, el: $content});
view.render();
}
});
breadcrumbs['Profile Home'] = Backbone.history.fragment;
windowView.addBreadcrumbs(breadcrumbs);
windowView.selectMenuItem('candidate-menu');
});
},
candidateProfileSection: function (id, section) {
require(["app/views/Candidate", "app/models/candidate"], function (CandidateView, models) {
var sectionNames = {
'questionnaire': 'Questionnaire',
'endorsements' : 'Endorsements',
'photo' : 'Photo',
};
var sectionName = sectionNames[section];
var candidate = new models.Candidate({id: id});
candidate.fetch({
success: function (data, response) {
var view = new CandidateView({model: data, el: $content});
view.render(section);
},
error: function (data, response) {
//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(section);
}
});
breadcrumbs['Profile Home'] = "profile/manage/" + id + "/";
breadcrumbs[sectionName] = Backbone.history.fragment;
windowView.addBreadcrumbs(breadcrumbs);
windowView.selectMenuItem('candidate-menu');
});
},
candidateProfileQuestionnaire: function (id, page) {
require(["app/views/Candidate", "app/models/candidate"], function (CandidateView, models) {
var pageNames = {
'personalInfo': 'Personal Information',
'essay' : 'Biography & Essay',
'survey' : 'Survey Questions',
'endorsements': 'Endorsements',
'photo' : 'Photo'
};
var pageName = "Questionnaire: " + pageNames[page];
var candidate = new models.Candidate({password: id});
candidate.fetch({
success: function (data, response) {
console.log('success');
var view = new CandidateView({model: data, el: $content});
view.render(page);
},
error: function (data, response) {
//Output the data to the console. Let the template take care of the error pages
console.log('error');
console.log(data);
var view = new CandidateView({model: data, el: $content});
view.render(page);
}
});
breadcrumbs['Profile Home'] = "profile/manage/" + id + "/";
breadcrumbs[pageName] = Backbone.history.fragment;
windowView.addBreadcrumbs(breadcrumbs);
windowView.selectMenuItem('candidate-menu');
});
},
});
});

You shouldn't need to specifically configure Backbone's router to handle browsers history state or events, it's built in. The issue lies somewhere else...in your case it was the view, rendering the template.

we have two solutions
Backbone.Router.navigate("url", {trigger: true});
or
Backbone.history.back();

Related

Backbone.Paginator infinite mode, with Marionette

In my Marionette app, I have a Collection view, with a childView for it's models.
The collection assigned to the CollectionView is a PageableCollection from Backbone.paginator. The mode is set to infinite.
When requesting the next page like so getNextPage(), the collection is fetching data and assigning the response to the collection, overwriting the old entries, though the full version is store in collection.fullCollection. This is where I can find all entries that the CollectionView needs to render.
Marionette is being smart about collection events and will render a new childView with it's new model when a model is being added to the collection. It will also remove a childView when it's model was removed.
However, that's not quite what I want to do in this case since the collection doesn't represent my desired rendered list, collection.fullCollection is what I want to show on page.
Is there a way for my Marionette view to consider collection.fullCollection instead of collection, or is there a more appropriate pagination framework for Marionette?
Here's a fiddle with the code
For those who don't like fiddle:
App = Mn.Application.extend({});
// APP
App = new App({
start: function() {
App.routr = new App.Routr();
Backbone.history.start();
}
});
// REGION
App.Rm = new Mn.RegionManager({
regions: {
main: 'main',
buttonRegion: '.button-region'
}
});
// MODEL
App.Model = {};
App.Model.GeneralModel = Backbone.Model.extend({});
// COLLECTION
App.Collection = {};
App.Collection.All = Backbone.PageableCollection.extend({
model: App.Model.GeneralModel,
getOpts: function() {
return {
type: 'POST',
contentType: 'appplication/json',
dataType: 'json',
data: {skip: 12},
add: true
}
},
initialize: function() {
this.listenTo(Backbone.Events, 'load', (function() {
console.log('Load more entries');
// {remove: false} doesn't seem to affect the collection with Marionette
this.getNextPage();
})).bind(this)
},
mode: "infinite",
url: "https://api.github.com/repos/jashkenas/backbone/issues?state=closed",
state: {
pageSize: 5,
firstPage: 1
},
queryParams: {
page: null,
per_page: null,
totalPages: null,
totalRecords: null,
sortKey: null,
order: null
},
/*
// Enabling this will mean parseLinks don't run.
sync: function(method, model, options) {
console.log('sync');
options.contentType = 'application/json'
options.dataType = 'json'
Backbone.sync(method, model, options);
}
*/
});
// VIEWS
App.View = {};
App.View.MyItemView = Mn.ItemView.extend({
template: '#item-view'
});
App.View.Button = Mn.ItemView.extend({
template: '#button',
events: {
'click .btn': 'loadMore'
},
loadMore: function() {
Backbone.Events.trigger('load');
}
});
App.View.MyColView = Mn.CollectionView.extend({
initialize: function() {
this.listenTo(this.collection.fullCollection, "add", this.newContent);
this.collection.getFirstPage();
},
newContent: function(model, col, req) {
console.log('FullCollection length:', this.collection.fullCollection.length, 'Collection length', this.collection.length)
},
childView: App.View.MyItemView
});
// CONTROLLER
App.Ctrl = {
index: function() {
var col = new App.Collection.All();
var btn = new App.View.Button();
var colView = new App.View.MyColView({
collection: col
});
App.Rm.get('main').show(colView);
App.Rm.get('buttonRegion').show(btn);
}
};
// ROUTER
App.Routr = Mn.AppRouter.extend({
controller: App.Ctrl,
appRoutes: {
'*path': 'index'
}
});
App.start();
You could base the CollectionView off the full collection, and pass in the paged collection as a separate option:
App.View.MyColView = Mn.CollectionView.extend({
initialize: function(options) {
this.pagedCollection = options.pagedCollection;
this.pagedCollection.getFirstPage();
this.listenTo(this.collection, "add", this.newContent);
},
// ...
}
// Create the view
var colView = new App.View.MyColView({
collection: col.fullCollection,
pagedCollection: col
});
Forked fiddle

How to load external html template in backbone view

I am a novice to backbone and need your help. I am facing an issue where I am not able to load an external template.
Here's the question:
I have a home_template.html which has a div i need to populate with an external template
<div class="ASContainer" id="asContainerView">
</div>
Now I have a Homeview.js
define([
'jquery',
'underscore',
'backbone',
'marionette',
'text!templates/home/homeTemplate.html',
'text!templates/home/activityTemplate.html',
'text!templates/compose/composeTemplate.html',
'text!templates/comments/commentsTemplate.html',
'views/home/ActivityListView',
'views/ComposePost/ComposePostView',
'views/ComposePost/WSListView',
], function ($, _, Backbone, Marionette, homeTemplate, activityTemplate, composeTemplate, commentsTemplate, ActivityListView, ComposePostView, WSListView) {
var HomeView = Backbone.View.extend({
el : $("#page"),
transition : 'slide',
activitiesListView : null,
initialize : function () {
this.$el.attr('data-transition', this.transition);
this.currentUserLogin = currentUserLogin;
var that = this;
this.activityId = this.options.activityId;
this.workspaceUrl = this.options.workspaceUrl;
this.context = this.options.parentContext;
},
events : {
'click #composePost' : 'onComposePost',
'click #btnPost' : 'onPostClick',
'click #cancelpost' : 'onCancelClick',
'click #comments' : 'OnCommentsClick',
'click #LeftNavIcon' : 'OnLeftNavigationClick',
},
render : function () { debugger
var that = this;
$('.menu li').removeClass('active');
$('.menu li a[href="#"]').parent().addClass('active');
that.callAjaxReload("homeTemplate.html", "aaa", "AS_Content");
this.$el.html(homeTemplate);
$("#userloggedin").text(currentUserLogin);
//var sidebarView = new SidebarView();
//sidebarView.render();
},
cleanup: function() {
this.undelegateEvents();
$(this.el).empty();
},
getActivitiesForWorkspace: function(ActStreamPageSize, activityId) { debugger;
try {
//LogInfoStart('getActivitiesStreamAjaxCommand');
var BrowserTimeZone = getTimezoneName();
$.ajax({
type : "POST",
url : siteBaseUrl + '/ActivityStreamClientService.svc/GetActivityStreamForWorkSpace',
contentType : "application/json; charset=utf-8",
dataType : 'json',
cache : false,
data : JSON.stringify({
workspaceUrl : siteBaseUrl,
userLoginName : currentUser,
filterValue : "All",
searchTxt : '',
searchFilter : "All", //add
searchTag : '', //add
activityId : activityId,
pageSize : 5,
commentCount : 9999,
tabContext : "MY",
customFilter : '',
activityMode : '',
time : new Date(), // need to add this for iPhone caching ajax posts problem!
browserTimeZone : BrowserTimeZone, // added for time zone changes in new ux
MySiteMode : '',
MySiteLogggedInUserName : '',
MySiteProfileVisitorruserName : '',
MySiteDetails : ''
}),
processData : true,
success : function (msg) { debugger;
//Define collection
var model = Backbone.Model.extend({});
var collection = Backbone.Collection.extend({
model : model
});
//var activityListView = ActivityListView({model : activitiesList});
//Add it to the collection after fetch
msgActivities = msg.GetActivityStreamForWorkSpaceResult;
var activitiesList = new collection(msgActivities);
//gtemplate = $('#personTemplate').html();
//var template: _.template( $('#personTemplate').html());
if (activityId <= 0) {
console.log("here");
$("#asContainerView").html(ActivityListView({model : activitiesList}).el);
}
else {
console.log("I am there");
$(new ActivityListView({model : activitiesList}).el).appendTo("#asContainerView");
}
lastactivityId = msgActivities[(msgActivities.length - 1)].ActivityID;
//Add collection to context
// that.context.collections.add(activitiesCollection, collectionTitle);
//that.context.currentCollectionNameId = collectionTitle; // for details page to work (ws url of stream is different than the activity ws url!!
//that.GetActivitiesSuccess(msg, dfd, that.eventData.onSuccessCallback);
//customFilterValue = "all";
},
error : function (err) {
//LogError('activities-ajax-commands.js getActivitiesStreamAjaxCommand.execute - Error', '', //that.context.parentContext.mainGlobalObject.CurrentUser, err);
//$.utilsSystem.alert('Error getting activities.', 'single');
//if (onSuccessCallback) onSuccessCallback();
}
});
} catch (err) {
// LogError('activities-ajax-commands.js getActivitiesStreamAjaxCommand.GetActivitiesError', '', $.SX.mainGlobalObject.CurrentUser, err);
}
},
callAjaxReload: function (url, parameter, classname) { debugger;
var that = this;
console.log(classname);
// $.ajax({
//url:url,
//dataType:datatype,
//success:function(data){
// }
//GetConfigurationSettings
var BrowserTimeZone = getTimezoneName();
$.ajax({
type : "GET",
url : siteBaseUrl + '/ActivityStreamClientService.svc/GetConfigurationSettings', //this.ActivitiesStreamOptions.ACTSTREAMGETCONFIGITEMURL,
contentType : "application/json; charset=utf-8",
dataType : 'json',
data : {
configurationItem : "ActivityStreamCount",
workspaceUrl : siteBaseUrl //this.ActivitiesStreamOptions.ActStreamWorkspaceUrl
},
async : false,
success : function (msg) { debugger;
//ActivitiesStreamOptions.ActStreamPageSize = msg.GetConfigurationSettingsResult[0];
ActivityStreamPageSize = msg.GetConfigurationSettingsResult[0];
that.**getActivitiesForWorkspace**(msg.GetConfigurationSettingsResult[0], 0);
debugger;
//console.log(ActivitiesStreamOptions.ActStreamPageSize);
// ActivitiesStreamOptions.ActStreamFilterValue = '';
},
error : function (err) {
console.log("Error: Hasan");
//LogError('activities-ajax-commands.js getActivitiesStreamAjaxCommand.execute - Error', '', that.context.parentContext.mainGlobalObject.CurrentUser, err);
// that.ActivitiesStreamOptions.ActStreamPageSize = 5;
}
});
//})
//$('.'+classname).html(data);
},
});
return HomeView;
});
Now this HomeView js has two functions. callAjaxReload and getActivitiesForWorkspace.
The render calls callAjaxReload which has an ajax function which on success calls getActivitiesForWorkspace.Now getActivitiesForWorkspace also has an ajax function and on whose success I have to pass the model to another View ActivityList View and appends that the template under the div id asContainerView.
The problem which I am facing is that the this line below fails to load the ActivityListView.
$("#asContainerView").html(ActivityListView({model : activitiesList}).el);
I get an error that ActivityListView is undefined.
Can anybody help me here.
The ActivityListView.js is
define([
'jquery',
'underscore',
'backbone',
'marionette',
'views/home/ActivityListItemView',
'text!templates/home/activityTemplate.html',
], function ($, _, Backbone, Marionette, ActivityListItemView, activityTemplate) {
var ActivityListView = Backbone.View.extend({
initialize: function () {
this.render();
},
render: function () {
var activities = this.model.models;
var len = activities.length;
//var startPos = (this.options.page - 1) * 8;
//var endPos = Math.min(startPos + 8, len);
var startPos = 0;
var endPos = len;
//$(this.el).html('<ul class="thumbnails"></ul>');
for (var i = startPos; i < endPos; i++) {
{ //$(this.el).append(new ActivityListItemView({model: activities[i]}).initialize);
//console.log("activities[i]:"activityListItemView.model.attributes.ActivityTypeSourceName);
var activityListItemView = new ActivityListItemView({ model: activities[i] });
$(this.el).append(activityListItemView.el);
}
//console.log(activityListItemView.el);
//var personView = new PersonView({ model: person });
}
//$(this.el).append(new Paginator({model: this.model, page: this.options.page}).render().el);
return this;
}
});
});
and this calls ActivityListItemView.js
define([
'jquery',
'underscore',
'backbone',
'marionette',
'text!templates/home/activityTemplate.html',
], function ($, _, Backbone, Marionette, activityTemplate) {
var ActivityListItemView = Backbone.View.extend({
tagName: "div",
template: activityTemplate,
//my_template: _.template(gtemplate1),
//my_template: _.template("<p class=\"AS_Content\"> <%= ActivityContent %> </p> "),
initialize: function(){
this.render();
},
render: function(){ debugger;
//$(this.el).html(this.template(this.model.toJSON()));
this.$el.html(_.template(this.template, { user: this.model.attributes }));
//this.$el( this.template(this.model.toJSON()));
//this.$el.html( this.template(this.model.toJSON()));
}
});
});
Thanks
You have:
$("#asContainerView").html(ActivityListView({model : activitiesList}).el);
but your syntax is off; when you want to instantiate a new Backbone.View you need to use the new keyword:
$("#asContainerView").html(new ActivityListView({model : activitiesList}).el);
Your requirejs definitions should return ActivityListView and ActivityListItemView
The function should return an object that defines the module
see http://www.requirejs.org/docs/api.html#defdep

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 });

Backbone Marionette routing - only the first route works

I have the following router:
define([
'backbone.marionette',
'app',
'views/products/list',
'views/products/browsing_filter',
'views/products/detail',
'views/dashboard/index',
'views/layout'
],
function(Marionette, App, ProductListView, BrowsingFilterView, ProductDetailView, LayoutView){
var AppRouter = Backbone.Marionette.AppRouter.extend({
routes: {
'product/:id': 'showProduct',
'products/:id': 'showProduct',
'products': 'listProducts',
'*path': 'showDashboard',
},
listProducts: function(path) {
App.contentRegion.show(new ProductListView());
product_filter_view = new BrowsingFilterView();
},
showProduct: function(id) {
App.contentRegion.show(new ProductDetailView({id: id}));
},
showDashboard: function() {
return require(['views/dashboard/index', 'collections/newsfeed_items','models/newsfeed_item'], function(DashboardView, NewsfeedItemCollection, NewsfeedItem) {
App.contentRegion.show(new DashboardView({
collection: new NewsfeedItemCollection(),
model: new NewsfeedItem()
}));
});
}
});
return AppRouter;
});
When a route is called it works fine. However, when the next route is called the container for the region App.contentRegion is emptied and no new content is rendered.
When the new route is called, the AJAX requests are done as they should, the view simply seems to either become detached or not rendered at all.
What is wrong?
Edit:
ProductDetailView:
define([
'jquery',
'backbone',
'models/product',
'models/product_property_value',
'models/product_property',
'hbs!template/product_detail/detail',
'hbs!template/product_detail/edit_string',
'collections/product_property_values',
'collections/newsfeed_items',
'hbs!template/newsfeed/feed'
],
function($, Backbone, ProductModel, ProductPropertyValueModel, ProductPropertyModel, ProductDetailTemplate, StringEditTemplate, ProductPropertyValueCollection, NewsfeedItemCollection, FeedTemplate){
ProductDetailView = Backbone.View.extend({
el: '#product_detail',
product_id: null,
events: {
'click a.show_edit': 'triggerEdit',
// 'click div.edit_container a.save': 'saveChanges',
'submit form.edit_property_value': 'saveChanges',
'click a.cancel_edit': 'cancelEdit'
},
initialize: function(param){
this.product_id = param.id;
this.product = new ProductModel({'id': this.product_id});
this.product.fetch();
this.newsfeeditems = new NewsfeedItemCollection({'product': {'id': this.product_id}});
this.listenTo(this.newsfeeditems, 'change', this.renderFeed);
this.listenTo(this.newsfeeditems, 'fetch', this.renderFeed);
this.listenTo(this.newsfeeditems, 'sync', this.renderFeed);
this.newsfeeditems.setProductId(this.product_id);
this.newsfeeditems.fetch({reset:true});
this.listenTo(this.product, 'change', this.render);
this.listenTo(this.product, 'fetch', this.render);
this.listenTo(this.product, 'sync', this.render);
},
renderFeed: function(r) {
context = this.newsfeeditems.toJSON();
this.$el.find('#product_newsfeed').html(FeedTemplate({items:context}));
},
edit_container: null,
product_property_model: null,
triggerEdit: function(r) {
r.preventDefault();
this.cancelEdit();
editable_container = $(r.target).parents('.editable').first();
product_property_value_ids = editable_container.data('property-value-id');
edit_container = $(editable_container).find('div.edit_container');
if(edit_container.length === 0) {
console.log(edit_container);
editable_container.append('<div class="edit_container"></div>');
edit_container = $(editable_container).find('div.edit_container');
}
this.edit_container = edit_container;
value_init = [];
for(var i = 0; i < product_property_value_ids.length; i++) {
value_init = {'id': product_property_value_ids[i]};
}
if(product_property_value_ids.length > 1) {
throw new Exception('Not supported');
}
this.edit_value = new ProductPropertyValueModel({'id': product_property_value_ids[0]});
this.listenTo(this.edit_value, 'change', this.renderEditField);
this.listenTo(this.edit_value, 'reset', this.renderEditField);
this.listenTo(this.edit_value, 'fetch', this.renderEditField);
this.edit_value.fetch({'reset': true});
return false;
},
cancelEdit: function() {
this.$el.find('.edit_container').remove();
},
renderEditField: function() {
edit_container.html(StringEditTemplate(this.edit_value.toJSON()));
},
saveChanges: function(r) {
r.preventDefault();
console.log('save changes');
ev = this.edit_value;
_.each($(r.target).serializeArray(), function(value, key, list) {
ev.set(value, key);
});
ev.save();
},
render: function(r) {
context = this.product.toJSON();
this.$el.html(ProductDetailTemplate(context));
$(document).foundation();
return this;
}
});
return ProductDetailView;
});
In our app we use appRoutes instead of routes as the key. I think that is the way you should do it when using Marionette.
Next you should make sure you are starting Backbone.history by using Backbone.history.start().

Clear localStorage and change the view Backbone

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});
});
});

Categories

Resources