2013-10-02 5 views
17

Próbuję zaimplementować system sprawdzania autentyczności użytkownika i systemu kontroli dostępu w mojej aplikacji, ale nadal trafiam na blokady dróg. Myślę, że tym razem mam to we właściwy sposób, ale mam ostatnią przeszkodę.

Nieco tła: próbowałem umieścić cały kod w $ rootScope.on ($ startChangeStart) i zadziałało, strasznie ... ale zadziałało. Trasa zawsze była przekierowywana, ale z powodu auth sprawdzenia w backendach wyświetlała pierwszą stronę żądania przez 1/2 sekundy, a następnie za każdym razem stronę przekierowania. Próbowałem więc wstrzymać ładowanie strony, wywołując funkcję evt.preventDefault() na samym początku funkcji $ startChangeStart, która działała, ale próba przeniesienia użytkownika na pierwotną trasę spowodowała nieskończoną pętlę w routerze.

Po więcej badań i przeczytaniu wielu postów na stosach jestem pewien, że "postanowienie:" jest właściwym miejscem do sprawdzenia auth, aby upewnić się, że strona nie ładuje się podczas jej występowania, a następnie przekierować użytkownika, jeśli potrzebne od $ startChangeStart. (Stan i wydarzenie $ są zawsze niezdefiniowane w moich próbach wprowadzenia ich do funkcji rozstrzygania). Wygląda to na zwycięską kombinację.

Mój problem: Mam postanowienie w sprawie stanu głównego w moim app: „main”

To było uniknąć redundancji kodu, jednak nie mogę określić, w jaki sposób uzyskać dostęp do właściwości Państwowej root, a zatem wynik resolve , z $ stateChangeStart. ToState jest stanem podrzędnym, podczas gdy parametr fromState jest stanem poprzednim lub stanem abstrakcyjnym z trasą "^" ...

Czy muszę rozwiązać każdy stan dziecka, aby to zadziałało, lub czy istnieje sposób na uzyskanie dostępu do stanu root z tego punktu?

Podstawowa aplikacja setup:

angular.module('App', ['ui.router', 'ui.bootstrap', 'ui.event', 'AngularGM', 'ngResource']) 
.config(['$urlRouterProvider', '$stateProvider', function($urlRouterProvider, $stateProvider){ 
    $urlRouterProvider 
     .when('/home', '/') 
     .when('', '/') 
     .when('/sign-up/joe', '/sign-up') 
     .otherwise('/'); 

    $stateProvider 
     .state('main', { 
      url: '', 
      abstract: true, 
      templateUrl: 'views/main.html', 
      controller: 'MainCtrl', 
      resolve: { 
       checkAccess: ['accountService', function(accountService) { 
        accountService.checkAuth(function(){ 
         accountService.checkAccess(function (access){ 
          return access; 
         }); 
        }); 
       }] 
      } 
     }) 
     .state('main.home', { 
      url: '', 
      abstract: true, 
      templateUrl: 'views/home.html', 
      controller: 'HomeCtrl' 
     }) 
     .state('main.home.index', { 
      url: '/', 
      templateUrl: 'views/home/index.html' 
     }); 


.run(['$rootScope', '$state', '$stateParams', 'accountService', function ($rootScope, $state, $stateParams) { 
    $rootScope.$state = $state; 
    $rootScope.$stateParams = $stateParams; 
    $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) { 
     console.dir(toState); 
     console.dir(toParams); 
     console.dir(fromState); 
     console.dir(fromParams); 
     if (!toState.checkAccess.allowed) { 
      event.preventDefault(); 
      $state.transitionTo(toState.checkAccess.newState); 
     } 
    }); 
}]); 

Jest to wyjście z console.dir() zwraca się z dwóch obiektów państwowych:

Object 
name: "main.home.index" 
templateUrl: "views/home/index.html" 
url: "/" 
__proto__: Object 

Object 
controller: "PlacesCtrl" 
name: "main.places.search" 
templateUrl: "views/places.html" 
url: "/places" 
__proto__: Object 

Aktualizacja

Ups, zapomniałem wspomnieć o wersji AngularJS v1.2.0-rc.2

$ state.current console.dir()

Object 
abstract: true 
name: "" 
url: "^" 
views: null 
__proto__: Object 

Odpowiedz

7

Tak, wierzę, że można uzyskać dostęp do stanu głównego z funkcją $stateChangeStart.

Przy stosowaniu czystych angularjs normalnie używam current.$$route

na przykład, stosując następującą trasą

.when('/home', { 
    title:'Home', 
    bodyClass: 'meetings', 
    controler: 'HomeCtrl' 
}) 

mogę uzyskać dostęp do stanu korzeni jak tak

$rootScope.$on('$routeChangeSuccess', function (event, current, previous) { 

    if (current.$$route) { 

    $rootScope.title  = current.$$route.title; 

    $rootScope.bodyClass = current.$$route.bodyClass; 
    } 

}); 

Korzystanie ui-router to tylko trochę inaczej, jak się nazywa $state.current. Możesz uzyskać dostęp do wszystkich właściwości powiązanych z dowolną trasą (np .: $ state.current.url)

więc na kodzie można mieć coś takiego

.run(['$rootScope', '$state', '$stateParams', function ($rootScope, $state, $stateParams) { 

     $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) { 
      console.log($state.current.url); 
     }); 
    }]); 
