基於angular實現等待長操做時鎖定頁面

因爲網絡等各方面緣由,有時執行一個ajax操做時有時須要等待比較長的時間,若是不進行特殊處理就沒法知道當前操做的狀態,嚴重影響用戶體驗。web

比較常見的解決方案是執行可能的長操做前先打開一個蒙版,覆蓋頁面,經過動畫指示當前頁面處於執行狀態,得到返回結果後在關閉蒙版。這種方式雖然能夠很清晰的展示正在等待操做結果的狀態,可是對於網絡條件比較好,操做結果很快就返回的狀況,會給用戶形成一種頁面閃爍的感受,也會影響用戶體驗。ajax

爲了解決上面的問題,考慮利用angular的directive對執行長操做時須要進行控制的元素進行設置,實現以元素爲單位進行頁面狀態的控制。promise

須要進行狀態控制的元素有幾類:一、input元素,執行長操做時它們應該處於disabled狀態;二、button等發起操做的元素,它們應該出於disabled的狀態,同時發起正在執行的長操做的元素應該出於running的狀態,例如:經過動畫。網絡

實現思路以下:
一、定義directive,tms-lockapp

app = angular.module('app', []);
app.directive('tmsLock', function() {
    return {
        restrict: 'A',
        scope: {
            lock: '=tmsLock'
        },
        priority: 99,
        compile: function(tElem, tAttrs) {
            var originalFn, lockableFn;
            if (tAttrs.tmsLockPromoter === 'Y' && tAttrs.ngClick) {
                originalFn = tAttrs.ngClick;
                lockableFn = '__lockable__' + originalFn;
                tAttrs.ngClick = lockableFn;
            }
            return {
                pre: function(scope, iElem, iAttrs) {
                    if (lockableFn) {
                        scope.$parent[lockableFn.replace(/\(.*\)/, '')] = function() {
                            var eleIndicator = document.createElement('div');
                            eleIndicator.classList.add('indicator');
                            scope.lock = true;
                            iElem.addClass('tms-lock-running');
                            iElem.append(eleIndicator);
                            scope.$parent[originalFn.replace(/\(.*\)/, '')].apply(scope, arguments).then(function() {
                                scope.lock = false;
                                iElem.removeClass('tms-lock-running');
                                iElem[0].removeChild(eleIndicator);
                            });
                        };
                    }
                    scope.$watch('lock', function(locked) {
                        if (locked === true) {
                            iElem.addClass('tms-locked');
                            iAttrs.$set('disabled', true);
                        } else if (locked === false) {
                            iElem.removeClass('tms-locked');
                            iAttrs.$set('disabled', undefined);
                        }
                    });
                }
            }
        }
    }
});
app.controller('ctrl', ['$scope', '$q', '$timeout', function($scope, $q, $timeout) {
    $scope.lock = false;
    $scope.longFn = function() {
        var defer;
        defer = $q.defer();
        $timeout(function() {
            defer.resolve();
        }, 10000);
        return defer.promise;
    };
    $scope.otherFn = function() {
        // do nothing
    };
}]);

二、設置運行狀態的樣式動畫

@keyframes tmsRunning{
    0%{transform:rotate(0deg);}
    12%{transform:rotate(45deg);}
    25%{transform:rotate(90deg);}
    37%{transform:rotate(135deg);}
    50%{transform:rotate(180deg);}
    62%{transform:rotate(225deg);}
    75%{transform:rotate(270deg);}
    87%{transform:rotate(315deg);}
    100%{transform:rotate(360deg);}
}
@-webkit-keyframes tmsRunning{
    0%{-webkit-transform:rotate(0deg);}
    12%{-webkit-transform:rotate(45deg);}
    25%{-webkit-transform:rotate(90deg);}
    37%{-webkit-transform:rotate(135deg);}
    50%{-webkit-transform:rotate(180deg);}
    62%{-webkit-transform:rotate(225deg);}
    75%{-webkit-transform:rotate(270deg);}
    87%{-webkit-transform:rotate(315deg);}
    100%{-webkit-transform:rotate(360deg);}
}
@-moz-keyframes tmsRunning{
    0%{-moz-transform:rotate(0deg);}
    12%{-moz-transform:rotate(45deg);}
    25%{-moz-transform:rotate(90deg);}
    37%{-moz-transform:rotate(135deg);}
    50%{-moz-transform:rotate(180deg);}
    62%{-moz-transform:rotate(225deg);}
    75%{-moz-transform:rotate(270deg);}
    87%{-moz-transform:rotate(315deg);}
    100%{-moz-transform:rotate(360deg);}
}
@-o-keyframes tmsRunning{
    0%{-o-transform:rotate(0deg);}
    12%{-o-transform:rotate(45deg);}
    25%{-o-transform:rotate(90deg);}
    37%{-o-transform:rotate(135deg);}
    50%{-o-transform:rotate(180deg);}
    62%{-o-transform:rotate(225deg);}
    75%{-o-transform:rotate(270deg);}
    87%{-o-transform:rotate(315deg);}
    100%{-o-transform:rotate(360deg);}
}
.btn.tms-lock-running {
    position: relative;
}
.btn.tms-lock-running .indicator::after {
    content: '';
    position: absolute;
    left: 50%;
    top: 4px;
    bottom: 4px;
    width: 4px;
    margin-left: -2px;
    background: #333;
}
.btn.tms-lock-running .indicator {
    position: absolute;
    left: 50%;
    top: 50%;
    margin-left: -1em;
    margin-top: -1em;
    border-radius: 1em;
    width: 2em;
    height: 2em;
    border: 2px solid #333;
    background: #fff;
    animation: tmsRunning 1s infinite;
    -webkit-animation: tmsRunning 1s infinite;
    -moz-animation: tmsRunning 1s infinite;
    -o-animation: tmsRunning 1s infinite;
}

三、給須要鎖定的元素添加屬性rest

<input class='form-control' type='text' tms-lock="lock">
<button class='btn btn-default' tms-lock="lock" tms-lock-promoter="Y" ng-click="longFn()">long action</button>
<button class='btn btn-default' tms-lock="lock" ng-click="otherFn()">other action</button>

示例code

其餘仍在考慮的問題:一、是否容許用戶主動解除頁面鎖定狀態?二、是否須要對硬的頁面導航操做,例如:後退,進行控制?orm

相關文章
相關標籤/搜索