我想知道是否有一种方法(类似于Gmail)让AngularJS延迟显示一个新的路由,直到每个模型和它的数据已经使用各自的服务获取。
例如,如果有一个ProjectsController列出了所有的项目,而project_index.html是显示这些项目的模板,那么Project.query()将在显示新页面之前被完全获取。
在此之前,旧页面仍将继续显示(例如,如果我正在浏览另一个页面,然后决定查看这个Project索引)。
我想知道是否有一种方法(类似于Gmail)让AngularJS延迟显示一个新的路由,直到每个模型和它的数据已经使用各自的服务获取。
例如,如果有一个ProjectsController列出了所有的项目,而project_index.html是显示这些项目的模板,那么Project.query()将在显示新页面之前被完全获取。
在此之前,旧页面仍将继续显示(例如,如果我正在浏览另一个页面,然后决定查看这个Project索引)。
当前回答
使用AngularJS 1.1.5
使用AngularJS 1.1.5语法更新justin回答中的“phones”函数。
原:
phones: function($q, Phone) {
var deferred = $q.defer();
Phone.query(function(phones) {
deferred.resolve(phones);
});
return deferred.promise;
}
更新:
phones: function(Phone) {
return Phone.query().$promise;
}
多亏了Angular团队和贡献者,这个版本变得更短了。:)
这也是马克西米利安·霍夫曼的答案。显然,这个commit被放到了1.1.5。
其他回答
我有一个复杂的多层次滑动面板界面,与禁用的屏幕层。在禁用屏幕层上创建指令,该指令将创建点击事件来执行状态
$state.go('account.stream.social.view');
产生了弹跳的效果。history.back()而不是它工作得很好,但它并不总是回到历史在我的情况下。所以我发现,如果我只是在我的禁用屏幕上创建属性href而不是状态。去吧,效果很好。
<a class="disable-screen" back></a>
指令“回来”
app.directive('back', [ '$rootScope', function($rootScope) {
return {
restrict : 'A',
link : function(scope, element, attrs) {
element.attr('href', $rootScope.previousState.replace(/\./gi, '/'));
}
};
} ]);
我只是保存了之前的状态
app.run(function($rootScope, $state) {
$rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams) {
$rootScope.previousState = fromState.name;
$rootScope.currentState = toState.name;
});
});
您可以使用$routeProvider解析属性来延迟路由更改,直到数据被加载。
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'});
}]);
注意,resolve属性是在route上定义的。
EntitiesCtrlResolve和EntityCtrlResolve是与EntitiesCtrl和EntityCtrl控制器定义在同一文件中的常量对象。
// 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..
});
$routeProvider resolve属性允许延迟路由更改,直到数据被加载。
首先像这样定义一个具有resolve属性的路由。
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'});
}]);
注意,resolve属性是在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;
}
}
注意,控制器定义包含一个resolve对象,它声明了控制器构造函数应该可用的东西。在这里,手机被注入到控制器中,并在resolve属性中定义。
的决心。Phones函数负责返回一个promise。收集所有的承诺,并延迟路由更改,直到所有的承诺都被解决。
工作演示:http://mhevery.github.com/angular-phonecat/app/#/phones 来源:https://github.com/mhevery/angular-phonecat/commit/ba33d3ec2d01b70eb5d3d531619bf90153496831
使用AngularJS 1.1.5
使用AngularJS 1.1.5语法更新justin回答中的“phones”函数。
原:
phones: function($q, Phone) {
var deferred = $q.defer();
Phone.query(function(phones) {
deferred.resolve(phones);
});
return deferred.promise;
}
更新:
phones: function(Phone) {
return Phone.query().$promise;
}
多亏了Angular团队和贡献者,这个版本变得更短了。:)
这也是马克西米利安·霍夫曼的答案。显然,这个commit被放到了1.1.5。
这个代码片段是依赖注入友好的(我甚至将它与ngmin和uglify结合使用),它是一个更优雅的基于领域驱动的解决方案。
下面的例子注册了一个Phone资源和一个常量phoneRoutes,其中包含了该(Phone)域的所有路由信息。在提供的答案中,我不喜欢解析逻辑的位置——主模块不应该知道任何事情,也不应该被提供给控制器的资源参数的方式所困扰。这样逻辑就保持在同一个域中。
注意:如果你正在使用ngmin(如果你没有:你应该),你只需要按照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;
}]
}
}
});
下一部分是在模块处于configure状态时注入路由数据,并将其应用到$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: '/' });
});
用这个设置测试路由配置也很简单:
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);
});
});
});
});
你可以在我最新的(即将到来的)实验中看到它的全部荣耀。 虽然这个方法对我来说工作得很好,但我真的想知道为什么$injector在检测到任何是promise对象的注入时不延迟任何东西的构造;这会让事情变得非常非常非常简单。
编辑:使用Angular v1.2(rc2)