J'utilise la technique event.preventDefault () pour rediriger conditionnellement l'utilisateur vers un état approprié en fonction d'une valeur d'indicateur que je récupère du backend. Tout semble fonctionner correctement, sauf que parfois la page réelle est rendue, puis la redirection se produit et cela aussi dans très peu de cas. Ce rendu de la page réelle est également d'une durée très courte. Voici l'extrait de code que j'utilise:

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

  if (fromState.name === '' && toState.name === "myState"){

    Auth.isLoggedInAsync(function(loggedIn) {
      if (!loggedIn) {
        event.preventDefault();
        $state.go('state1');
      } else {
          event.preventDefault();
          Auth.getNextStateForUser()
          .then(function(data) {
            console.log(data.nextState);
            $state.go(data.nextState);
          })
          .catch(function(err){
              console.log(err);
              $state.go(toState.name);
          });
      }
    });

La logique que je suis est de vérifier si un utilisateur est connecté, puis de rediriger vers state1, sinon de rediriger vers l'état approprié suivant en fonction de nextstateValue récupéré depuis le backend. Si une erreur se produit, ouvrez la page réelle sans aucune redirection.

J'espère que je suis clair avec ce que je fais. Je veux juste savoir si le problème auquel je suis confronté est un problème réel ou s'il y a quelque chose de mal que je fais de mon côté.

Je l'ai également publié sur la page github, mais il semble que je sois le seul à faire face à ce problème.

Vérifiez ceci pour plus d'informations: https://github.com/angular-ui/ui-router/issues/2088

Merci

Modifier : sur la base des réponses, j'ai mis à jour mon code d'utilisation de la résolution de l'interface utilisateur et j'ai converti mon isLoggedInAsyc en une promesse de retour.

resolve: {

  promiseState: function(Auth, $state, $q, $stateParams) {

    var deferred = $q.defer();

    Auth.isLoggedInSync()
    .then(function(isLoggedIn){
        console.log(isLoggedIn);
        if(isLoggedIn) {
          Auth.getNextStateForUser()
              .then(function(data) {
                console.log(data.nextState);
                $state.go(data.nextState);
                deferred.resolve(data);
               })
              .catch(function(error) {
                console.log(error);
                deferred.reject(error);
          }); 
        }

        deferred.resolve(isLoggedIn);
    })
    .catch(function(err){
        console.log(err);
        deferred.reject(err);
    });

    return deferred.promise;
  }
}

Et ma promesse de retour isLoggedIn ().

  isLoggedInSync: function(callback) {
    var cb = callback || angular.noop;
    var deferred = $q.defer();
    if(currentUser.hasOwnProperty('$promise')) {
      currentUser.$promise.then(function() {
        deferred.resolve(true);
        return cb(true);
      }).catch(function() {
        deferred.resolve(false);
        return cb(false);
      });
    } else if(currentUser.hasOwnProperty('role')) {
      deferred.resolve(true);
    } else {
      deferred.resolve(false);
    }
    return deferred.promise;
  }
1
dark_shadow 12 juil. 2015 à 16:36

2 réponses

Meilleure réponse

Comme mentionné par @milaenlempera, tous les événements angulaires sont synchrones et il n'y a aucun moyen réel pour vous de gérer correctement l'asynchronie dans ce cas. Nous avions ce problème dans notre code de production et notre solution consistait à utiliser la fonctionnalité resolve dans ui-router. C'est ainsi que nous l'avons résolu (branché sur votre code évidemment)

.state({
  name: 'myState',
  resolve: {
    principal: ['Auth', '$state', '$q', function(Auth, $state, $q) {
      var deferred = $q.defer();
      Auth.isLoggedInAsync(function(loggedIn) {
        if(!isLoggedIn) {
          deferred.resolve($state.go('state1'));
          return;
        }
        return Auth.getNextStateForUser()
          .then(function(data) {
            $state.go(data.nextState);
           })
          .catch(function(error) {
            deferred.reject(error);
          });
      });
      return deferred.promise;
    }]
  }
});

Je recommanderais de tourner Auth.isLoggedInAsync pour retourner une promesse.

De plus, votre code d'origine provoquera une boucle infinie si Auth.getNextStateForUser() est rejeté. Il tentera d'accéder à l'état avec le nom toState.name, qui est l'état qui vient de provoquer l'erreur, ce qui provoquera une autre erreur et l'amènera à toState.name ...

Comme resolve cascades, vous pouvez implémenter des états enfants si vous souhaitez partager des règles pour les autorisations. C'est vraiment dommage qu'il n'y ait aucun moyen de simplement mettre une propriété sur l'état et de gérer la vérification des autorisations asynchrones ailleurs, mais c'est la main qui nous a été traitée pour le moment.

Conformément aux commentaires, voici un exemple de chaînage de promesses avec votre code modifié.

  promiseState: ['Auth', '$state', function(Auth, $state) {
    return Auth.isLoggedInSync()
      .then(function(isLoggedIn) {
        if(isLoggedIn) {
          return Auth.getNextStateForUser().then(function(data) {
            return data.nextState;
          });
        }
        // If the user isn't logged in, return the state that you should go to instead
        return 'state1';
      })
      .then(function(nextState) {
        return $state.go(nextState);
      });
  }]
1
Dan Pantry 13 juil. 2015 à 08:43

Les événements angulaires sont synchronisés. Si vous avez besoin de l'arrêter, vous devez le faire de manière synchrone dans la fonction de gestionnaire d'événements. Dans votre exemple, la connexion est vérifiée de manière asynchrone, tandis que le routage continue. C'est pourquoi vous voyez la page et la redirection se produit ensuite.

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

      // here check if route can be display (synchronously)
      // or event.preventDefault()

      Auth.isLoggedInAsync(function(loggedIn) {
      if (loggedIn) {
        // continue to original route or go to state by server data
      } else {
        // redirect to login page
      }  
});

Vous pouvez vous inspirer de ma solution ici

0
milanlempera 12 juil. 2015 à 14:36