5

I've got an issue with my Angular application. The application is running well the first time, it is possible to navigate inside, but it fails when I try to refresh the page with F5. When F5 key is pressed, the partial is called to the server, and the server obviously responds with the partial view, but not the whole application.

Node routing:

app.get('/', node_routes.welcome);
...
app.get('/profile', pass.ensureAuthenticated, profile_routes.profile);
app.get('/profile/:name', pass.ensureAuthenticated, profile_routes.partials);
app.post('/profile/update', pass.ensureAuthenticated, profile_routes.update);
...

Controller:

exports.profile = function(req, res) {
    var user = req.user;
    Profile.findOne({ username: user.username }, function(err, profile) {
        if (err) { 
            res.redirect('/'); 
        }       
        res.render("profile");
    });  
};  

exports.partials = function(req, res) {
    var name = req.params.name;
    var user = req.user;
    Profile.findOne({ username: user.username }, function(err, profile) {
        if (err) { 
            res.redirect('/'); 
        }       
        res.render(path.join(__dirname + '/../views/profile/' + name));
    });
};

exports.update = function(req, res){
    var profile = req.body;
    delete profile._id;
    Profile.update({'username':profile.username},profile,{safe:true}, function(err, result){
        if(err) {
            console.log('Error updating profile. ' + err);
            res.redirect('/profile');
        }
        else{
            console.log('' + result + ' profile updated for user: ' + profile.username);
            res.redirect('/profile');
        }           
    });
};

Angular application

myApp.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider){
    $routeProvider
       .when('/profile/update', {
           controller: 'myJobOfferListCtrl',
           templateUrl: '/profile/update',
           reloadOnSearch: false
       })
       .when('/profile', {
           controller: 'myJobOfferListCtrl',
           templateUrl: '/profile/dashboard'
       })
       .when('/profile/dashboard', {
           controller: 'myJobOfferListCtrl',
           templateUrl: '/profile/dashboard',
           reloadOnSearch: false
       })
       .when('/profile/offers', {
           controller: 'myJobOfferListCtrl',
           templateUrl: '/profile/offers',
           reloadOnSearch: false
       })      
       .otherwise({redirectTo: '/profile'});
    $locationProvider.html5Mode(true);
}]);

My profile page

extends layout

block content   
   div.container-fluid(ng-app="appProfile", ng-controller="myJobOfferListCtrl", ng-init="initProfile()")
      div.row-fluid
         div.span3(ng-controller="navBarCtrl")
           div.well.sidebar-nav
             ul.nav.nav-list
               li.well.nav-header.
                  My Profile ({{data.profile.username}})
               li(ng-class="{ active: isActive('dashboard')}") 
                  a(href="/profile/dashboard") Dashboard   
               li(ng-class="{ active: isActive('update')}") 
                  a(href="/profile/update") Update profile
               li.divider
               li.well.nav-header My Jobs
               li(ng-class="{ active: isActive('offers')}") 
                  a(href="/profile/offers") Offers
               li(ng-class="{ active: isActive('application')}") 
                  a(href="/profile/application") Applications
         div.span9(ng-view, ng-cloak)

And a sample partial view page

   div.row-fluid 
      div(ng-init="initMyOffers()")  
         accordion.span8.offset1(close-others="true")
            accordion-group(ng-repeat="item in data.myJobs", heading="{{item.title}}")
               accordion-heading
                  {{item.title}}
                  i.pull-right.icon-remove-circle.removeButton(ng:click="remove(item)") 
               p {{item.description}}
               hr
               div.footer
                  div.pull-left 
                     i.icon-calendar.calendar
                     {{item.dueDate | date:'d MMMM yyyy'}}
                  div.pull-right 
                     i.icon-user 
                     {{item.author}}

How can I reload a partial page when refreshing with F5 ?? What I expected with angular is that when trying to refresh for example the page /profile/dashboard, the partial /views/profile/dashboard.jade is called but also the views/profile.jade. And what about the $scope ?

Sorry, but I'm a little confioused... thanks for you help !

1
  • One simple solution is to turn off HTML5 routing: $locationProvider.html5Mode(false). Commented Nov 20, 2013 at 20:27

3 Answers 3

4

I think it is a common problem and the user is not supposed to use F5 in your application. I do not think, it is possible to stop default browser action after F5 has been pressed.

One simple solution is to add this or similar script to every view:

<script>
    if (angular == undefined)
        // alert("It is SPA (Single Page Application) -- do not press F5 anymore, please.");
        window.location.replace("your/main/url");
</script>

A bit more complicated would be realization of the following scenario.

  1. user hits f5
  2. server sends partial view for current url current_url
  3. client codes detects lack of angular and send request to be redirected to current_url
  4. server sends modified version of the index.html file [with e.g. some hidden input field, that stores current_url]
  5. after angular has been loaded, the application checks the hidden field and change the location accordingly
Sign up to request clarification or add additional context in comments.

5 Comments

I think that this will lead to downright terrible user experience if refreshing the page takes one back to the main page with a grim alert. The user should be allowed to reload the page and the application should just work. However, there are some circumstances where this is not possible and telling the user to not take those actions might be the best thing to do, but certainly not for every SPA.
@musically_ut it was rather meant as a form of joke -- of course it is very context dependent. but commented that out.
Ok, so if I understand well, there is no "simple" way to deal with refresh... I must trap that angular is not loaded and force reload of the main view then redirect to the page to be reloaded. Not so easy and a little bit tricky. Hope angular had such an embeded functinality but I understand that "user is not supposed to refresh" but the application has not to fail if it is the case !
@G_A The problem is that hitting reload clears all of state from current tab. It has nothing to do with angular itself. It is how JavaScript in browsers is supposed to work. Btw. my proposals are probably the most basic. You can store state of your application in cookies or other permanent storage (with HTML5), so you can be somehow prepared for recreating the state of application after reload. But in most context it is probably not worth to be done.
The first thing I want is that the application does not fail. So I think that redirecting to the "main" view when detecting that this is not called from angular. Nevermind that current state is flushed for the moment, as a first step, this is a perfectly suitable approach.
2

The simplest and most elegant way to avoid losing stuff when refreshing an Angular app is to store whatever values you need in a cookie by using $cookies service. Here's an example. Hope this helps.

"use strict";
angular.module('myApp').factory('SessionSrv', [ '$cookies', function($cookies){

    var _myString = $cookies.get('myString'); // e.g. "foo"
    var _myObject = $cookies.getObject('myObject'); // e.g. {foo: "bar"}

    return {

        setMyString: function(myString){
            _myString = myString;
            $cookies.put('myString', _myString);
        },

        getMyString: function(){
            return _myString;
        },

        setMyObject: function(myObject){
            _myObject = myObject;
            $cookies.putObject('myObject', _myObject);
        },

        getMyObject: function(){
            return _myObject;
        },

    };
}]);

Comments

1

Finally implemented the solution as proposed by artur :

<script>
  if (typeof angular == 'undefined')
     window.location.replace("/main_url");
</script>

2 Comments

Not entirely sure why you couldn't have just accepted his answer instead if you're just going to say that his answer solved your problem?
because there is a little difference and because his solution did not work as is.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.