Retarder le changement de route AngularJS jusqu'au chargement du modèle pour éviter le scintillement

321

Je me demande s'il existe un moyen (similaire à Gmail) pour AngularJS de retarder l'affichage d'une nouvelle route jusqu'à ce que chaque modèle et ses données aient été récupérés à l' aide de ses services respectifs.

Par exemple, s'il y avait un ProjectsController qui répertoriait tous les projets et project_index.htmlqui était le modèle qui montrait ces projets, il Project.query()serait récupéré complètement avant d'afficher la nouvelle page.

Jusque-là, l'ancienne page continuerait à s'afficher (par exemple, si je parcourais une autre page et décidais ensuite de voir cet index de projet).

Misko Hevery
la source

Réponses:

374

$ routeProvider détermination propriété permet de retarder le changement d'itinéraire jusqu'à ce que les données sont chargées.

Définissez d'abord une route avec un resolveattribut comme celui-ci.

angular.module('phonecat', ['phonecatFilters', 'phonecatServices', 'phonecatDirectives']).
  config(['$routeProvider', function($routeProvider) {
    $routeProvider.
      when('/phones', {
        templateUrl: 'partials/phone-list.html', 
        controller: PhoneListCtrl, 
        resolve: PhoneListCtrl.resolve}).
      when('/phones/:phoneId', {
        templateUrl: 'partials/phone-detail.html', 
        controller: PhoneDetailCtrl, 
        resolve: PhoneDetailCtrl.resolve}).
      otherwise({redirectTo: '/phones'});
}]);

notez que la resolvepropriété est définie sur l'itinéraire.

function PhoneListCtrl($scope, phones) {
  $scope.phones = phones;
  $scope.orderProp = 'age';
}

PhoneListCtrl.resolve = {
  phones: function(Phone, $q) {
    // see: https://groups.google.com/forum/?fromgroups=#!topic/angular/DGf7yyD4Oc4
    var deferred = $q.defer();
    Phone.query(function(successData) {
            deferred.resolve(successData); 
    }, function(errorData) {
            deferred.reject(); // you could optionally pass error data here
    });
    return deferred.promise;
  },
  delay: function($q, $defer) {
    var delay = $q.defer();
    $defer(delay.resolve, 1000);
    return delay.promise;
  }
}

Notez que la définition du contrôleur contient un objet de résolution qui déclare des choses qui devraient être disponibles pour le constructeur du contrôleur. Ici, le phonesest injecté dans le contrôleur et il est défini dans la resolvepropriété.

le resolve.phones fonction est responsable du retour d'une promesse. Toutes les promesses sont collectées et le changement d'itinéraire est retardé jusqu'à ce que toutes les promesses soient résolues.

Démo de travail: http://mhevery.github.com/angular-phonecat/app/#/phones Source: https://github.com/mhevery/angular-phonecat/commit/ba33d3ec2d01b70eb5d3d531619bf90153496831

Misko Hevery
la source
10
@MiskoHevery - et si vos contrôleurs sont à l'intérieur d'un module et sont définis comme une chaîne plutôt que comme une fonction. Comment pouvez-vous configurer l'attribut de résolution comme vous le faites?
aar0n
53
Comment est-ce utilisé dans angular.controller()les définitions de contrôleur de type? Dans le $routeProvidertruc, je pensais que vous deviez utiliser les noms de chaîne des contrôleurs.
Ben Lesh
6
Un exemple utilisant angular.controller () et avec la dernière version d'AngularJS?
Laurent
22
@blesh, lorsque vous utilisez angular.controller(), vous pouvez affecter le résultat de cette fonction à une variable ( var MyCtrl = angular.controller(...)), puis travailler avec celle-ci plus loin ( MyCtrl.loadData = function(){..}). Regardez la vidéo d' egghead
petrkotek
17
Je voudrais toujours une bonne façon de faire sans avoir à placer votre contrôleur dans un global. Je ne veux pas joncher de globaux partout. Vous pouvez le faire avec une constante, mais ce serait bien de pouvoir mettre la fonction de résolution sur / dans le contrôleur, pas ailleurs.
Erik Honn
51

