Отмена изменения пути в AngularJS

в 15:18, , рубрики: AngularJS, javascript, route, отмена, переход, Программирование

Давно полюбив Angular, я только сейчас добрался до блога автора фреймворка Dan Wahlin. А зря — там можно найти много интересных и нужных каждому приложению мыслей. Одну из них я и перевёл в этой статье. В ней вы узнаете как можно остановить переход на другую страницу в приложении Angular.

Отмена изменения пути в AngularJS


Словарь переводчика:
— view — шаблон
— route — маршрут, путь
— navigation — переход

Маршрутизация является отличным средством, которое позволяет с наименьшим количеством необходимого кода связать шаблоны с контроллерами AngularJS.
В то время как пользователь может переходить прямо к заданному пути, существует возможность того, что он инициирует переход до завершения какого-нибудь важного действия (например сохранение данных). В таких ситуациях Вы можете захотеть отменить переход и спросить пользователя, хочет ли он закончить свою работу или предупредить о потере его данных. В этой статье я хочу поговорить о приёме, который может быть использован для реализации такого задания.

Событие $locationChangeStart

Когда происходит переход в приложении AngularJS возникает несколько событий. Одно из них называется $locationChangeStart, а другое — $routeChangeStart (на самом деле существует ещё несколько). В настоящее время (версия 1.2) событие $routeChangeStart не предоставляет возможности отменить переход, зато $locationChangeStart вполне пригоден для этого. Если Вы посмотрите на код AngularJS, то найдёте следующий участок, который показывает возникновение события $locationChangeStart при вызове метода onUrlChange() объекта $browser:

$browser.onUrlChange(function (newUrl) {
    if ($location.absUrl() != newUrl) {
        if ($rootScope.$broadcast('$locationChangeStart', newUrl, 
            $location.absUrl()).defaultPrevented) {
            $browser.url($location.absUrl());
            return;
        }
        $rootScope.$evalAsync(function () {
            var oldUrl = $location.absUrl();

            $location.$$parse(newUrl);
            afterLocationChange(oldUrl);
        });
        if (!$rootScope.$$phase) $rootScope.$digest();
    }
});

Самая важная часть кода — это вызов $broadcast. Этот вызов передаёт событие $locationChangeStart всем дочерним scope, благодаря чему они могут быть оповещены о смене location. Чтобы обработать это событие, Вы можете использовать функцию $rootScope.on(). Для примера я добавил обращение к $on() в функцию, которая вызывается сразу после запуска контроллера:

 function init() {
    
    //initialize data here..
    //Make sure they're warned if they made a change but didn't save it
    //Call to $on returns a "deregistration" function that can be called to
    //remove the listener (see routeChange() for an example of using it)
    onRouteChangeOff = $rootScope.$on('$locationChangeStart', routeChange);
}

Этот код ожидает событие $locationChangeStart и вызывает функцию routeChange() при его возникновении. Возвращаемым значением функции $on является функция «отмены ожидания», которая может быть вызвана для того, чтобы прекратить ожидание события. В этом примере она называется onRouteChangeOff (доступна в контроллере). Очень скоро вы увидите как она используется.

Отмена перехода по маршруту

Callback-функция routeChange() срабатывает благодаря событию $locationChangeStart и показывает предупреждающее пользователя диалоговое окно:
Отмена изменения пути в AngularJS

Вот код функции routeChange():

function routeChange(event, newUrl) {
    //Navigate to newUrl if the form isn't dirty
    if (!$scope.editForm.$dirty) return;

    var modalOptions = {
        closeButtonText: 'Cancel',
        actionButtonText: 'Ignore Changes',
        headerText: 'Unsaved Changes',
        bodyText: 'You have unsaved changes. Leave the page?'
    };

    modalService.showModal({}, modalOptions).then(function (result) {
        if (result === 'ok') {
            onRouteChangeOff(); //Stop listening for location changes
            $location.path(newUrl); //Go to page they're interested in
        }
    });

    //prevent navigation by default since we'll handle it
    //once the user selects a dialog option
    event.preventDefault();
    return;
}

В параметрах этой функции Вы можете заметить объект события и новый маршрут, по которому пользователь пытается перейти. Так как до смены шаблона нужно напомнить пользователю о потере данных, объект события используется для отмены перехода. Обратите внимание на вызов event.preventDefault() в конце функции.
Диалоговое окно показано вызовом функции modalService.showModal() (взгляните на мой предыдущий пост о персонализированном modalService который работает обёрткой вокруг $modal сервиса из Angular UI Bootstrap).
Если пользователь выберет «Ignore Changes» то его изменения будут отменены и приложение перейдёт по необходимому маршруту. Это произойдёт благодаря отключению ожидания события $locationChangeStart вызовом функции onRouteChangeOff() (вспомните, что эта функция возвращена вызовом функции $on), так что мы не застрянем в бесконечном цикле появления диалогового окна при клике на кнопку «Ignore Changes».
Затем вызывается $location.path(newUrl) для обработки перехода к заданному шаблону. Если пользователь отменит действие, он останется на текущем шаблоне.

Выводы

Ключ к отмене перехода — это понимание принципа работы с событием $locationChangeStart.
Я надеюсь в будущем подобное задание можно будет решить и с помощью события $routeChangeStart, но на данный момент приведённый код выполняет эту работу в полном объёме.
Вы можете увидеть пример работы этого кода в приложении управления клиентами, которое доступно на Github (особенно шаблон customerEdit). Изучайте больше о приложении тут.

Автор: valkiriy

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js