ui-router – Stepan Suvorov Blog https://stepansuvorov.com/blog Release 2.0 Thu, 02 Mar 2017 20:22:14 +0000 en-US hourly 1 https://wordpress.org/?v=6.3.1 ui-router: Deprecated events and hooks https://stepansuvorov.com/blog/2016/07/ui-router-deprecated-events-and-hooks/ https://stepansuvorov.com/blog/2016/07/ui-router-deprecated-events-and-hooks/#comments Thu, 28 Jul 2016 19:28:50 +0000 http://stepansuvorov.com/blog/?p=3093 Continue reading ]]> If you are using latest version of ui-router (now it’s 1.0.0-alpha) you probably already noticed that there are no events that we all got used to: $stateChangeCancel,  $stateChangeError, $stateChangeStart$stateChangeSuccess$stateNotFound. All of them are deprecated in 1.0.0-alpha.3 and you can not use them anymore in 1.0.0-alpha.5.

If you want to move to new version of ui-router you should make such changes:

$stateChangeCancel

It was an event that was thrown when somebody called “event.preventDefault()”. Now everything is controlled by transitions so no chance to get such event.

$stateChangeError

old code

[javascript]
$rootScope.$on(‘$stateChangeError’, function(event, toState) {
//some code to handle the error
})
[/javascript]

new code

[javascript]
$transitions.onError({ to: ‘stateName’ }, function($error$) {
console.log($error$);
}
[/javascript]

in progress

$stateChangeStart

old code

[javascript]
$rootScope.$on(‘$stateChangeStart’, function(event, toState) {
event.preventDefault();
})
[/javascript]

new code

[javascript]
$transitions.onEnter({ to: ‘stateName’ }, function($state$, $transition$) {
return $q.reject()
}
[/javascript]

$stateChangeSuccess

old code

[javascript]
$rootScope.$on(‘$stateChangeSuccess’, function(event) {})
[/javascript]

new code

[javascript]
$transitions.onSuccess({}, () => {});
[/javascript]

$stateNotFound

old code

[javascript]
$rootScope.$on(‘$stateNotFound’, function(event) {})
[/javascript]

new code

[javascript]
$stateProvider.onInvalid(($to$, $from$) => {})
[/javascript]

 

Sandbox to play with all examples.

]]>
https://stepansuvorov.com/blog/2016/07/ui-router-deprecated-events-and-hooks/feed/ 4
ui-router: Default child for abstract state https://stepansuvorov.com/blog/2016/05/ui-router-default-child-for-abstract-state/ https://stepansuvorov.com/blog/2016/05/ui-router-default-child-for-abstract-state/#comments Sat, 28 May 2016 15:08:06 +0000 http://stepansuvorov.com/blog/?p=3078 Continue reading ]]> In version 1.0.0alpha0 they finally make it possible! Child for abstract states? No! But now at least it’s possible to create own fix for it due to new $transitionsProvider, in which you could define onBefore hook. You can change the behaviour depends on state options. Let’s use “abstract” property that is boolean and extend it: to make it possible to add child state here:

[javascript]
$transitionsProvider.onBefore({
to: state => !!state.abstract
}, ($transition$, $state) => {
if (angular.isString($transition.to().abstract)) {
return $state.target($transition.to().abstract);
}
});
[/javascript]

basically if abstract param is a string we set it like a target. Example of use:

[javascript]
.state({
name: ‘abstract2’,
url: ‘/abstract2’,
abstract: ‘abstract2.foo’, // redirect to ‘abstract2.foo’
template: ‘abstract2’
})
[/javascript]

to cover more complex case we could set it like a function:

[javascript]
$transitionsProvider.onBefore({
to: state => !!state.abstract
}, ($transition$, $state, $injector) => {
if (angular.isFunction($transition.to().abstract)) {
return $state.target($injector.invoke($transition.to().abstract))
}
});
[/javascript]

and an example for an abstract state with child that is defined by function:

[javascript]
.state({
name: ‘abstract3’,
url: ‘/abstract3’,
abstract: function() {
return ‘abstract3.foo’
}, // Function redirect
template: ‘abstract3’
})
[/javascript]

All code for experiments is available on plunker.

]]>
https://stepansuvorov.com/blog/2016/05/ui-router-default-child-for-abstract-state/feed/ 1
How to catch Angular ui-router resolve errors https://stepansuvorov.com/blog/2015/07/how-to-catch-angular-ui-router-resolve-errors/ https://stepansuvorov.com/blog/2015/07/how-to-catch-angular-ui-router-resolve-errors/#comments Thu, 09 Jul 2015 10:04:23 +0000 http://stepansuvorov.com/blog/?p=2737 Continue reading ]]> It’s more than annoying to have just a blank page and no errors in console when your issue is inside ui-router resolve. Finally I found a pretty nice solution, don’t know why it’s not provided “from the box”. In case of error ui-router sends specific event $stateChangeError that we could listen for:

[javascript]
$rootScope.$on(‘$stateChangeError’, function(event, toState, toParams, fromState, fromParams, error){
console.error(error);
});
[/javascript]

and now wow! we have an exact error in console and no need to guess what’s wrong with your code anymore.

]]>
https://stepansuvorov.com/blog/2015/07/how-to-catch-angular-ui-router-resolve-errors/feed/ 2
ui-router debug snippet https://stepansuvorov.com/blog/2015/04/ui-router-debug/ https://stepansuvorov.com/blog/2015/04/ui-router-debug/#respond Mon, 13 Apr 2015 14:41:01 +0000 http://stepansuvorov.com/blog/?p=2531 Continue reading ]]> found nice code snippet that could help you to debug ui-router issues by loggin router events in console:

[javascript]
// Credits: Adam`s answer in http://stackoverflow.com/a/20786262/69362
var $rootScope = angular.element(document.querySelectorAll(‘[ui-view]’)[0]).injector().get(‘$rootScope’);

$rootScope.$on(‘$stateChangeStart’,function(event, toState, toParams, fromState, fromParams){
console.log(‘$stateChangeStart to ‘+toState.to+’- fired when the transition begins. toState,toParams : \n’,toState, toParams);
});

$rootScope.$on(‘$stateChangeError’,function(event, toState, toParams, fromState, fromParams){
console.log(‘$stateChangeError – fired when an error occurs during transition.’);
console.log(arguments);
});

$rootScope.$on(‘$stateChangeSuccess’,function(event, toState, toParams, fromState, fromParams){
console.log(‘$stateChangeSuccess to ‘+toState.name+’- fired once the state transition is complete.’);
});

$rootScope.$on(‘$viewContentLoaded’,function(event){
console.log(‘$viewContentLoaded – fired after dom rendered’,event);
});

$rootScope.$on(‘$stateNotFound’,function(event, unfoundState, fromState, fromParams){
console.log(‘$stateNotFound ‘+unfoundState.to+’ – fired when a state cannot be found by its name.’);
console.log(unfoundState, fromState, fromParams);
});
[/javascript]

if you have you Angular app root on html element you can simply use:

[javascript]
var $rootScope = angular.element(document).scope();
[/javascript]

code is placed here.

]]>
https://stepansuvorov.com/blog/2015/04/ui-router-debug/feed/ 0
Авторизация AngularJS. Right way. https://stepansuvorov.com/blog/2014/08/%d0%b0%d0%b2%d1%82%d0%be%d1%80%d0%b8%d0%b7%d0%b0%d1%86%d0%b8%d1%8f-angularjs-right-way/ https://stepansuvorov.com/blog/2014/08/%d0%b0%d0%b2%d1%82%d0%be%d1%80%d0%b8%d0%b7%d0%b0%d1%86%d0%b8%d1%8f-angularjs-right-way/#comments Fri, 15 Aug 2014 18:47:29 +0000 http://stepansuvorov.com/blog/?p=1622 Continue reading ]]>

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

UPD: Статья обновлена и дополнена 2017-02-21

Отдельный модуль

Так как я сторонник модульного подхода и считаю, что именно это правильный способ использования AngualrJS, то весь функционал связанный с авторизацией я сложил в отдельный модуль auth. К нему подключаю модуль управления кукисами и модуль организации удобной работы с REST(restangular):

[javascript]angular.module(‘auth’, [‘ngCookies’, ‘restangular’]);[/javascript]

Сервис авторизации

Понятное дело, что какой бы вы самокат не делали, без сервиса не обойтись. Что в нем должно находиться? Делаем REST/token вариант авторизации, поэтому в сервисе будет следующий функционал:

  • авторизация по логину (асинхронная операция: POST запрос и получение/сохранения токена)
  • авторизация по токену (асинхронная операция: GET запрос и получение информации по пользователю)
  • проверка статуса (синхронная операция)
  • логаут (зачистка данных авторизации – токена)

Вот упрощенная версия кода(выкинул обработку ошибок):