Voici un exemple de travail minimal qui fonctionne pour Angular 1.0.2

Modèle:

<script type="text/ng-template" id="/editor-tpl.html">
    Editor Template {{datasets}}
</script>

<div ng-view>

</div>

JavaScript:

function MyCtrl($scope, datasets) {    
    $scope.datasets = datasets;
}

MyCtrl.resolve = {
    datasets : function($q, $http) {
        var deferred = $q.defer();

        $http({method: 'GET', url: '/someUrl'})
            .success(function(data) {
                deferred.resolve(data)
            })
            .error(function(data){
                //actually you'd want deffered.reject(data) here
                //but to show what would happen on success..
                deferred.resolve("error value");
            });

        return deferred.promise;
    }
};

var myApp = angular.module('myApp', [], function($routeProvider) {
    $routeProvider.when('/', {
        templateUrl: '/editor-tpl.html',
        controller: MyCtrl,
        resolve: MyCtrl.resolve
    });
});​

http://jsfiddle.net/dTJ9N/3/

Version simplifiée:

Puisque $ http () renvoie déjà une promesse (alias différée), nous n'avons en fait pas besoin de créer la nôtre. Nous pouvons donc simplifier MyCtrl. décidez de:

MyCtrl.resolve = {
    datasets : function($http) {
        return $http({
            method: 'GET', 
            url: 'http://fiddle.jshell.net/'
        });
    }
};

Le résultat de $ http () contient des données , des statuts , des en- têtes et des objets de configuration , nous devons donc changer le corps de MyCtrl en:

$scope.datasets = datasets.data;

http://jsfiddle.net/dTJ9N/5/

mb21
la source
J'essaie de faire quelque chose comme ça, mais j'ai du mal à injecter des «ensembles de données» car il n'est pas défini. Des pensées?
Rob Bygrave
Hé mb21, je pense que vous pourriez m'aider avec cette question: stackoverflow.com/questions/14271713/…
winduptoy
Quelqu'un pourrait-il m'aider à convertir cette réponse au format app.controller ('MyCtrl')? jsfiddle.net/5usya/1 n'a pas fonctionné pour moi.
user1071182
je reçois une erreur:Unknown provider: datasetsProvider <- datasets
chovy
Vous pouvez rendre votre réponse plus simple en remplaçant les ensembles de données par ceci:function($http) { return $http({method: 'GET', url: '/someUrl'}) .then( function(data){ return data;}, function(reason){return 'error value';} ); }
Morteza Tourani
32

Je vois certaines personnes demander comment faire cela en utilisant la méthode angular.controller avec une injection de dépendance conviviale pour la minification. Depuis que je viens de travailler, je me suis senti obligé de revenir et d'aider. Voici ma solution (adoptée à partir de la question d'origine et de la réponse de Misko):

angular.module('phonecat', ['phonecatFilters', 'phonecatServices', 'phonecatDirectives']).
  config(['$routeProvider', function($routeProvider) {
    $routeProvider.
      when('/phones', {
        templateUrl: 'partials/phone-list.html', 
        controller: PhoneListCtrl, 
        resolve: { 
            phones: ["Phone", "$q", function(Phone, $q) {
                var deferred = $q.defer();
                Phone.query(function(successData) {
                  deferred.resolve(successData); 
                }, function(errorData) {
                  deferred.reject(); // you could optionally pass error data here
                });
                return deferred.promise;
             ]
            },
            delay: ["$q","$defer", function($q, $defer) {
               var delay = $q.defer();
               $defer(delay.resolve, 1000);
               return delay.promise;
              }
            ]
        },

        }).
      when('/phones/:phoneId', {
        templateUrl: 'partials/phone-detail.html', 
        controller: PhoneDetailCtrl, 
        resolve: PhoneDetailCtrl.resolve}).
      otherwise({redirectTo: '/phones'});
}]);

