javascript - promesas - return promise angular 6




$ state.go no funciona de la promesa (2)

¿Por qué no usa una función de resolución para verificar si el usuario puede acceder a su ruta?
Algo como esto:

autoExecApp.config(['$stateProvider', '$urlRouterProvider', function (
    $stateProvider, $urlRouterProvider) {
    $stateProvider
        .state('index', {
            url: '/index',
            templateUrl: 'partials/index.html'
        })
        .state('permission', {
            url: '/permission',
            templateUrl: 'partials/permissions.html'
        })
    .state('restricted-route', {
      url: '/restricted',
      templateUrl: 'partials/restricted.html',
      resolve: {
        authenticated: function ($q) {
          var deferred = $q.defer();
          $userRoles.hasPermissions(this)
            .then(function (data) {
              var result = data === "true";
              if (result) {
                deferred.resolve();
              } else {
                deferred.reject('unauthorized');
              }
            });
          return deferred.promise;
        }
      }
    });
}]);

$rootScope.$on('$stateChangeError',
  function (event, toState, toParams, fromState, fromParams, rejection) {

    if (rejection.error === 'unauthorized') {
      $state.go('permission');
    }
  }
}

Estoy tratando de implementar una verificación de seguridad básica para evitar que los usuarios accedan a ciertos estados dependiendo de su conjunto de permisos:

'use strict';

var autoExecApp = angular.module("myApp", ['ui.router']);

autoExecApp.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider){
    $stateProvider
        .state('index', {
            url: '/index',
            templateUrl: 'partials/index.html'
        })
        .state('permission', {
            url: '/permission',
            templateUrl: 'partials/permissions.html'
        });
}]);

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

    $rootScope.$on('$stateChangeStart', function (event, toState, toParams) {

        event.preventDefault(); // prevent page from being loaded anyway
        $userRoles.hasPermissions(toState.name).then(function(data) {
            var result = data === "true";
            if (!result) {
                $state.go('permission');
            }
            else {
                $state.go(toState.name, toParams ,{notify:false});
            }
        });
    });
}]);

Ahora el problema está relacionado con el hecho de que mi cambio de estado ocurre dentro de una promesa. Me encantaría hacerlo sincrónico, como debería ser en este caso, pero no estoy seguro de por qué no funciona de forma asíncrona.

Si muevo el event.preventDefault() dentro de la promesa, funciona. PERO el estado cambia en 2 pasos. Primero, siempre irá a la página original y luego lo hará poco después de la transición a la página 'bloqueada' (si fue bloqueada). Entiendo que esto se debe a que regresa con éxito una vez desde $ on y actualiza el estado, y luego lo actualiza nuevamente cuando regresa de la promesa.

Si la promesa es cómo la ves, el estado no cambiará. Todo parece estar funcionando, pero la página no actualizará la vista. No estoy seguro de por qué este es el caso. Por alguna razón, si el evento no se previene, el cambio de estado posterior funcionará. Sin embargo, si se evita y se llama a $ state.go a partir de la promesa devuelta, parece ser demasiado tarde para actualizar $ state.

EDITAR: Tuve éxito usando la solución de transmisión y así era como se veía:

event.preventDefault();
$state.go(toState.name, toParams, {notify: false}).then(function() {
     // line 907 state.js
     $rootScope.$broadcast('$stateChangeSuccess', toState, toParams, fromState, fromParams);
});

Aparentemente debido a que está en una promesa, el preventDefault (); suprime este '$ stateChangeSuccess' del disparo natural. Realizarlo manualmente restaura el comportamiento esperado.


Creo que la solución de "transmisión" de Vladimir Gurovich es el camino a seguir, especialmente porque indica que se trata de una solución establecida.

Pero me pregunto si una solución en la que los permisos se almacenan en caché y luego se busca de forma sincronizada , también podría ser viable.

autoExecApp.run(['$rootScope', '$state', '$userRoles', function($rootScope, $state, $userRoles) {
    // Create a permissions cache, seeded with 
    // permission to go to the 'permission' state.
    var permissionCache = {
        'permission': true
    };

    // Create a researched list of all possible state names.
    var stateNames = ['stateA', 'stateB', 'stateC'];

    // Now load the cache asynchronously with 
    // `true` for each permission granted.
    stateNames.forEach(function(state) {
        $userRoles.hasPermissions(state).then(function(data) {
            if(data === "true") {
                permissionCache[state] = true;
            }
        });
    });

    // And finally, establish a $stateChangeStart handler 
    // that looks up permissions **synchronously**, 
    // in the permissionCache.
    $rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
        if (!permissionCache[toState.name]) {
            event.preventDefault(); // prevent page from being loaded
            $state.go('permission');
        }
    });
}]);

Por supuesto, esta solución tendría sus propios problemas:

  • se basa en una buena investigación de todos los nombres de estado posibles.
  • cualquier cambio de estado intentado antes de que se cargue la memoria caché, será rechazado.
  • cualquier cambio de userRoles en la vida de la página requeriría que la memoria caché se borre y vuelva a cargar.

Sin investigación / prueba, no puedo decir si estos problemas son importantes o no.





angular-http