[javascript]angular.module(‘auth’)
.service(‘AuthService’, function($cookies, $http, Restangular) {
‘use strict’;

var self = this;
this.status = {
authorized: false,
};

this.loginByCredentials = function(username, password) {
return Restangular.all(‘sessions’).post({ email: username, password: password })
.then(function(response) {
return self.loginByToken(response.contents);
});
};

this.loginByToken = function(token) {
$http.defaults.headers.common[‘X-Token’] = token;

return Restangular.all(‘sessions’).get(token)
.then(function(response) {
$cookies.accessToken = token;
self.status.authorized = true;
return response;
});
};

this.logout = function() {
self.status.authorized = false;
$cookies.accessToken = ”;

Restangular.all(‘sessions’).remove();
};
});[/javascript]

Приходится подключать $http сервис для вот этого хака:

[javascript]$http.defaults.headers.common[‘X-Token’] = token;[/javascript]

– по другому в Restangular не получается динамически задать хедер, в котором мы хотим отправлять токен авторизации.

(если у вас есть другое решение данной ситуации – пожалуйста поделитесь)

Страница авторизации

Если вы используете стандартный ангуляровский роутер, тут может возникнуть вопрос: Как для авторизации показывать другой лэйаут?

Решение подсказал Валентин Шибанов, который предложил завернуть все содержимое(в том числе ui-view) в ng-if, который бы в зависимости от того, авторизираван пользователь или нет, показывал бы либо содержимое с меню либо формочку авторизации.

Итого имеем(опять очень упрощенная версия):

[html]
<div ng-if="isAutorized">
<menu></menu>
<ui-view></ui-vew>
<div ng-if="!isAutorized">
<login></login>
</div>
[/html]

Плюшки с интерсепторами

Если переодически пропадает сессия с сервером(таймаут), но клиент остается активным, и вы хотите восстановить сессию без привлечения пользователя, можно посмотреть в сторону интерсепторов. Так же интерсепторы можно использовать для общей обработки ошибок авторизации.

Момент определения состояния авторизации и ограничение доступа

Давайте разберемся, в какой момент мы должны/можем проверять состояние авторизации. Все варианты можно условно разбить на 2:

  • ответ API (мы узнали от сервера)
  • изменение состояния (перешли на другую страничку)
    • событие изменение состояния
    • резолв конкретного стейта

Давайте посмотрим на все случаи.

Ответ от API и интерсептор для обратки ошибки

Про интерсепторы можно более подробно почитать тут. Мы с вами реализуем один из них интесептор ошибки ответа:

[javascript]
app.service(‘authRejector’, function($q) {
this.responseError = (rejection) => {
if (rejection.status === 401) {
//делаем какие-то действия для пользователя без авторизации
}

return $q.reject(rejection);
};
});
[/javascript]

и потом подключаем наш интерсептор к остальным:

[javascript]
app.config(function($httpProvider) {
$httpProvider.interceptors.push(‘authRejector’);
});
[/javascript]

Изменение стейта

Навесить хук в событие ui-router можно с помощью сервиса $transitions:

[javascript]
$transitions.onEnter({ to: ‘stateName’ }, function($state$, $transition$) {
if(!AuthService.status.authorized){
//делаем какие-то действия для пользователя без авторизации
return $q.reject()
}

}
[/javascript]

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

Давайте предлополжим, что у нас есть стейт users:

[javascript]
.state(‘users’, {})
[/javascript]

к которому мы хотим ограничить доступ. Для этого добавим резолв с использованием все того же сервиса авторизации:

[javascript]
.state(‘users’, {
resolve: {
auth: function($q, AuthService) {
if(!AuthService.status.authorized) {
$q.reject();
alert(‘Вы должны авторизироваться!’);
}
}
}
})
[/javascript]

Проверка авторизации при переходе на другую страницу

В ui-router мы можем навесить хук на событие перехода на какой либо стейт с помощью сервиса $transitions:

[javascript]
$transitions.onEnter({ to: ‘stateName’ }, function($state$, $transition$) {
if(!AuthService.status.authorized){
//делаем какие-то действия для пользователя без авторизации
return $q.reject()
}
}
[/javascript]

Но у приведем сам логин компонент для полноты примера.

Пример логин компонента

[javascript]
app.component(‘login’, {
controller: function(AuthService){
this.login = function(login, password)
AuthService.loginByCredentials(login, password).catch(function(){
//выводим ошибку авторизации
});
}
});
[/javascript]

 

]]>
https://stepansuvorov.com/blog/2014/08/%d0%b0%d0%b2%d1%82%d0%be%d1%80%d0%b8%d0%b7%d0%b0%d1%86%d0%b8%d1%8f-angularjs-right-way/feed/ 20