angular.controller("PhoneListCtrl", [ "$scope", "phones", ($scope, phones) {
  $scope.phones = phones;
  $scope.orderProp = 'age';
}]);

Étant donné que ce code est dérivé de la question / réponse la plus populaire, il n'est pas testé, mais il devrait vous envoyer dans la bonne direction si vous comprenez déjà comment rendre le code angulaire convivial pour la minification. La seule partie que mon propre code ne nécessitait pas était une injection de "Phone" dans la fonction de résolution pour les "téléphones", et je n'ai pas du tout utilisé d'objet "delay".

Je recommande également cette vidéo youtube http://www.youtube.com/watch?v=P6KITGRQujQ&list=UUKW92i7iQFuNILqQOUOCrFw&index=4&feature=plcp , ce qui m'a beaucoup aidé

Si cela vous intéresse, j'ai décidé de coller mon propre code (écrit en coffeescript) afin que vous puissiez voir comment je l'ai fait fonctionner.

Pour info, j'utilise à l'avance un contrôleur générique qui m'aide à faire du CRUD sur plusieurs modèles:

appModule.config ['$routeProvider', ($routeProvider) ->
  genericControllers = ["boards","teachers","classrooms","students"]
  for controllerName in genericControllers
    $routeProvider
      .when "/#{controllerName}/",
        action: 'confirmLogin'
        controller: 'GenericController'
        controllerName: controllerName
        templateUrl: "/static/templates/#{controllerName}.html"
        resolve:
          items : ["$q", "$route", "$http", ($q, $route, $http) ->
             deferred = $q.defer()
             controllerName = $route.current.controllerName
             $http(
               method: "GET"
               url: "/api/#{controllerName}/"
             )
             .success (response) ->
               deferred.resolve(response.payload)
             .error (response) ->
               deferred.reject(response.message)

             return deferred.promise
          ]

  $routeProvider
    .otherwise
      redirectTo: '/'
      action: 'checkStatus'
]

appModule.controller "GenericController", ["$scope", "$route", "$http", "$cookies", "items", ($scope, $route, $http, $cookies, items) ->

  $scope.items = items
      #etc ....
    ]
bitwit
la source
Dois-je déduire correctement de votre exemple et de mes tentatives infructueuses qu'il est désormais impossible de référencer une resolvefonction dans le contrôleur, dans les versions récentes d'Angular? Il faut donc le déclarer dans la config comme il est ici?
XML
@XMLilley Je suis sûr que c'est le cas. Cet exemple venait de 1.1.2 quand je l'ai écrit, je crois. Je n'ai vu aucune documentation sur la façon de mettre la résolution à l'intérieur d'un contrôleur
bitwit
2
Cool merci. Il y a beaucoup d'exemples de le faire sur SO (comme les deux premiers ici), mais ils sont tous de 2012 et début 2013. C'est une approche élégante, mais semble obsolète. L'alternative la plus propre semble maintenant être d'écrire des services individuels qui sont des objets promis.
XML
Merci, cela a fonctionné pour moi. Pour toute autre personne qui obtient des erreurs concernant un $deferservice non défini , notez que dans la version 1.5.7 d'AngularJS, vous souhaitez utiliser à la $timeoutplace.
racl101
18

Cette validation , qui fait partie de la version 1.1.5 et supérieure, expose l' $promiseobjet de $resource. Les versions de ngResource incluant ce commit permettent de résoudre des ressources comme celle-ci:

$ routeProvider

resolve: {
    data: function(Resource) {
        return Resource.get().$promise;
    }
}

manette

