Backbone unwanted route redirect on webkit - javascript

I have an unwanted route redirect after authentification
My base url is
http://127.0.0.1:8020/VT/
If the user isn't logged in localstorage, i'll load the login view.
The main menu view render correctly if the login succeed,
I have this unwanted automaticaly redirection to
http://127.0.0.1:8020/
This only happen on webkit browser. (haven't tried on IE)
Router = new MainRouter();
Backbone.history.start({
pushState: false,
root: "/VT/"
});
var MainRouter = Backbone.Router.extend({
routes: {
"": "login",
"login": "login",
"main": "mainMenu"
},
showView: function(view){
if (this.currentView){
this.currentView.close();
}
this.currentView = view;
this.currentView.render();
$('#wrapper').html( this.currentView.el );
},
login: function(){
var result = app.session.isLoggedIn();
if(result == true){
this.navigate('main', {trigger: true, replace: true});
}
else{
var loginView = new LoginView();
this.showView( loginView );
}
},
mainMenu: function(){
var mainMenu = new MainMenuView();
this.showView( mainMenu );
},
});
var LoginView = Backbone.View.extend({
name: "Login",
el: $('#page-login'),
initialize : function() {
this.template = _.template( app.get("tpl/nav/login.html") );
this.clearStatusBar();
},
render: function(){
var renderedContent = this.template();
$(this.el).html(renderedContent);
return this;
},
events: {
"click input[type=submit]": "loginAction"
},
validate: function(){
// some validation code
},
loginAction: function(){
if( this.validate() ){
app.router.navigate('main', {trigger: true, replace: true});
}
else{
alert('Login failed');
}
},
});
I can't figure it out why. Any suggestions ?

Related

Single model doesn't show with router

I can not understand how to show a single product. When I want to show product page (model view - app.ProductItemView in productPageShow) I get "Uncaught TypeError: Cannot read property 'get' of undefined at child.productPageShow (some.js:58)" >> this.prod = this.prodList.get(id);
Here is my code:
// Models
var app = app || {};
app.Product = Backbone.Model.extend({
defaults: {
coverImage: 'img/placeholder.png',
id: '1',
name: 'Unknown',
price: '100'
}
});
app.ProductList = Backbone.Collection.extend({
model: app.Product,
url: 'php/listProducts.php'
});
// Views
app.ProductListView = Backbone.View.extend({
el: '#product-list',
initialize: function() {
this.collection = new app.ProductList();
this.collection.fetch({ reset: true });
this.render();
this.listenTo(this.collection, 'reset', this.render);
},
render: function() {
this.collection.each(function(item) {
this.renderProduct(item);
}, this);
},
renderProduct: function(item) {
app.productView = new app.ProductView({
model: item
});
this.$el.append(app.productView.render().el);
}
});
app.ProductItemView = Backbone.View.extend({
tagName: 'div',
template: _.template($('#productPage').html()),
render: function(eventName) {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
app.ProductView = Backbone.View.extend({
tagName: 'div',
template: _.template($('#productTemplate').html()),
render: function() {
this.$el.html(this.template(this.model.attributes));
return this;
}
});
// Router
app.Router = Backbone.Router.extend({
routes: {
"list": 'list',
"product/:id": "productPageShow"
},
initialize: function() {
this.$content = $("#product-list");
},
list: function() {
this.prodList = new app.ProductList();
this.productListView = new app.ProductListView({ model: this.prodList });
this.prodList.fetch();
this.$content.html(app.productListView.el);
},
productPageShow: function(id) {
this.prod = this.prodList.get(id);
this.prodItView = new app.ProductItemView({ model: this.prod });
this.$content.html(this.prodItView.el);
}
});
$(function() {
new app.Router();
Backbone.history.start();
});
There are some conceptual problems with the code, without getting into too much details, there are a lot of things happening in the Router that don't belong there, but for a (currently) non-complex application that's manageable.
I'll focus on the app.Router file because that's the culprit of your problems most probably.
routes: {
"list": 'list',
"product/:id": "productPageShow"
}
Let's start with the basics, when you define a list of routes in Backbone Router( or any other Router in other frameworks ) you give a route a key that will correspond to something in the URL that the Router will recognize and call a callback method.
If you navigate your browser to:
http://your-url#list
Backbone will call the list callback
Similarly:
http://your-url#product/1
Backbone will call productPageShow callback
Thing to know: ONLY ONE ROUTE CALLBACK CAN EVER BE CALLED! The first time a Backbone Router finds a matching route it will call that callback and skip all others.
In your code you're relying on the fact that this.prodList will exist in productPageShow method but that will only happen if you first go to list route and then to product/{id} route.
Another thing .. in your listcallback in the Router you set a model on the ProductListView instance .. but that model is neither user, nor is it a model since this.productList is a Backbone.Collection
Additionally, you need to know that fetch is an asynchronous action, and you're not using any callbacks to guarantee that you'll have the data when you need it ( other than relying on the 'reset' event ).
So this would be my attempt into making this workable:
// Models
var app = app || {};
app.Product = Backbone.Model.extend({
defaults: {
coverImage: 'img/placeholder.png',
id: '1',
name: 'Unknown',
price: '100'
}
});
app.ProductList = Backbone.Collection.extend({
model: app.Product,
url: 'php/listProducts.php'
});
// Views
app.ProductListView = Backbone.View.extend({
el: '#product-list',
initialize: function() {
this.render();
this.listenTo(this.collection, 'reset', this.render);
},
render: function() {
this.collection.each(function(item) {
this.renderProduct(item);
}, this);
},
renderProduct: function(item) {
app.productView = new app.ProductView({
model: item
});
this.$el.append(app.productView.render().el);
}
});
app.ProductItemView = Backbone.View.extend({
tagName: 'div',
template: _.template($('#productPage').html()),
render: function(eventName) {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
app.ProductView = Backbone.View.extend({
tagName: 'div',
template: _.template($('#productTemplate').html()),
render: function() {
this.$el.html(this.template(this.model.attributes));
return this;
}
});
// Router
app.Router = Backbone.Router.extend({
routes: {
"": "list",
"product/:id": "productPageShow"
},
initialize: function() {
this.$content = $("#product-list");
},
list: function() {
this.prodList = new app.ProductList();
this.productListView = new app.ProductListView({ collection: this.prodList });
this.prodList.fetch({reset:true});
this.$content.html(app.productListView.el);
},
productPageShow: function(id) {
try {
this.prod = this.prodList.get(id);
this.prodItView = new app.ProductItemView({ model: this.prod });
this.$content.html(this.prodItView.el);
} catch (e) {
// Navigate back to '' route that will show the list
app.Router.navigate("", {trigger:'true'})
}
}
});
$(function() {
app.Router = new app.Router();
Backbone.history.start();
});
So with a bit of shooting in the dark without the complete picture, this is what changed:
ProductListView is no longer instantiating ProductList collection that is done in the list callback in Router
Changed the route from 'list' to '', that will guarantee that the list is shown immediately
In case there are no product data available in productPageShow navigate back to list

Backbone: Navigate does not hit the specified route

Calling navigate after saving a model.
this.model.save({},{
success: function(model, response, options){
Backbone.history.navigate('getCampaigns', {tigger: true});
}
});
But it never hits the specified route.
Route class
var Router = Backbone.Router.extend({
routes: {
"":"home",
"login":"login",
"getCampaigns":"getCampaigns"
},
start: function() {
Backbone.history.start({pushState:true});
},
home: function() {
var loginView = new LoginView({model: loginModel});
loginView.render();
$(".container").append(loginView.el);
},
login: function(event) {
event.preventDefault();
},
getCampaigns: function() {
this.dashboardList.fetch();
$('.container').html(this.dashboardListView.render().el);
}
});
var app = new Router();
app.start();
You have an error in your code :
Backbone.history.navigate('getCampaigns', {trigger: true}); // not {tigger: true}

Backbone view not getting the JSON objects

Making a Rails app with Backbone on the frontend. There are two Backbone models, Publications and Articles. I am having issues rendering one of my Backbone views. Here is the code that is giving me an issue:
SimpleGoogleReader.Views.PublicationsIndex = Backbone.View.extend({
template: JST['publications/index'],
el: '#publication',
events:{
'click #new_feed': 'createFeed'
},
initialize: function() {
this.listenTo(this.model, "sync", this.render);
},
render: function(){
this.$el.html( this.template({publications: this.model.toJSON()}) );
return this;
},
createFeed: function(e){
e.preventDefault();
var feed_url = $('#new_feed_name').val();
// var that = this;
this.model.create(
{url: feed_url},
{ success: function(data){
$.post('/articles/force_update', {url: feed_url, publication_id: data.id}, function(data){
});
}
}
);
}
});
I know the render function is getting called and when I console.log(this.model.toJSON()) it does not return the JSON objects. However the render function works just fine within the Articles View:
SimpleGoogleReader.Views.ArticlesIndex = Backbone.View.extend({
template: JST['articles/index'],
el: '#article',
initialize: function() {
this.listenTo(this.model, "sync", this.render);
},
render: function(){
console.log(this.model.toJSON());
this.$el.html( this.template({articles: this.model.toJSON()}) );
return this;
}
});
** Edit ** here is my router where I instantiate the views:
SimpleGoogleReader.Routers.Publications = Backbone.Router.extend({
routes: {
'': 'home',
'all_articles': 'get_all_articles',
'publications/:id': 'articles_by_id',
'publications/:id/delete': 'delete_publication'
},
home: function(){
var publications = new SimpleGoogleReader.Collections.Publications();
var articles = new SimpleGoogleReader.Collections.Articles();
var pubIndex = new SimpleGoogleReader.Views.PublicationsIndex({model: publications});
var artIndex = new SimpleGoogleReader.Views.ArticlesIndex({model: articles});
articles.listenTo(publications, "sync", function(){
articles.fetch( {success: function(){}} );
});
publications.fetch();
},
Any ideas on where the discrepancy may lie?

How to handle a simple click event in Backbone.js?

I am having difficulty with something very simple in Backbone. I want to wire up the <h1> in my page so that when the user clicks on it, it returns seamlessly to the homepage, without a postback.
This is the HTML:
<h1><a id="home" href="/">Home</a></h1>
(UPDATE: fixed ID as suggested by commenter.) And this is my Backbone view and router:
var HomeView = Backbone.View.extend({
initialize: function() {
console.log('initializing HomeView');
},
events: {
"click a#home": "goHome"
},
goHome: function(e) {
console.log('goHome');
e.preventDefault();
SearchApp.navigate("/");
}
});
var SearchApp = new (Backbone.Router.extend({
routes: {
"": "index",
},
initialize: function(){
console.log('initialize app');
this.HomeView = new HomeView();
},
index: function(){
// do stuff here
},
start: function(){
Backbone.history.start({pushState: true});
}
}));
$(document).ready(function() {
SearchApp.start();
});
The console is showing me
initialize app
initializing HomeView
But when I click on the <h1>, the page posts back - and I don't see goHome in the console.
What am I doing wrong? Clearly I can wire up the <h1> click event simply enough in jQuery, but I want to understand how I should be doing it in Backbone.
If you enable pushState you need to intercept all clicks and prevent the refresh:
$('a').click(function (e) {
e.preventDefault();
app.router.navigate(e.target.pathname, true);
});
Something like:
$(document).ready(function(){
var HomeView = Backbone.View.extend({
initialize: function() {
console.log('initializing HomeView');
}
});
var AboutView = Backbone.View.extend({
initialize: function() {
console.log('initializing AboutView');
}
});
var AppRouter = Backbone.Router.extend({
routes: {
"": "index",
"about":"aboutView"
},
events: function () {
$('a').click(function (e) {
e.preventDefault();
SearchApp.navigate(e.target.pathname, true);
});
},
initialize: function(){
console.log('initialize app');
this.events();
this.HomeView = new HomeView();
},
index: function(){
this.HomeView = new HomeView();
},
aboutView : function() {
this.AboutView = new AboutView();
}
});
var SearchApp = new AppRouter();
Backbone.history.start({pushState: true});
});
Your tag id is invalid, try this:
<h1><a id="home" href="/">Home</a></h1>

Login Handling in Backbone.js

i need a very simple login system for my web application with backbone.js
Workflow:
App Start -> LoginView -> When Logged In -> App
Here is my solution. What can i do better?
Login Status Model:
window.LoginStatus = Backbone.Model.extend({
defaults: {
loggedIn: false,
userId: null,
username: null,
error: "An Error Message!"
},
initialize: function () {
_.bindAll(this, 'getSession', 'setStorage');
},
getSession: function (username, password) {
var tmpThis = this;
$.getJSON('http://requestURL.de/getSession.php?username=' + username + '&password=' + password, function(data) {
if (data != null) {
tmpThis.setStorage(data.id, data.username);
$.mobile.changePage("#home");
}
});
},
setStorage: function(userId, username) {
localStorage.setItem("userId", userId);
localStorage.setItem("username", username);
this.set({ "loggedIn" : true});
}
});
Here is my Login View:
window.LoginView = Backbone.View.extend({
el: $("#login"),
initialize:function () {
this.render();
},
events: {
"click input[type=submit]": "onSubmit"
},
onSubmit: function(event) {
var username = $(this.el).find("#user-username").val(),
password = $(this.el).find("#user-password").val();
this.model.getSession(username, password);
return false;
},
render:function () {
if (this.model.get("loggedIn")) {
var template = _.template( $("#login_template").html(), {} );
} else {
var template = _.template( $("#login_template").html(), { "error" : this.model.get("error") } );
}
$(this.el).html( template );
}
});
My suggestion to you is making the login outside the backbone app and only after a success login process let them access the "single page app"
You can refer to this backbone project which does the login request using POST method. https://github.com/denysonique/backbone-login

Categories