5

Note: I am using AngularJS v1.6.0

I have implemented a stand-alone AngularJS app into another existing website, it exists on a single page on the site and the rest of the site doesn't have any Angular code running on it.

For example, a regular page on the site could be at:

http://example.com/any-page

Then the user can click on a link and it takes them to the page with the Angular JS running on it:

http://example.com/angularjs-app

When landing on this URL it loads the AngularJS app and appends #!/ to the URL as expected. It doesn't contain any elements from the rest of the site such as the header, so for the user it looks like a completely different section. However it also breaks the back button. It's not possible press back to go back to http://example.com/any-page. Each time you press back, it just loads the landing view of the AngularJS app again - effectively the user is stuck on the AngularJS app page and cannot go back to /any-page.

I think this has something to do with AngularJS routing, because it seems to refresh the #!/ portion of the URL when you press back, and just reload the initial view on the app. But I could be wrong.

Note that the back button works fine within the AngularJS app when visiting various routes. For example, if I navigate between various routes/views in the app, such as #!/login or #!/view-details, I can always go back through these views via the back button. But when it reaches that initial view, it stops working.

Does anyone know a way around this?

Note: I have looked at various other Stack Overflow posts on this, however they all seem to be concered with the back button not working at all, rather than this issue where the back button works for navigating between routes within the app, but not back to the original non-AngularJS page on the site.

Routing config

(function () {
    "use strict";

    var routes = {
        error: "/error",
        forgottenPassword: "/forgotten-password",
        home: "/home",
        login: "/login",
        orders: "/orders",
        paymentDetails: "/payment-details",
        personalDetails: "/personal-details",
        subscriptions: "/subscriptions",
        updatePassword: "/update-password",
        accountVerification: "/account-verification",
        register: '/register',
        profile: '/profile',
        accountConfirm: '/account-confirm',
        deleteAccount: '/delete-account'
    };

    var configFunc = function (
        $routeProvider,
        $locationProvider,
        CONFIG,
        ROUTES) {

        var resolve = {
            isLoggedIn: [
                "$q", "ERRORS", "core.services.sessionsService", function ($q, ERRORS, sessionsService) {
                    return sessionsService.isLoggedIn().then(function (isLoggedIn) {
                        if (isLoggedIn) {
                            return isLoggedIn;
                        }

                        return $q.reject({ error: ERRORS.route.requiresAuth });
                    });
                }
            ]
        };

        var getTemplateUrl = function(page) {
            return CONFIG.rootPagesUrl + page;
        };

        $routeProvider
            .when("/", {
                controller: "StartCtrl",
                template: ""
            })
            .when(ROUTES.login, {
                templateUrl: getTemplateUrl("login.html"),
                pageName: "Login"
            })
            .when(ROUTES.forgottenPassword, {
                templateUrl: getTemplateUrl("forgotten-password.html"),
                pageName: "Forgotten Password"
            })
            .when(ROUTES.home, {
                templateUrl: getTemplateUrl("home.html"),
                resolve: resolve
            })
            .when(ROUTES.personalDetails, {
                templateUrl: getTemplateUrl("personal-details.html"),
                resolve: resolve
            })
            .when(ROUTES.paymentDetails, {
                templateUrl: getTemplateUrl("payment-details.html"),
                resolve: resolve
            })
            .when(ROUTES.orders, {
                templateUrl: getTemplateUrl("orders.html"),
                resolve: resolve
            })
            .when(ROUTES.subscriptions, {
                templateUrl: getTemplateUrl("subscriptions.html"),
                resolve: resolve
            })
            .when(ROUTES.updatePassword, {
                templateUrl: getTemplateUrl("update-password.html"),
                pageName: "Update Password"
            })
            .when(ROUTES.accountVerification, {
                templateUrl: getTemplateUrl("account-verification.html"),
                pageName: "Account Verification"
            })
            .when(ROUTES.error, {
                templateUrl: getTemplateUrl("error.html"),
                pageName: "Error"
            })
            .when(ROUTES.register, {
                templateUrl: getTemplateUrl("register.html"),
                pageName: "Register"
            })
            .when(ROUTES.profile, {
                templateUrl: getTemplateUrl("profile.html"),
                resolve: resolve,
                pageName: "Profile"
            })
            .when(ROUTES.accountConfirm, {
                templateUrl: getTemplateUrl("accountConfirm.html"),
                pageName: "Registration Complete"
            })
            .when(ROUTES.deleteAccount, {
                templateUrl: getTemplateUrl("deleteAccount.html"),
                resolve: resolve,
                pageName: "Delete Account"
            })
            .otherwise({
                templateUrl: getTemplateUrl("login.html"),
                pageName: "Login"
            });
    };

    var config = [
        "$routeProvider",
        "$locationProvider",
        "CONFIG",
        "ROUTES",
        configFunc
    ];

    var module = angular.module("app");
    module.constant("ROUTES", routes);
    module.config(config);
})();

