【问题标题】:AngularJS app breaks the back button when used for a single page application within another website当用于另一个网站中的单页应用程序时,AngularJS 应用程序会破坏后退按钮
【发布时间】:2017-07-27 07:18:26
【问题描述】:

注意:我使用的是 AngularJS v1.6.0

我已经在另一个现有网站中实现了一个独立的 AngularJS 应用程序,它存在于该网站的单个页面上,并且该网站的其余部分没有运行任何 Angular 代码。

例如,网站上的常规页面可能位于:

http://example.com/any-page

然后用户可以点击一个链接,它会将他们带到运行 Angular JS 的页面:

http://example.com/angularjs-app

当登陆此 URL 时,它会加载 AngularJS 应用程序并按预期将 #!/ 附加到 URL。它不包含站点其余部分的任何元素,例如标题,因此对于用户来说,它看起来像是一个完全不同的部分。但是,它也破坏了后退按钮。无法按返回返回http://example.com/any-page。每次您按下返回时,它只是再次加载 AngularJS 应用程序的登陆视图 - 实际上用户卡在 AngularJS 应用程序页面上,无法返回到/any-page

我认为这与 AngularJS 路由有关,因为当您按下返回时,它似乎会刷新 URL 的 #!/ 部分,然后重新加载应用程序上的初始视图。但我可能是错的。

请注意,在访问各种路线时,后退按钮在 AngularJS 应用程序中可以正常工作。例如,如果我在应用程序中的各种路线/视图之间导航,例如#!/login#!/view-details,我总是可以通过后退按钮返回这些视图。但是当它到达初始视图时,它会停止工作。

有人知道解决这个问题的方法吗?

注意:我已经查看了关于此的各种其他 Stack Overflow 帖子,但是他们似乎都关心后退按钮根本不起作用,而不是后退按钮用于在路线之间导航的问题应用程序,但不会返回网站上的原始非 AngularJS 页面。

路由配置

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

ng-view 所在的索引部分:

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

【问题讨论】:

  • 你用什么路由到外部 url 的?
  • 您有更多详细信息吗?我创建了一个简单的测试,一切正常。路由是如何配置的?这个不工作的演示将有助于理解在这种情况下发生了什么
  • 谢谢@Brian,我已经添加了我的路由配置,如果你还需要什么,请告诉我。如果可能的话,你能把你的简单测试添加到某个地方让我看看吗?它可能有助于了解我的问题的原因。

标签: javascript angularjs


【解决方案1】:

这不是 AngularJS 路由器的问题(虽然可能是特定版本的错误,所以请在问题中添加有关版本的信息)。但很可能您在路由器事件的处理程序或StartCtrl 中有重定向。

AngularJS 教程中的示例运行良好,您可以在此处获取:

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

要本地化问题,我会首先尝试使用以下配置运行您的应用:

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

如果它工作正常,那么你在 StartCtrl 控制器中有一些可疑的事情发生。如果仍然不能正常工作,试试这个:

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

如果工作正常,然后寻找$routeChangeErrorhandlers。如果不是,那么可能是任何路由器事件处理程序。以下是 ngRoute 的事件列表:$routeChangeStart$routeChangeError$routeUpdate$routeChangeSuccess

编辑:

由于您不希望用户在点击返回时返回到/ 路由,您可以将其从历史记录中删除。当您将用户重定向到 / 路由时,只需在 $location 服务上调用 replace method。例如。 $location.path('/login').replace()

【讨论】:

  • 谢谢亚历山大。你是对的 - 罪魁祸首是 StartCtrl 控制器。在这个控制器中,有一些代码可以检查用户是否登录,如果他们存在登录视图,如果不存在“个人资料”视图。因此,当用户到达/ 时,它会呈现一个新视图,相应地更新 URL,因此每次用户返回到 / 的默认视图时都会创建一个循环。我认为我无法更改此功能;你能推荐一个解决这个问题的方法吗?如果您需要更多代码示例,请告诉我。
  • 将导致问题的 startctrl 代码添加到问题中
  • 已添加 - 如果您需要其他任何内容,请告诉我。
  • 更新了答案
【解决方案2】:

您可以通过启用HTML5Mode 来禁用#! 的前置。我不能 100% 确定这是否与您的问题兼容,但这样一来,打开 Angular 应用程序看起来就像一个历史事件,而不是两个。

【讨论】:

  • 我试过这个,它确实删除了#!,但我仍然有同样的问题。从 Angular 应用中的第一个视图按下返回只会重新加载该视图,而不是返回到之前的 URL。
猜你喜欢
  • 2023-03-20
  • 1970-01-01
  • 2015-11-04
  • 1970-01-01
  • 2015-03-04
  • 1970-01-01
  • 2014-11-29
  • 1970-01-01
  • 2011-07-07
相关资源
最近更新 更多