簡單的控制等級

讓我們構建一個簡單的控制元件,一個評級小部件,旨在用作:

<rating min="0" max="5" nullifier="true" ng-model="data.rating"></rating>

現在沒有花哨的 CSS; 這將呈現為:

0 1 2 3 4 5 x

點選一個數字可選擇該評級; 然後單擊 x 將評級設定為 null。

app.directive('rating', function() {

    function RatingController() {
        this._ngModel = null;
        this.rating = null;
        this.options = null;
        this.min = typeof this.min === 'number' ? this.min : 1;
        this.max = typeof this.max === 'number' ? this.max : 5;
    }
    
    RatingController.prototype.setNgModel = function(ngModel) {
        this._ngModel = ngModel;
        
        if( ngModel ) {
            // KEY POINT 1
            ngModel.$render = this._render.bind(this);
        }
    };
    
    RatingController.prototype._render = function() {
        this.rating = this._ngModel.$viewValue != null ? this._ngModel.$viewValue : -Number.MAX_VALUE;
    };
    
    RatingController.prototype._calculateOptions = function() {
        if( this.min == null || this.max == null ) {
            this.options = [];
        }
        else {
            this.options = new Array(this.max - this.min + 1);
            for( var i=0; i < this.options.length; i++ ) {
                this.options[i] = this.min + i;
            }
        }
    };
    
    RatingController.prototype.setValue = function(val) {
        this.rating = val;
        // KEY POINT 2
        this._ngModel.$setViewValue(val);
    };
    
    // KEY POINT 3
    Object.defineProperty(RatingController.prototype, 'min', {
        get: function() {
            return this._min;
        },
        set: function(val) {
            this._min = val;
            this._calculateOptions();
        }
    });
    
    Object.defineProperty(RatingController.prototype, 'max', {
        get: function() {
            return this._max;
        },
        set: function(val) {
            this._max = val;
            this._calculateOptions();
        }
    });
    
    return {
        restrict: 'E',
        scope: {
            // KEY POINT 3
            min: '<?',
            max: '<?',
            nullifier: '<?'
        },
        bindToController: true,
        controllerAs: 'ctrl',
        controller: RatingController,
        require: ['rating', 'ngModel'],
        link: function(scope, elem, attrs, ctrls) {
            ctrls[0].setNgModel(ctrls[1]);
        },
        template:
            '<span ng-repeat="o in ctrl.options" href="#" class="rating-option" ng-class="{\'rating-option-active\': o <= ctrl.rating}" ng-click="ctrl.setValue(o)">{{ o }}</span>' +
            '<span ng-if="ctrl.nullifier" ng-click="ctrl.setValue(null)" class="rating-nullifier">&#10006;</span>'
    };
});

關鍵點:

  1. 實現 ngModel.$render 以將模型的檢視值傳輸到檢視。
  2. 只要你覺得應更新檢視值,請呼叫 ngModel.$setViewValue()
  3. 控制當然可以引數化; 使用'<'範圍繫結引數,如果在 Angular> = 1.5 中清楚地表示輸入 - 單向繫結。如果你必須在引數更改時執行操作,則可以使用 JavaScript 屬性(請參閱 Object.defineProperty())來儲存一些手錶。

注 1:為了不使實現過於複雜,將評級值插入陣列中 - ctrl.options。這不是必需的; 當 min / max 發生變化時,更高效但也更復雜的實現可以使用 DOM 操作來插入/刪除評級。

注 2:除'<'範圍繫結外,此示例可用於 Angular <1.5。如果你在 Angular> = 1.5,那麼將它轉換為一個元件並使用 $onInit() 生命週期鉤子來初始化 minmax,而不是在控制器的建構函式中這樣做是個好主意。

一個必要的小提琴: https//jsfiddle.net/h81mgxma/