
misko hevery
Me pregunto si hay una forma (similar a Gmail) para que AngularJS retraso en mostrar una nueva ruta hasta después de que se haya obtenido cada modelo y sus datos utilizando sus respectivos servicios.
Por ejemplo, si hubiera un ProjectsController
que enumeró todos los proyectos y project_index.html
cual era la plantilla que mostraba estos Proyectos, Project.query()
se recuperaría por completo antes de mostrar la nueva página.
Hasta entonces, la página anterior seguiría mostrándose (por ejemplo, si estuviera navegando por otra página y luego decidiera ver este índice de Proyecto).

misko hevery
$rutaProveedor resolver La propiedad permite retrasar el cambio de ruta hasta que se carguen los datos.
Primero defina una ruta con resolve
atributo como este.
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'});
}]);
notar que el resolve
la propiedad se define en la ruta.
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;
}
}
Tenga en cuenta que la definición del controlador contiene un objeto de resolución que declara cosas que deberían estar disponibles para el constructor del controlador. Aquí el phones
se inyecta en el controlador y se define en el resolve
propiedad.
los resolve.phones
La función es responsable de devolver una promesa. Se recopilan todas las promesas y el cambio de ruta se retrasa hasta que se resuelven todas las promesas.
demostración de trabajo: http://mhevery.github.com/angular-phonecat/app/#/teléfonos
Fuente: https://github.com/mhevery/angular-phonecat/commit/ba33d3ec2d01b70eb5d3d531619bf90153496831
Aquí hay un ejemplo de trabajo mínimo que funciona para Angular 1.0.2
Plantilla:
<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("https://stackoverflow.com/", {
templateUrl: '/editor-tpl.html',
controller: MyCtrl,
resolve: MyCtrl.resolve
});
});
http://jsfiddle.net/dTJ9N/3/
Versión simplificada:
Dado que $http() ya devuelve una promesa (también conocida como diferida), en realidad no necesitamos crear la nuestra. Entonces podemos simplificar MyCtrl. resolver:
MyCtrl.resolve = {
datasets : function($http) {
return $http({
method: 'GET',
url: 'http://fiddle.jshell.net/'
});
}
};
El resultado de $http() contiene datos, estado, encabezados y configuración objetos, por lo que debemos cambiar el cuerpo de MyCtrl a:
$scope.datasets = datasets.data;
http://jsfiddle.net/dTJ9N/5/

tonto
Veo que algunas personas preguntan cómo hacer esto usando el método angular.controller con inyección de dependencia amigable con la minificación. Como acabo de hacer que esto funcione, me sentí obligado a volver y ayudar. Aquí está mi solución (adoptada de la pregunta original y la respuesta 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';
}]);
Dado que este código se deriva de la pregunta/respuesta más popular, no se ha probado, pero debería enviarlo en la dirección correcta si ya comprende cómo hacer un código angular compatible con la minificación. La única parte que mi propio código no requería era una inyección de “Teléfono” en la función de resolución para “teléfonos”, ni usé ningún objeto de “retraso”.
tambien te recomiendo este video de youtube http://www.youtube.com/watch?v=P6KITGRQujQ&list=UUKW92i7iQFuNILqQOUOCrFw&index=4&feature=plcp que me ayudó bastante
Si le interesa, he decidido pegar también mi propio código (escrito en script de café) para que pueda ver cómo lo hice funcionar.
FYI, de antemano uso un controlador genérico que me ayuda a hacer CRUD en varios modelos:
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: "https://stackoverflow.com/"
action: 'checkStatus'
]
appModule.controller "GenericController", ["$scope", "$route", "$http", "$cookies", "items", ($scope, $route, $http, $cookies, items) ->
$scope.items = items
#etc ....
]

Max Hoffman
este compromisoque es parte de la versión 1.1.5 y superior, expone el $promise
objeto de $resource
. Las versiones de ngResource que incluyen este compromiso permiten resolver recursos como este:
$rutaProveedor
resolve: {
data: function(Resource) {
return Resource.get().$promise;
}
}
controlador
app.controller('ResourceCtrl', ['$scope', 'data', function($scope, data) {
$scope.data = data;
}]);

nulo
Este fragmento es inyección de dependencia amigable (incluso lo uso en combinación con ngmin y afear) y es más elegante impulsado por dominio solución basada.
El siguiente ejemplo registra un Teléfono recurso y un constante rutas telefónicas, que contiene toda su información de enrutamiento para ese dominio (teléfono). Algo que no me gustó en la respuesta proporcionada fue la ubicación de la resolver lógica — la principal el módulo no debería saber nada o preocuparse por la forma en que se proporcionan los argumentos de recursos al controlador. De esta manera la lógica permanece en el mismo dominio.
Nota: si está utilizando ngmin (y si no lo es: debería) solo tiene que escribir las funciones de resolución con la convención de matriz 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 siguiente pieza es inyectar los datos de enrutamiento cuando el módulo está en el estado de configuración y aplicarlos al $rutaProveedor.
angular.module('myApp').config(function ($routeProvider,
phoneRoutes,
/* ... otherRoutes ... */) {
$routeProvider.when("https://stackoverflow.com/", { 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: "https://stackoverflow.com/" });
});
Probar la configuración de la ruta con esta configuración también es bastante fácil:
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);
});
});
});
});
Puedes verlo en todo su esplendor en mi último (próximo) experimento. Aunque este método funciona bien para mí, realmente me pregunto por qué $injector no está retrasando la construcción de cualquier cosa cuando detecta inyección de cualquier cosa eso es un promesa objeto; Haría las cosas muuuuuuucho más fáciles.
Editar: usó Angular v1.2 (rc2)

jpsimons
Retrasar la visualización de la ruta seguramente conducirá a un enredo asíncrono… ¿por qué no simplemente rastrear el estado de carga de su entidad principal y usarlo en la vista? Por ejemplo, en su controlador, puede usar las devoluciones de llamada de éxito y error en ngResource:
$scope.httpStatus = 0; // in progress
$scope.projects = $resource.query('/projects', function() {
$scope.httpStatus = 200;
}, function(response) {
$scope.httpStatus = response.status;
});
Luego, en la vista, podrías hacer lo que sea:
<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>

F Lekschas
Trabajé con el código de Misko anterior y esto es lo que hice con él. Esta es una solución más actual ya que $defer
ha sido cambiado a $timeout
. Sustituyendo $timeout
sin embargo, esperará el período de tiempo de espera (en el código de Misko, 1 segundo), luego devolverá los datos con la esperanza de que se resuelva a tiempo. De esta manera, vuelve lo antes posible.
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;
}
}