318 votes

Retardant AngularJS itinéraire changer jusqu'à ce que le modèle chargé d’empêcher le scintillement

Je me demande si il existe un moyen (similaire à Gmail) pour AngularJS pour retarder montrant une nouvelle route jusqu'à ce que après chaque modèle et ses données ont été extraites à l'aide de ses services.

Par exemple, si il y avait un ProjectsController la liste de tous les Projets et project_index.html qui a été le modèle qui a montré ces Projets, Project.query() serait récupéré complètement avant d'afficher la nouvelle page.

Jusqu'alors, l'ancienne page continuera à afficher (par exemple, si j'ai été la navigation sur une autre page, puis a décidé de voir ce Projet d'index).

375voto

Misko Hevery Points 25631

$routeProvider résoudre propriété permet de retarder la route à modifier jusqu'à ce que les données sont chargées.

Tout d'abord, définir un itinéraire en resolve attribut de ce genre.

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 l' resolve propriété est définie sur la route.

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 le contrôleur de définition contient une détermination de l'objet qui déclare des choses qui devraient être à la disposition du contrôleur de constructeur. Ici l' phones est injecté dans le contrôleur et il est défini dans l' resolve de la propriété.

L' resolve.phones fonction est responsable du retour d'une promesse. Toutes les promesses sont recueillies et le changement d'itinéraire est retardée jusqu'à ce que toutes les promesses ne sont pas résolus.

Travail de démonstration: http://mhevery.github.com/angular-phonecat/app/#/phones Source: https://github.com/mhevery/angular-phonecat/commit/ba33d3ec2d01b70eb5d3d531619bf90153496831

51voto

mb21 Points 3703

Voici un travail minimale exemple qui fonctionne Angulaire 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() retourne déjà une promesse (aka différé), nous n'avez pas besoin de créer notre propre. Nous pouvons simplifier MyCtrl. résoudre:

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

Le résultat de $http() contient des données, le statut, les en-têtes et config objets, nous avons donc besoin de modifier le corps de MyCtrl:

$scope.datasets = datasets.data;

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

32voto

spadict Points 776

Je vois certaines personnes demandant comment faire cela à l'aide de l'angle.la méthode du contrôleur avec minification respectueux de l'injection de dépendances. Depuis que je viens de recevoir ce travail je me suis senti obligé de revenir et de les aider. Voici ma solution (adopté à partir de la question d'origine et de Misko de la réponse):

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 c'est non testé, mais il doit vous envoyer dans la bonne direction si vous comprenez déjà comment faire de minification amical angulaire de code. La seule partie que mon propre code ne nécessite pas été une injection de "Téléphone" dans la détermination de la fonction de 'phones', ni ai-je utiliser un "report" de l'objet.

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

Faut-il vous intéresser, j'ai décidé de coller mon propre code (Écrit en coffeescript) de sorte que vous pouvez voir comment j'ai eu de travail.

Pour info, à l'avance-je utiliser 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 ....
    ]

18voto

Cette validation, qui fait partie de la version 1.1.5 et expose ci-dessus, le objet de . Versions de ngResource y compris cette validation permettent de résoudre les ressources de ce genre :

$routeProvider

contrôleur de

16voto

null Points 2511

Cet extrait est l'injection de dépendance sympathique (j'ai même l'utiliser en combinaison de ngmin et uglify) et c'est un plus élégant pilotée par domaine en fonction de la solution.

L'exemple ci-dessous enregistre un Téléphone de ressources et une constante phoneRoutes, qui contient toutes vos informations de routage pour que (de téléphone) de domaine. Quelque chose que je n'aimais pas dans la réponse a été le lieu de la résoudre logique -- le principal module ne devrait pas savoir quoi que ce soit ou être dérangé au sujet de la façon dont la ressource arguments 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 n'êtes pas: vous devez vous n'avez qu'à écrire le résoudre fonctions avec la DI de la matrice de la convention.

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;
        }]
      }
    }
  });

Le morceau suivant est d'injecter de l'acheminement des données lorsque le module est dans la configuration de l'état et de l'appliquer à l' $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: '/' });

});

Test de la route de configuration avec ce programme d'installation est également assez facile:

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 la voir en pleine gloire dans mon dernier (à venir) expérience. Bien que cette méthode fonctionne très bien pour moi, je me demande vraiment pourquoi le $injecteur n'est pas de retarder la construction de quelque chose lorsqu'il détecte l'injection de quelque chose qui est une promesse de l'objet; il rendrait les choses soooOOOOOooOOOOO beaucoup plus facile.

Edit: utilisé Angulaire v1.2(rc2)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X