Запретить пользователю покидать маршрут в одностраничном приложении с помощью hashbang

Я использую Sammy.js для своего одностраничного приложения. Я хочу создать функциональность, аналогичную SO (тот, когда вы вводите свой вопрос и пытаетесь покинуть страницу, и он спрашивает вас, уверены ли вы).

Если бы это не было одностраничным приложением, я бы просто сделал что-то вроде:

$(window).bind('beforeunload', function(){
  return 'Are you sure you want to leave?';
});

Проблема в том, что в одностраничном приложении пользователь фактически не покидает страницу, а скорее меняет свой document.location.hash (он может покинуть страницу, закрыв ее). Есть ли способ сделать что-то подобное для SPA, желательно с sammy.js?


person Salvador Dali    schedule 30.05.2014    source источник
comment
Вы хотите показать это приглашение в любом коде, обрабатывающем изменения страницы в вашей структуре.   -  person ThiefMaster    schedule 30.05.2014


Ответы (1)


У нас была аналогичная проблема, которую нужно было решить в нашем одностраничном веб-приложении на моей работе. У нас были некоторые страницы, которые могли быть грязными, и если бы это было так, мы хотели предотвратить переход с этой страницы до тех пор, пока пользователь не подтвердит, что это нормально. Поскольку мы хотели предотвратить навигацию, мы не могли прослушивать событие onhashchange, которое запускается после изменения хеша, а не до этого. Поэтому мы решили переопределить LocationProxy по умолчанию, включив в него логику, которая позволяла нам дополнительно предотвращать навигацию до изменения местоположения.

Имея это в виду, вот прокси, который мы использовали:

PreventableLocationProxy = (function () {
    function PreventableLocationProxy(delegateProxy, navigationValidators) {
        /// <summary>This is an implementation of a Sammy Location Proxy that allows cancelling of setting a location based on the validators passed in.</summary>
        /// <param name="delegateProxy" type="Sammy.DefaultLocationProxy">The Location Proxy which we will delegate all method calls to.</param>
        /// <param name="navigationValidators" type="Function" parameterArray="true" mayBeNull="true">One or more validator functions that will be called whenever someone tries to change the location.</param>
        this.delegateProxy = delegateProxy;
        this.navigationValidators = Array.prototype.slice.call(arguments, 1);
    }

    PreventableLocationProxy.prototype.bind = function () {
        this.delegateProxy.bind();
    };

    PreventableLocationProxy.prototype.unbind = function () {
        this.delegateProxy.unbind();
    };

    PreventableLocationProxy.prototype.getLocation = function () {
        return this.delegateProxy.getLocation();
    };

    PreventableLocationProxy.prototype.setLocation = function (new_location) {
        var doNavigation = true;
        _.each(this.navigationValidators, function (navValidator) {
            if (_.isFunction(navValidator)) {
                // I don't just want to plug the result of the validator in, it could be anything!
                var okayToNavigate = navValidator(new_location);
                // A validator explicitly returning false should cancel the setLocation call. All other values will
                // allow navigation still.
                if (okayToNavigate === false) {
                    doNavigation = false;
                }
            }
        });
        if (doNavigation) {
            return this.delegateProxy.setLocation(new_location);
        }
    };

    return PreventableLocationProxy;
}());

Этот код довольно прост сам по себе, это объект javascript, который принимает прокси делегата, а также одну или несколько функций проверки. Если какой-либо из этих валидаторов явно возвращает false, навигация невозможна, и местоположение не изменится. В противном случае навигация разрешена. Чтобы это работало, нам пришлось переопределить стандартную обработку кликов наших тегов привязки, чтобы направить ее через Sammy.Application.setLocation. Однако после того, как это было сделано, это позволило нашему приложению обрабатывать логику грязной страницы.

На всякий случай вот наш валидатор грязной страницы:

function preventNavigationIfDirty(new_location) {
    /// <summary>This is an implementation of a Sammy Location Proxy that allows cancelling of setting a location based on the validators passed in.</summary>
    /// <param name="new_location" type="String">The location that will be navigated to.</param>
    var currentPageModels = [];
    var dirtyPageModels = [];
    //-----

    // Get the IDs of the current virtual page(s), if any exist.
    currentPageModels = _.keys(our.namespace.currentPageModels);

    // Iterate through all models on the current page, looking for any that are dirty and haven't had their changes abored.
    _.forEach(currentPageModels, function (currentPage) {
        if (currentPage.isDirty() && currentPage.cancelled === false) {
            dirtyPageModels.push(currentPage);
        }
    });

    // I only want to show a confirmation dialog if we actually have dirty pages that haven't been cancelled.
    if (dirtyPageModels.length > 0) {
        // Show a dialog with the buttons okay and cancel, and listen for the okay button's onclick event.
        our.namespace.confirmDirtyNavigation(true, function () {
            // If the user has said they want to navigate away, then mark all dirty pages with the cancelled
            // property then do the navigating again. No pages will then prevent the navigation, unlike this
            // first run.

            _.each(dirtyPageModels, function (dirtyPage) {
                dirtyPage.cancelled = true;
            });

            our.namespace.sammy.setLocation(new_location);
        });

        // Returns false in order to explicitly cancel the navigation. We don't need to return anything in any
        // other case.
        return false;
    }
}

Помните, что это решение не будет работать, если пользователь явно изменит местоположение, но это не тот вариант использования, который мы хотели поддерживать. Надеюсь, это приблизит вас к собственному решению.

person Smartboy    schedule 30.05.2014