app.controller('ResourceCtrl', ['$scope', 'data', function($scope, data) {

    $scope.data = data;

}]);
Maximilian Hoffmann
la source
Quelles versions incluent ce commit, s'il vous plaît?
XML
La dernière version instable (1.1.5) inclut ce commit. ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js
Maximilian Hoffmann
J'aime cette approche moins verbeuse. Ce serait bien de créer une promesse à partir de l'objet de données réel et de le transmettre directement, mais c'est si peu de code que cela fonctionne bien.
Sam Barnum
1
Comment la ressource accède-t-elle à $ routeParams? Par exemple: dans GET '/api/1/apps/:appId'-> App.get({id: $routeParams.appId}).$promise();Je ne peux pas utiliser comme ça
zeronone
2
@zeronone que vous injectez $routeselon votre résolution et votre utilisation $route.current.params. Attention, $routeParamspointe toujours vers l'ancienne route.
Brice Stacey
16

Cet extrait est compatible avec l' injection de dépendances (je l'utilise même en combinaison de ngmin et uglify ) et c'est un domaine plus élégant piloté par solution basée sur .

L'exemple ci-dessous enregistre une ressource Phone et un phoneRoutes constant , qui contient toutes vos informations de routage pour ce domaine (téléphone). Quelque chose que je n'ai pas aimé dans la réponse fournie était l'emplacement de la logique de résolution - le module principal ne devrait rien savoir ou être dérangé par la façon dont les arguments de ressource sont fournis au contrôleur. De cette façon, la logique reste dans le même domaine.