+1

Po prostu zrobiłem plik console.dir na obiekcie $ state.current w $ stateChangeStart i został on teraz dodany. To abstrakcyjny obiekt. Jeśli przeniesię prace do innej funkcji i użyję stanu $ z wewnątrz, to stanem $ będzie obiekt podrzędny .. ale powinien istnieć sposób dostępu do niego bez tworzenia dodatkowych linii dla funkcji ... nie? Ponadto, to nadal nie daje mi dostępu do stanu root, który zawiera postanowienie. – Routhinator

+1

Czy udało Ci się dowiedzieć, jak uzyskać dostęp do danych rozstrzygnięcia z poziomu stateChangeStart? – Yavar

+2

Miałem wrażenie, że dane rozstrzygnięcia zostały "rozwiązane" po zdarzeniu stateChangeStart ... ie. jeśli wywołasz 'event.preventDefault()', wynik nigdy się nie rozpocznie. Może się mylę. –

0

jest to możliwe do zrobienia przekierowanie z poziomu accountService? Jeśli wykryjesz, że użytkownik nie spełnia Twoich funkcji checkAuth lub checkAccess, możesz uniemożliwić wywołanie zwrotne i przekierować użytkownika na stronę błędu (lub logowania).

Coś innego do rozważenia to implementacja jakiejś zmiennej/kolejki stanów, jeśli chcesz przekierować kogoś na stronę logowania, aby odświeżyć autoryzację/uwierzytelnienie, a następnie powrócić do poprzedniego stanu.

1

Nie musisz używać resolve. Spójrz na moje rozwiązanie:

app.run ($rootScope, $state, Auth, ngDialog) -> 
    $rootScope.$on '$stateChangeStart', (e, to) -> 
    if to.authRequired and not Auth.isAuthenticated() 
     e.preventDefault() 
     Auth.currentUser().then(
     (user) -> 
      $state.go(to) 
     (failure) -> 
      $rootScope.storedState = to 
      $state.go('main') 
      ngDialog.closeAll() 
      ngDialog.open 
      template: 'modals/login.html' 
      controller: 'loginCtrl' 
      className: 'ngdialog-theme-default' 
    ) 

używam angular_devise i ngDialog ale są opcjonalne i można je realizować z obsługą swojego własnego użytkownika.

0

Jeśli zainicjujesz swój stan za pomocą domyślnego, pustego obiektu po rozwiązaniu, będziesz mógł nim manipulować w ramach $stateChangeStart.

$stateProvider 
    .state 'home', 
    url: "/" 
    resolve: {} 

... 

    $rootScope.$on '$stateChangeStart', (e, toState, toParams, fromState, fromParams) -> 
    toState.resolve.x = -> 
     $timeout -> 
     alert "done" 
     , 3000 

Zobacz https://github.com/angular-ui/ui-router/issues/1165

0

Ta odpowiedź jest bardzo późno, ale może być przydatna.

Zdarzenia stanu i zdarzenia $stateChangeStart są wykonywane w tym samym czasie. Do czasu próby uzyskania dostępu do rozwiązanych danych w numerze $stateChangeStart nie będzie dostępna, ale będzie dostępna po wywołaniu zdarzenia $stateChangeSuccess.

Jeśli używasz $stateChangeStart, musisz wykonać checkAuth z dwóch miejsc $stateChangeStart zdarzenia i głównej decyzji. Ponieważ mają one równoległe wykonanie, do serwera wysyłane są co najmniej 2 żądania sieciowe dla tych samych danych.

Zamiast tego użyj $stateChangeSuccess. Użycie tego spowoduje, że twoje rozstrzygnięcia zostaną rozwiązane, a następnie możesz sprawdzić dostęp. Ponadto, zamiast uzyskiwać dostęp do rozstrzygniętych właściwości, można uzyskać dostęp do danych rozwiązanych za pomocą usługi kątowej.