我想尽可能限制AngularJS应用程序中的“闪烁”。我从路由器使用 resolve:(使用 ngRouter 和 ui-router )来加载数据,以便显示我的页面所需的一切在改变路线之前可以使用。
常见例子:
.state('recipes.category', {
url: '/:cat',
templateUrl: '/partials/recipes.category.html',
controller: 'RecipesCategoryCtrl as recipeList',
resolve: {
category: ['$http','$stateParams', function ($http, $stateParams) {
return $http.get('/recipes/' + $stateParams.cat).then(function(data) { return data.data; });
}]
}
});
现在我的 RecipesCategoryCtrl 控制器可以加载类别,并且可以直接解析承诺。
有没有办法将加载代码直接嵌入我的控制器中?或者其他地方更“干净”?我不喜欢在路线定义中有太多的逻辑......
类似的东西:
.state('recipes.category', {
url: '/:cat',
templateUrl: '/partials/recipes.category.html',
controller: 'RecipesCategoryCtrl as recipeList',
resolve: 'recipeList.resolve()' // something related to RecipesCategoryCtrl and "idiomatic" AngularJS
});
答案 0 :(得分:4)
也许这不是您想要的,但您可以使用以下方式将一些逻辑从路由器移动到控制器:
//prepare content
$scope.$on('$viewContentLoading', function(event) {
//show a progress bar whatever
//fetch/request your data asynchronously
//when promise resolves, add your data to $scope
});
//remove spinning loader when view is ready
$scope.$on('$viewContentLoaded', function(event) {
//remove progress bar
});
基本上,用户首先被发送到页面,然后加载动态内容,然后显示整页。
这可能完全偏离主题,但我正在使用这种方法并且工作正常。首先显示新视图然后获取数据也是一种很好的做法。有一个非常好的视频here解释了原因。该视频是关于带有Phonegap的SPA,但有很多关于SPA的提示。有趣的部分(针对这个具体案例) 是大约1小时1分钟。
修改:如果$viewContentLoading
未被解雇,请查看here。您可能需要将所有逻辑放在$viewContentLoaded
这就是我目前正在做的事情:
$scope.$on('$viewContentLoaded', function(event) {
//show loader while we are preparing view
notifications.showProgressDialog();
//get data
getData().then(function(data) {
//bind data to view
$scope.data = data;
//remove spinning loader as view is ready
notifications.hideProgressDialog();
});
});
我仍然对$scope.data = data;
不满意,好像我的数据对象很大,我可能会在与视图绑定完成之前隐藏进度对话框,因此可能会发生一些闪烁。解决方案是使用处理scope.$last
的自定义指令,请参阅this answer(即使绑定到$stateChangeSuccess
就足够了,请查看here)
这是ui-router在更改状态/视图时的当前工作方式:
$viewContentLoading
广播$viewContentLoaded
。$viewContentLoaded
作出反应(当它被设置为当事人派遣那些事件时)。答案 1 :(得分:3)
这是常见问题,通常发生在使用ngRoute或uiRouter的应用程序中。在这种情况下,我通常使用缓存。
例如,如果您使用Active Record like pattern与我们的业务层进行通信,则可以按以下步骤操作:
//...
.state('users', {
url: '/users',
templateUrl: '/partials/users.html',
controller: 'UsersCtrl',
resolve: {
users: ['Users', function (Users) {
return Users.getList();
}]
}
})
.state('user', {
url: '/users/:id',
templateUrl: '/partials/user.html',
controller: 'UserCtrl',
resolve: {
users: ['Users', '$stateParams', function (Users, $stateParams) {
return Users.get($stateParams.id);
}]
}
});
myModule.factory('User', function ($q, $http) {
var cachedUsers = null;
function User() {
}
User.getList = function () {
if (cachedUsers) {
return $q.when(cachedUsers);
} else {
return $http.get('/users')
.then(function (resp) {
cachedUsers = resp.data;
return cachedUsers;
});
}
};
User.get = function (id) {
if (cachedUsers && cachedUsers[id]) {
return $q.when(cachedUsers[id]);
} else {
return $http.get('/users/' + id)
.then(function (resp) {
cachedUsers = cachedUsers || {};
cachedUsers[id] = resp.data;
return cachedUsers[id];
});
}
};
return User;
});
myModule.controller('UsersCtrl', function ($scope, users) {
$scope.users = data;
});
myModule.controller('UserCtrl', function ($scope, user) {
$scope.users = data;
});
这样,您的应用程序会缓存请求的结果,并且在每个后续路由更改中,它都会通过内存缓存获取请求的值。由于这是一个虚拟示例,我建议您使用内置的AngularJS缓存机制,因为它利用了不同的HTTP头。
答案 2 :(得分:2)
您可以在控制器中注入一个服务,该服务执行$ http.get来清理您的代码。
.state('recipes.category', {
url: '/:cat',
templateUrl: '/partials/recipes.category.html',
controller: 'RecipesCategoryCtrl as recipeList',
resolve: {
category: ['recipeService','$stateParams', function (recipeService, $stateParams) {
return recipeService.get($stateParams.cat)
}]
}
});
答案 3 :(得分:2)
我来到这里寻找有关ui-router
处理resolve
的方式的信息,以及它与ngRoute
的方式有何不同。我没有找到答案,但最近我和你遇到的同样问题打了好几个小时,所以我认为这可能有用。需要注意的是,解决方案是针对ngRoute
而不是ui-router
,因此YMMV(我很乐意听到它是否/如何变化,实际上)。
我发现在resolve
中封装代码的最佳方法是使用工厂(或服务)。当您将匿名函数写入resolve:
时,如上所述,您实际上是在动态创建服务。因此,您可以参考现有的工厂(或服务) - 只要它使用promises(来自$ q)。然后,您告诉路由器等待该工厂的任何承诺解决。
就我而言,我使用的是在angular-pouch
之上构建的自定义工厂 - 为了简单起见,我们只是说我通过http访问CouchDB。我的工厂使用了已解决的延迟对象。
我从这篇文章中学到了很多东西:http://www.undefinednull.com/2014/02/17/resolve-in-angularjs-routes-explained-as-story。但这是我的代码:
路由器
.config(function ($routeProvider) {
$routeProvider
.when('/',{
controller: 'TestController',
templateUrl: 'templates/myview.html',
resolve: {"pouchDbFactory":"pouchDbFactory"}
})
.otherwise({ redirectTo: '/' });
})
/*snip*/
工厂
.factory('pouchDbFactory', function (myPouch,$q) {
var myDefer = $q.defer();
myDefer.resolve({
db: myPouch,
all: function(){
// another factory is doing the work behind the scenes
// but here's where it returns a pouchDB promise
return this.db.allDocs({include_docs: true});
},
/* more functions, etc. */
});
// Return the promise, along with its result
return myDefer.promise;
});
控制器(这里是解析逻辑被封装的地方)
.controller('TestController',function ($scope,pouchDbFactory){
// this variable makes it to the template right away
$scope.foo = "Hello Foo";
pouchDbFactory.all().then(
function(result){
/* this one gets added to the template once it's fetched
AND the template isn't rendered until that's done --
because it's pouchDbFactory, from "resolve:" property above
*/
$scope.username = result.username;
}
);
})
/*snip*/
我有兴趣知道哪些方法适用于其他人。