Remarque: si vous utilisez ngmin (et si vous ne l'êtes pas: vous devriez) vous n'avez qu'à écrire les fonctions de résolution avec la convention du tableau DI.

angular.module('myApp').factory('Phone',function ($resource) {
  return $resource('/api/phone/:id', {id: '@id'});
}).constant('phoneRoutes', {
    '/phone': {
      templateUrl: 'app/phone/index.tmpl.html',
      controller: 'PhoneIndexController'
    },
    '/phone/create': {
      templateUrl: 'app/phone/edit.tmpl.html',
      controller: 'PhoneEditController',
      resolve: {
        phone: ['$route', 'Phone', function ($route, Phone) {
          return new Phone();
        }]
      }
    },
    '/phone/edit/:id': {
      templateUrl: 'app/phone/edit.tmpl.html',
      controller: 'PhoneEditController',
      resolve: {
        form: ['$route', 'Phone', function ($route, Phone) {
          return Phone.get({ id: $route.current.params.id }).$promise;
        }]
      }
    }
  });

La pièce suivante injecte les données de routage lorsque le module est dans l'état de configuration et les applique au $ routeProvider .

angular.module('myApp').config(function ($routeProvider, 
                                         phoneRoutes, 
                                         /* ... otherRoutes ... */) {

  $routeProvider.when('/', { templateUrl: 'app/main/index.tmpl.html' });

  // Loop through all paths provided by the injected route data.

  angular.forEach(phoneRoutes, function(routeData, path) {
    $routeProvider.when(path, routeData);
  });

  $routeProvider.otherwise({ redirectTo: '/' });

});

Tester la configuration de l'itinéraire avec cette configuration est également assez simple:

describe('phoneRoutes', function() {

  it('should match route configuration', function() {

    module('myApp');

    // Mock the Phone resource
    function PhoneMock() {}
    PhoneMock.get = function() { return {}; };

    module(function($provide) {
      $provide.value('Phone', FormMock);
    });

    inject(function($route, $location, $rootScope, phoneRoutes) {
      angular.forEach(phoneRoutes, function (routeData, path) {

        $location.path(path);
        $rootScope.$digest();

        expect($route.current.templateUrl).toBe(routeData.templateUrl);
        expect($route.current.controller).toBe(routeData.controller);
      });
    });
  });
});

Vous pouvez le voir en pleine gloire dans ma dernière expérience (à venir) . Bien que cette méthode fonctionne bien pour moi, je me demande vraiment pourquoi l'injecteur $ ne retarde pas la construction de quoi que ce soit lorsqu'il détecte l'injection de tout ce qui est un objet promis ; cela rendrait les choses tellement plus faciles.

Edit: utilisé Angular v1.2 (rc2)

nul
la source
2
Cette excellente réponse semble beaucoup plus conforme à la philosophie "angulaire" (encapsulation, etc.). Nous devrions tous faire un effort conscient pour empêcher la logique de se propager partout dans la base de code comme kudzu.
zakdances
I really wonder why the $injector isn't delaying construction of anything when it detects injection of anything that is a promise objectJe suppose qu'ils ont omis cette fonctionnalité car cela pourrait encourager les modèles de conception qui affectent négativement la réactivité des applications. L'application idéale dans leur esprit est une application vraiment asynchrone, donc la résolution devrait être un cas de pointe.
zakdances
11

Retarder l'affichage de l'itinéraire entraînera certainement un enchevêtrement asynchrone ... pourquoi ne pas simplement suivre l'état de chargement de votre entité principale et l'utiliser dans la vue. Par exemple, dans votre contrôleur, vous pouvez utiliser à la fois les rappels de réussite et d'erreur sur ngResource:

$scope.httpStatus = 0; // in progress
$scope.projects = $resource.query('/projects', function() {
    $scope.httpStatus = 200;
  }, function(response) {
    $scope.httpStatus = response.status;
  });

Ensuite, dans la vue, vous pouvez faire n'importe quoi:

<div ng-show="httpStatus == 0">
    Loading
</div>
<div ng-show="httpStatus == 200">
    Real stuff
    <div ng-repeat="project in projects">
         ...
    </div>
</div>
<div ng-show="httpStatus >= 400">
    Error, not found, etc. Could distinguish 4xx not found from 
    5xx server error even.
</div>
jpsimons
la source
6
Peut-être que l'exposition du statut HTTP à la vue n'est pas correcte, pas plus que le traitement des classes CSS et des éléments DOM n'appartient au contrôleur. J'utiliserais probablement la même idée mais le statut abstrait dans isValid () et isLoaded ().
jpsimons
1
Ce n'est en effet pas la meilleure séparation des préoccupations, en plus qu'il se bloque si vous avez des contrôleurs imbriqués qui dépendent de l'objet spécifique.
null
C'est assez intelligent .. quant à l'exposition des codes d'état à la vue, vous pouvez simplement coller la logique http dans les propriétés de portée dans le contrôleur, puis les lier. De plus, si vous effectuez plusieurs appels ajax qui se produisent en arrière-plan, vous voudrez quand même le faire.
KingOfHypocrites
Ce serait bien si la question consistait simplement à retarder une opinion. Mais la résolution est mieux utilisée si vous devez retarder l' instanciation d'un contrôleur - pas seulement la vue. (Ex: si vous devez être sûr que votre JSON est chargé parce que votre contrôleur le transmet à une directive avant qu’il ne soit câblé.) À partir des documents: "le routeur attendra qu’ils soient tous résolus ou qu’un soit rejeté avant que le contrôleur ne soit instancié ".
Dan
8

J'ai travaillé à partir du code de Misko ci-dessus et c'est ce que j'ai fait avec. Il s'agit d'une solution plus récente car elle $defera été remplacée par $timeout. $timeoutCependant, la substitution attendra le délai d'expiration (dans le code de Misko, 1 seconde), puis retournera les données en espérant qu'elles soient résolues à temps. De cette façon, il revient dès que possible.

function PhoneListCtrl($scope, phones) {
  $scope.phones = phones;
  $scope.orderProp = 'age';
}

PhoneListCtrl.resolve = {

  phones: function($q, Phone) {
    var deferred = $q.defer();

    Phone.query(function(phones) {
        deferred.resolve(phones);
    });

    return deferred.promise;
  }
}
Justen
la source
7

Utilisation d'AngularJS 1.1.5

Mise à jour de la fonction «téléphones» dans la réponse de Justen en utilisant AngularJS 1.1.5 syntaxe .

Original:

phones: function($q, Phone) {
    var deferred = $q.defer();

    Phone.query(function(phones) {
        deferred.resolve(phones);
    });

    return deferred.promise;
}

Actualisé:

phones: function(Phone) {
    return Phone.query().$promise;
}

Beaucoup plus court grâce à l'équipe Angular et aux contributeurs. :)

C'est aussi la réponse de Maximilian Hoffmann. Apparemment, cet engagement est devenu 1.1.5.

OJ Raqueño
la source
1
Je n'arrive pas à trouver quoi que ce soit $promisedans les documents . Il pourrait avoir été supprimé à partir de la v2.0 +.
zakdances
Il n'est disponible qu'en 1.2
Thomas
5

Vous pouvez utiliser la propriété de résolution $ routeProvider pour retarder le changement d'itinéraire jusqu'à ce que les données soient chargées.

angular.module('app', ['ngRoute']).
  config(['$routeProvider', function($routeProvider, EntitiesCtrlResolve, EntityCtrlResolve) {
    $routeProvider.
      when('/entities', {
        templateUrl: 'entities.html', 
        controller: 'EntitiesCtrl', 
        resolve: EntitiesCtrlResolve
      }).
      when('/entity/:entityId', {
        templateUrl: 'entity.html', 
        controller: 'EntityCtrl', 
        resolve: EntityCtrlResolve
      }).
      otherwise({redirectTo: '/entities'});
}]);

Notez que le resolve propriété est définie sur l'itinéraire.

EntitiesCtrlResolveet EntityCtrlResolveest constante objets définis dans même fichier EntitiesCtrlet les EntityCtrlcontrôleurs.

// EntitiesCtrl.js

angular.module('app').constant('EntitiesCtrlResolve', {
  Entities: function(EntitiesService) {
    return EntitiesService.getAll();
  }
});

angular.module('app').controller('EntitiesCtrl', function(Entities) {
  $scope.entities = Entities;

  // some code..
});

// EntityCtrl.js

angular.module('app').constant('EntityCtrlResolve', {
  Entity: function($route, EntitiesService) {
    return EntitiesService.getById($route.current.params.projectId);
  }
});

angular.module('app').controller('EntityCtrl', function(Entity) {
  $scope.entity = Entity;

  // some code..
});
Bohdan Lyzanets
la source
3

J'aime l'idée de darkporter, car il sera facile pour une équipe de développeurs novice d'AngularJS de comprendre et de travailler immédiatement.

J'ai créé cette adaptation qui utilise 2 divs, une pour la barre de chargement et une autre pour le contenu réel affiché après le chargement des données. La gestion des erreurs se ferait ailleurs.

Ajoutez un indicateur «prêt» à $ scope:

$http({method: 'GET', url: '...'}).
    success(function(data, status, headers, config) {
        $scope.dataForView = data;      
        $scope.ready = true;  // <-- set true after loaded
    })
});

