Давно полюбив Angular, я только сейчас добрался до блога автора фреймворка Dan Wahlin. А зря — там можно найти много интересных и нужных каждому приложению мыслей. Одну из них я и перевёл в этой статье. В ней вы узнаете как можно остановить переход на другую страницу в приложении Angular.
Словарь переводчика:
— 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 и показывает предупреждающее пользователя диалоговое окно:
Вот код функции 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