有段时间没写博客了,前段时间在看 Underscore 源码所以写的多了点,这段时间还是在忙自己的其他项目去了,还是有不少收获的。
Angular 视图过渡动画
之前使用 angular-promise-button
这个模块实现了按钮的自动变化,以前自己是用很多标志位来判断特别二。不仅如此,页面切换动画也是用标志位判断,这样就特别不好维护特别不优雅,上次重构的时候就把这些全部去掉了。但是问题来了,页面数据未到达时候页面就渲染肯定会造成视觉上的问题,怎么解决呢。
我们都想写一些应用很广的代码,比如指令,比如上面这个 angular-promise-button
模块等等。其实要解决上面的问题,也是几行代码就可以解决的事情了。
我所使用的是 Angular 的 ui-router。ngRoute 应该也差不多。
在 ui-router 中可以使用 resolve 达到在控制器初始化以及视图加载前确保数据到达。比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| $stateProvider .state('me',{ url: '/me', controller: 'MeCtrl', templateUrl: 'me/me_tpl.html', controllerAs: 'vm', nav: true, resolve: { me: function(userservice) { return userservice.getUserInfo() .then(response => response); } } })
|
只有 resolve 中的全部方法执行完后,才会开始初始化控制和加载视图。这个数据如果在控制器或者视图中要使用,可以在控制器中进行依赖注入。例如上面这个我的控制器是这样写的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| (function(){ 'use strict'; angular .module('index') .controller('MeCtrl', MeCtrl); MeCtrl.$inject = ['me']; function MeCtrl(me) { let vm = this; vm.user = me; } }());
|
resolve中的方法是阻塞页面进行的,这样就会带来问题了,如果数据请求比较久将导致网站停滞,我们这时候就希望可以有过渡动画出来。要达到全局过渡效果的作用,可以直接监听 $rootScope
中的三个状态即 $stateChangeStart
和 $stateChangeSuccess
以及 $stateChangeError
事件。例如上面这个例子中,当我们触发 me
这个 state 时,也就触发了 $rootScope 上的 $stateChangeStart
事件,当处理结束后将出发 $stateChangeSuccess
并加载视图, 处理失败就会触发 $stateChangeError
事件。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| angular .module('index', [ 'ui.router', 'ui.bootstrap', 'ngAnimate', 'ngSanitize', 'ngTouch', 'infinite-scroll', 'angularPromiseButtons' ]) .config(config) .run(($state,$rootScope) => { $rootScope.$state = $state; $rootScope.$on("$stateChangeStart", (event, toState, toStateParams, fromState, fromStateParams) => { var isLoading = toState.resolve; if(!isLoading) { for (var prop in toState.views) { if (toState.views.hasOwnProperty(prop)) { if(toState.views[prop].resolve) { isLoading = true; break; } } } } if (isLoading) { $rootScope.loading = true; } }); $rootScope.$on("$stateChangeSuccess", (event, toState, toParams, fromState, fromParams) => { $rootScope.loading = false; }); $rootScope.$on("$stateChangeError", (event, toState, toParams, fromState, fromParams, error) => { $rootScope.loading = false; }); });
|
入口页面,省去了其他代码,这里第一行就是视图,第二行是加载动画,通过ng-show来控制显示。第三行是引入导航栏,这个在后面会说下。
1 2 3
| <div ui-view class="uiview" ng-show="!$root.loading"></div> <div class="cssload-thecube" ng-show="$root.loading"> loading... </div> <div ng-show="$state.current.nav" ng-include="'navbar/navbar_tpl.html'"></div>
|
可以看到上面的代码中是监听了 $stateChangeStart
事件,然后获取目标 state 上的 resolve 方法,当 state 上的 resolve 方法全部结束后,$rootScope.loading
设置为 false,否则保持为 true。
当监听到 $stateChangeSuccess
或者 $stateChangeError
事件时,置 $rootScope.loading
为 false,退出过渡动画。在视图中可以使用 $root
得到 $rootScope
。
可以看到这里有很多参数,可见其功能是很强大的。