En vue html:

<div ng-show="!ready">

    <!-- Show loading graphic, e.g. Twitter Boostrap progress bar -->
    <div class="progress progress-striped active">
        <div class="bar" style="width: 100%;"></div>
    </div>

</div>

<div ng-show="ready">

    <!-- Real content goes here and will appear after loading -->

</div>

Voir aussi: Boostrap progress bar docs

reggoodwin
la source
Se désagrège un peu si vous chargez plusieurs éléments de données. Comment savoir si tout est chargé?
toxaq
Les choses ont évolué depuis que j'ai ajouté cette réponse en février, avec beaucoup plus d'activité sur cette page. Il semble qu'il y ait un meilleur support dans Angular pour résoudre ce problème maintenant que ce qui est suggéré ici.
Santé
J'arrive un peu en retard, mais le traitement de plusieurs données n'est pas une grande préoccupation. Il vous suffit d'utiliser des variables distinctes (booléens: isReadyData1, isReadyData2, etc.) pour chaque requête et de définir $ scope.ready = isReadyData1 && isReadyData2 ...; fonctionne bien pour moi.
GuillaumeA
1

J'ai aimé les réponses ci-dessus et j'ai beaucoup appris d'eux, mais il y a quelque chose qui manque dans la plupart des réponses ci-dessus.

