Category Archives: backbone-routing

Parse.Router cannot find reference files when deep link

I've been slowly finding my way through building a single page website using parse.js, which includes Parse.Router.

Today I finally overcame the issue where clicking the browser refresh button caused a 404 error by modifying my .htaccess file.

The issue I'm facing now is that if I navigate to say "localhost/root/profile/edit" and then click the browser refresh button, my page is found OK, but none of my resources load correctly. In fact, I get this error in my console:

SyntaxError: expected expression, got '<'

I can see in the console that it is trying to load my files from "localhost/root/profile/js/file.js" instead of from where they're located in my root directory. The odd thing is that when I click refresh at "localhost/root/profile" everything loads up just fine. Just one level deeper and it doesn't work.

Here is my .htaccess file:

<ifModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{THE_REQUEST} ^.*/index.php
    RewriteRule ^(.*)index.php$ $1 [R,L]

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} !^/index\.php

    RewriteRule (.*) index.php
</ifModule>

Here is my app.js file, loaded in my main index.php file:

// app.js
$(function() {
    Parse.$ = jQuery;

    Parse.initialize("xxxx", "xxxx");

    // * ---------
    // * Root View
    // * ---------
    var RootView = Parse.View.extend({
        template: Handlebars.compile($('#root-tpl').html()),
        events:{
        },
        render: function() {
            this.$el.html(this.template());
        }
    });

    // * ------------
    // * Profile View
    // * ------------
    var ProfileView = Parse.View.extend({
        template: Handlebars.compile($('#profile-tpl').html()),
        events: {
            'click .edit-profile': 'edit'
        },
        edit: function(){
            Parse.history.navigate("profile/edit", true);
            return false;
        },
        render: function(){
            var attributes = this.model.toJSON();
            this.$el.html(this.template(attributes));
        }
    });

    // * ------------
    // * Edit View
    // * ------------
    var EditView = Parse.View.extend({
        template: Handlebars.compile($('#edit-profile-tpl').html()),
        render: function(){
            var attributes = this.model.toJSON();
            this.$el.html(this.template(attributes));
        }
    });

    // * ------------
    // * Parse Router
    // * ------------
    var MyRouter = Parse.Router.extend({
        initialize: function(options){
            this.user = Parse.User.current();
            $("body").on("click","a:not(a[data-bypass])",function(e){
                // block the default link behavior
                e.preventDefault();
                // take the href of the link clicked
                var href = $(this).attr("href");
                // pass this link to Parse
                Parse.history.navigate(href,true);
            });
        },      
        start: function(){
            Parse.history.start({
                root: App.ROOT,
                pushState:  true,
                silent:     false
            });
        },
        routes: {
            "":             "root",
            "profile":      "profile",
            "profile/edit": "editProfile",
            "*nf":          "notFound"
        },
        root: function() {
            var rootView = new RootView();
            rootView.render();
            $('.main-container').html(rootView.el);
        },
        profile: function() {
            if (this.user) {
                var profileView = new ProfileView({ model: this.user });
                profileView.render();
                $('.main-container').html(profileView.el);
            }  else {
                this.navigate("root", {trigger: true});
            }
        },
        editProfile: function() {
            var editView = new EditView({ model: this.user });
            editView.render();
            $('.main-container').html(editView.el);
        },
        notFound: function() {
            console.log("in the not found");
        }
    }),
    var App = {
        ROOT: "/root/",
        router: new MyRouter()
    };
    App.router.start();
});

Any help is greatly appreciated.

Parse.Router – Browser refresh button returns to index.html

I'm trying to get a website setup using Parse.com's parse.js framework. Included with the framework is Parse.Router (a fork of Backbone.Router, but seems slightly different in execution; but I could be wrong).

I have things working well but I came across the issue that when I'm on a page like 'localhost/root/profile' and I click the refresh button I get a 404 error. After some searching I took the approach to modify my .htaccess file so that this would instead trigger a redirect to my single page app 'index.html'. Now the issue is that when this page is reloaded it also seems to restart the router and thus return me to 'localhost/root' instead of 'localhost/root/profile'. Can anyone tell me where I'm going wrong?

Here is my .htaccess file:

// .htaccess
<ifModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} !index
    RewriteRule (.*) index.html [L]
</ifModule>

Here is my app.js file, loaded in my main index.html file:

// app.js
$(function() {
    Parse.$ = jQuery;

    Parse.initialize("xxxx", "xxxx");

    // * --------------
    // * Navigation Bar
    // * --------------
    var NavigationView = Parse.View.extend({
        template: Handlebars.compile($('#navigation-tpl').html()),
        events: {
            'click .nav-link': 'link'
        },
        link: function(e) {
            // Prevent Default Submit Event
            e.preventDefault();
            var href = $(e.target).attr('href');
            myRouter.navigate(href, { trigger: true });
        },
        render: function() {
            var attributes = this.model.toJSON();
            this.$el.html(this.template(attributes));
        }
    });

    // * ---------
    // * Root View
    // * ---------
    var RootView = Parse.View.extend({
        template: Handlebars.compile($('#root-tpl').html()),
        events:{
        },
        render: function() {
            this.$el.html(this.template());
        }
    });

    // * ------------
    // * Profile View
    // * ------------
    var ProfileView = Parse.View.extend({
        template: Handlebars.compile($('#profile-tpl').html()),
        events: {
        },
        render: function(){
            var attributes = this.model.toJSON();
            this.$el.html(this.template(attributes));
        }
    });

    // * ------------
    // * Parse Router
    // * ------------
    MyRouter = Parse.Router.extend({

        // Here you can define some shared variables
        initialize: function(options){
            console.log("initializing router, options: " + options);
            this.user = Parse.User.current();
        },      
        // This runs when we start the router. Just leave it for now.
        start: function(){
            Parse.history.start({
                root: "/root/",
                pushState:  true,
                silent:     false
            });
            this.navigate("", { trigger: true });
        },  
        // This is where you map functions to urls.
        // Just add '{{URL pattern}}': '{{function name}}'
        routes: {
            "":             "root",
            "profile":      "profile",
            "*nf":          "notFound"
        },
        root: function() {
            var rootView = new RootView();
            rootView.render();
            $('.main-container').html(rootView.el);
        },
        profile: function() {
            if (this.user) {
                var profileView = new ProfileView({ model: this.user });
                profileView.render();
                $('.main-container').html(profileView.el);
            }  else {
                this.navigate("root", {trigger: true});
            }
        },
        notFound: function() {
            console.log("in the not found");
        }
    }),
    myRouter = new MyRouter();
    myRouter.start();

    // * --------------------------
    // * Add Navigation to the View
    // * --------------------------
    var user = Parse.User.current();
    var navigationView;
    if (user) {
        navigationView = new NavigationView({model: user});
    } else {
        navigationView = new NavigationView();      
    }
    navigationView.render();
    $('.navigation-container').html(navigationView.el);
});

Do I have this setup correctly? This is my first attempt at this (aside from following some tutorials and reading through the docs), so I may be missing something quite obvious. Any help is appreciated.