Portion of index where ng-view lives:

<body ng-app="app" ng-strict-di>

    <div>
        <div id="container" class="mpp-app">
            <div class="mpp-page" id="mpp-page">
                <div class="page-wrapper">
                    <div class="ui-module-container">
                        <div brand></div>
                    </div>
                    <div ng-view></div>
                </div>
            </div>
             <div class="ui-module-container">
                 <div footer></div>
             </div>
         </div>
    </div>

    <div id="spinner" class="hidden"><div class="icon"></div></div>

StartCtrl

(function () {
    "use strict";

    var func = function (
        $rootScope,
        $scope,
        $location,
        ROUTES,
        APP_EVENTS,
        CORE_EVENTS,
        SessionModel,
        sessionsService,
        configurationAggregator) {

        var broadcast = function(event, args) {
            $rootScope.$broadcast(event, args);
        };

        var redirectToLogin = function() {
            broadcast(APP_EVENTS.navigation.login);
        };

        // check if user is signed in and has a valid session
        var verifySession = function() {
            sessionsService.verify().then(function() {
                // if user is logged in navigate to profile
                // otherwise navigate to login
                configurationAggregator.getConfiguration().then(function () {
                    broadcast(APP_EVENTS.auth.login.success);
                    //broad cast(APP_EVENTS.navigation.home);
                    broadcast(APP_EVENTS.navigation.uktvProfile);
                }, redirectToLogin);
            }, redirectToLogin);
        };

        // init
        var sessionId = $location.search().guid;

        if (angular.isDefined(sessionId) && sessionId !== null) {
            broadcast(CORE_EVENTS.session.changed, { sessionId: sessionId });
            verifySession();
        } else {
            verifySession();
        }
    };

    var controller = [
        "$rootScope",
        "$scope",
        "$location",
        "ROUTES",
        "EVENTS",
        "mpp.core.EVENTS",
        "mpp.core.SessionModel",
        "mpp.core.services.sessionsService",
        "mpp.core.aggregators.configurationAggregator",
        func
    ];

    var module = angular.module("app");
    module.controller("StartCtrl", controller);
})();
shrewdbeans
  • 11,971
  • 23
  • 69
  • 115
  • What are you using to route to external url's? – Kamesh Jul 27 '17 at 07:20
  • Possible duplicate of [AngularJS, using routes without refreshes on back button](https://stackoverflow.com/questions/23457904/angularjs-using-routes-without-refreshes-on-back-button) – Ramesh Rajendran Jul 27 '17 at 07:23
  • 2
    Do you have more details? I created a simple test and everything is working in it. How is routing configured? A demo of this not working would help understand what is happening in this scenario – Brian Aug 11 '17 at 15:55
  • 1
    Thank you @Brian, I've added my routing config, let me know if there's anything else you need. If possible, are you able to add your simple test somewhere for me to take a look? It may help see the cause of the my issue. – shrewdbeans Aug 13 '17 at 14:25

2 Answers2

2

It is not an issue of the AngularJS Router (although might be a bug of the specific version, so please add information about the version to the question). But most likely you have a redirect in a router event(s)' handler, or in StartCtrl.

The example from the AngularJS tutorial works fine, you can get it here:

git clone https://github.com/angular/angular-phonecat.git

To localize the issue i would first try to run your app with following config:

$routeProvider
        .when('/', {
            template: '<div>Hello</div>'
        })
        .when(ROUTES.login, {
            templateUrl: getTemplateUrl("login.html"),
            pageName: "Login"
        })
        .when(ROUTES.forgottenPassword, {
            templateUrl: getTemplateUrl("forgotten-password.html"),
            pageName: "Forgotten Password"
        })
        .when(ROUTES.home, {
            templateUrl: getTemplateUrl("home.html"),
            resolve: resolve
        })
        .when(ROUTES.personalDetails, {
            templateUrl: getTemplateUrl("personal-details.html"),
            resolve: resolve
        })
        .when(ROUTES.paymentDetails, {
            templateUrl: getTemplateUrl("payment-details.html"),
            resolve: resolve
        })
        .when(ROUTES.orders, {
            templateUrl: getTemplateUrl("orders.html"),
            resolve: resolve
        })
        .when(ROUTES.subscriptions, {
            templateUrl: getTemplateUrl("subscriptions.html"),
            resolve: resolve
        })
        .when(ROUTES.updatePassword, {
            templateUrl: getTemplateUrl("update-password.html"),
            pageName: "Update Password"
        })
        .when(ROUTES.accountVerification, {
            templateUrl: getTemplateUrl("account-verification.html"),
            pageName: "Account Verification"
        })
        .when(ROUTES.error, {
            templateUrl: getTemplateUrl("error.html"),
            pageName: "Error"
        })
        .when(ROUTES.register, {
            templateUrl: getTemplateUrl("register.html"),
            pageName: "Register"
        })
        .when(ROUTES.profile, {
            templateUrl: getTemplateUrl("profile.html"),
            resolve: resolve,
            pageName: "Profile"
        })
        .when(ROUTES.accountConfirm, {
            templateUrl: getTemplateUrl("accountConfirm.html"),
            pageName: "Registration Complete"
        })
        .when(ROUTES.deleteAccount, {
            templateUrl: getTemplateUrl("deleteAccount.html"),
            resolve: resolve,
            pageName: "Delete Account"
        })
        .otherwise({
            templateUrl: getTemplateUrl("login.html"),
            pageName: "Login"
        });

If it works properly then you have something fishy going on in the StartCtrl controller. If still doesn't work properly, try this:

$routeProvider
        .when('/', {
            template: '<div>Hello</div>'
        })
        .otherwise('/');

If works properly then look for $routeChangeErrorhandlers. If not then might be any router event handler. Here is the list of event for the ngRoute: $routeChangeStart, $routeChangeError, $routeUpdate, $routeChangeSuccess

EDIT:

Since you don't want users to return back to / route when they click go back you can remove it from history. Just call replace method on $location service when you redirect users away from / route. E.g. $location.path('/login').replace().

shrewdbeans
  • 11,971
  • 23
  • 69
  • 115
Alexander Mokin
  • 2,264
  • 1
  • 9
  • 14
  • Thanks Alexander. You're right - the culprit is the StartCtrl controller. In this controller there is some code to check if a user is logged in, and if they are present the login view, if not present a "profile" view. Therefore, when the user lands at `/`, it then presents a new view, updating the URL accordingly, and therefore creating a loop each time the user goes back to the default view at `/`. I don't think I can change this functionality; could you recommend a way around this issue? Let me know if you need any more code examples. – shrewdbeans Aug 14 '17 at 11:34
  • Add the code from startctrl that causes the issue to the question – Alexander Mokin Aug 14 '17 at 11:46
  • This has been added - let me know if you need anything else. – shrewdbeans Aug 14 '17 at 11:55
  • Updated the answer – Alexander Mokin Aug 14 '17 at 13:26
1

You can disable the prepending of the #! by enabling HTML5Mode. I'm not 100% sure if that is compatible with your problem, but this way, opening the Angular app looks like one history-event, instead of two.

Jeff Huijsmans
  • 1,388
  • 1
  • 14
  • 35
  • I've tried this and it does indeed remove the `#!`, but I still have the same issue. Pressing back from the first view in the Angular app just reloads that view, instead of going back to the previous URL. – shrewdbeans Aug 13 '17 at 14:27