J'étais coincé dans un scénario similaire où je résolvais l'url avec des données récupérées dans la première demande du serveur. Le problème que j'ai rencontré était de savoir si la promesse était rejected.

Je travaillais avec un fournisseur personnalisé qui permet de revenir un Promisequi a été résolu par la resolvede$routeProvider au moment de la phase de configuration.

Ce que je veux souligner ici, c'est le concept de when fait quelque chose comme ça.

Il voit l'url dans la barre d'url puis le whenbloc correspondant dans le contrôleur appelé et la vue est référencée jusqu'à présent tout va bien.

Disons que j'ai le code de phase de configuration suivant.

App.when('/', {
   templateUrl: '/assets/campaigns/index.html',
   controller: 'CampaignListCtr',
   resolve : {
      Auth : function(){
         return AuthServiceProvider.auth('campaign');
      }
   }
})
// Default route
.otherwise({
   redirectTo: '/segments'
});

Sur l'URL racine du navigateur, le premier bloc d'exécution est appelé, sinon il otherwiseest appelé.

Imaginons qu'un scénario que j'ai frappé rootUrl dans la AuthServicePrivider.auth()fonction de barre d'adresse soit appelé.

Disons que la promesse retournée est en état de rejet quoi alors ???

Rien n'est rendu du tout.

Otherwise Le bloc ne sera pas exécuté comme pour toute URL non définie dans le bloc de configuration et inconnue de la phase de configuration angularJs.

Nous devrons gérer l'événement qui est renvoyé lorsque cette promesse n'est pas tenue. En cas d'échec se $routeChangeErorrfait tirer dessus $rootScope.

Il peut être capturé comme indiqué dans le code ci-dessous.

$rootScope.$on('$routeChangeError', function(event, current, previous, rejection){
    // Use params in redirection logic.
    // event is the routeChangeEvent
    // current is the current url
    // previous is the previous url
    $location.path($rootScope.rootPath);
});

IMO C'est généralement une bonne idée de mettre le code de suivi des événements dans le bloc d'exécution de l'application. Ce code s'exécute juste après la phase de configuration de l'application.

App.run(['$routeParams', '$rootScope', '$location', function($routeParams, $rootScope, $location){
   $rootScope.rootPath = "my custom path";
   // Event to listen to all the routeChangeErrors raised
   // by the resolve in config part of application
   $rootScope.$on('$routeChangeError', function(event, current, previous, rejection){
       // I am redirecting to rootPath I have set above.
       $location.path($rootScope.rootPath);
   });
}]);

De cette façon, nous pouvons gérer l'échec de la promesse au moment de la phase de configuration.

Ashish Singh
la source
0

J'ai eu une interface de panneau coulissant multi-niveaux complexe, avec une couche d'écran désactivée. Création d'une directive sur la désactivation de la couche d'écran qui créerait un événement de clic pour exécuter l'état comme

$state.go('account.stream.social.view');

produisaient un effet de scintillement. history.back () au lieu de cela a bien fonctionné, mais ce n'est pas toujours de retour dans l'histoire dans mon cas. Donc, ce que je découvre, c'est que si je crée simplement l'attribut href sur mon écran de désactivation au lieu de state.go, cela fonctionne comme un charme.

<a class="disable-screen" back></a>

Directive «retour»

app.directive('back', [ '$rootScope', function($rootScope) {

    return {
        restrict : 'A',
        link : function(scope, element, attrs) {
            element.attr('href', $rootScope.previousState.replace(/\./gi, '/'));
        }
    };

} ]);

app.js Je viens de sauvegarder l'état précédent

app.run(function($rootScope, $state) {      

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

        $rootScope.previousState = fromState.name;
        $rootScope.currentState = toState.name;


    });
});
Dima
la source
-2

Une solution possible pourrait être d'utiliser la directive ng-cloak avec l'élément où nous utilisons les modèles, par exemple

<div ng-cloak="">
  Value in  myModel is: {{myModel}}
</div>

Je pense que celui-ci prend le moins d'effort.

Devendra Singh
la source