수색…


간단한 제어 : 등급

다음과 같이 사용하기위한 간단한 컨트롤, 등급 위젯을 만들어 보겠습니다.

<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() 호출 ngModel.$setViewValue() .
  3. 물론 컨트롤을 매개 변수화 할 수 있습니다. 앵글> = 1.5 인 경우 매개 변수에 '<' 범위 바인딩을 사용하여 입력 - 단방향 바인딩을 명확하게 나타냅니다. 매개 변수가 변경 될 때마다 조치를 취해야하는 경우 JavaScript 속성 ( Object.defineProperty() 참조)을 사용하여 몇 개의 시계를 저장할 수 있습니다.

참고 1 : 구현을 지나치게 복잡하게 만들지 않으려면 rating 값이 배열에 삽입됩니다 ( ctrl.options . 이것은 필요하지 않습니다. 보다 효율적이지만 복잡한 구현은 min / max 변경시 등급 삽입 / 제거에 DOM 조작을 사용할 수 있습니다.

참고 2 : '<' 범위 바인딩을 제외하고이 예제는 Angular <1.5로 사용할 수 있습니다. Angular> = 1.5 인 경우 컨트롤러의 생성자에서 수행하는 대신 $onInit() 라이프 사이클 후크를 사용하여 minmax 를 초기화하는 것이 좋습니다.

그리고 필요한 바이올린 : https://jsfiddle.net/h81mgxma/

몇 가지 복잡한 컨트롤 : 전체 개체 편집

커스텀 컨트롤은 프리미티브 같은 사소한 것들로 제한 될 필요가 없다. 더 재미있는 것을 편집 할 수 있습니다. 여기에서는 사용자 편집과 주소 편집을위한 두 가지 유형의 사용자 정의 컨트롤을 제시합니다. 주소 컨트롤은 사람의 주소를 편집하는 데 사용됩니다. 사용 예는 다음과 같습니다.

<input-person ng-model="data.thePerson"></input-person>
<input-address ng-model="data.thePerson.address"></input-address>

이 예제의 모델은 의도적으로 단순하다.

function Person(data) {
  data = data || {};
  this.name = data.name;
  this.address = data.address ? new Address(data.address) : null;
}

function Address(data) {
  data = data || {};
  this.street = data.street;
  this.number = data.number;
}

주소 편집기 :

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

    InputAddressController.$inject = ['$scope'];
    function InputAddressController($scope) {
        this.$scope = $scope;
        this._ngModel = null;
        this.value = null;
        this._unwatch = angular.noop;
    }

    InputAddressController.prototype.setNgModel = function(ngModel) {
        this._ngModel = ngModel;
        
        if( ngModel ) {
            // KEY POINT 3
            ngModel.$render = this._render.bind(this);
        }
    };
    
    InputAddressController.prototype._makeWatch = function() {
        // KEY POINT 1
        this._unwatch = this.$scope.$watchCollection(
            (function() {
                return this.value;
            }).bind(this),
            (function(newval, oldval) {
                if( newval !== oldval ) { // skip the initial trigger
                    this._ngModel.$setViewValue(newval !== null ? new Address(newval) : null);
                }
            }).bind(this)
        );
    };
    
    InputAddressController.prototype._render = function() {
        // KEY POINT 2
        this._unwatch();
        this.value = this._ngModel.$viewValue ? new Address(this._ngModel.$viewValue) : null;
        this._makeWatch();
    };

    return {
        restrict: 'E',
        scope: {},
        bindToController: true,
        controllerAs: 'ctrl',
        controller: InputAddressController,
        require: ['inputAddress', 'ngModel'],
        link: function(scope, elem, attrs, ctrls) {
            ctrls[0].setNgModel(ctrls[1]);
        },
        template:
            '<div>' +
                '<label><span>Street:</span><input type="text" ng-model="ctrl.value.street" /></label>' +
                '<label><span>Number:</span><input type="text" ng-model="ctrl.value.number" /></label>' +
            '</div>'
    };
});

키 포인트:

  1. 우리는 객체를 편집 중입니다. 우리는 부모로부터 우리에게 주어진 객체를 직접 변경하고 싶지 않습니다 (우리는 모델이 불변의 원칙과 호환되기를 바랍니다). 그래서 우리는 편집중인 객체에 얕은 시계를 만들고 속성이 변경 될 때마다 $setViewValue() 모델을 업데이트합니다. 우리는 부모에게 사본 을 전달합니다.
  2. 모델이 바깥에서 바뀔 때마다 우리는 복사본을 복사하여 범위에 저장합니다. 불변의 원칙은 내부 사본이 불변은 아니지만 외부는 매우 잘 될 수 있습니다. 또한 모델에서 푸시 한 변경 사항을 감시자가 트리거하지 않도록 watch ( this_unwatch();this._makeWatch(); )를 다시 작성합니다. UI에서 변경 한 사항에 대해서만 트리거가 실행되기를 원합니다.
  3. 다른 점은 위, 우리가 구현하는 것이 ngModel.$render() 와 전화 ngModel.$setViewValue() 우리는 간단한 제어 (등급의 예를 참조) 것 등을.

사용자 정의 컨트롤의 코드는 거의 동일합니다. 템플릿은 <input-address> 사용하고 있습니다. 보다 진보 된 구현에서 우리는 재사용 가능한 모듈에서 컨트롤러를 추출 할 수있었습니다.

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

    InputPersonController.$inject = ['$scope'];
    function InputPersonController($scope) {
        this.$scope = $scope;
        this._ngModel = null;
        this.value = null;
        this._unwatch = angular.noop;
    }

    InputPersonController.prototype.setNgModel = function(ngModel) {
        this._ngModel = ngModel;
        
        if( ngModel ) {
            ngModel.$render = this._render.bind(this);
        }
    };
    
    InputPersonController.prototype._makeWatch = function() {
        this._unwatch = this.$scope.$watchCollection(
            (function() {
                return this.value;
            }).bind(this),
            (function(newval, oldval) {
                if( newval !== oldval ) { // skip the initial trigger
                    this._ngModel.$setViewValue(newval !== null ? new Person(newval) : null);
                }
            }).bind(this)
        );
    };
    
    InputPersonController.prototype._render = function() {
        this._unwatch();
        this.value = this._ngModel.$viewValue ? new Person(this._ngModel.$viewValue) : null;
        this._makeWatch();
    };

    return {
        restrict: 'E',
        scope: {},
        bindToController: true,
        controllerAs: 'ctrl',
        controller: InputPersonController,
        require: ['inputPerson', 'ngModel'],
        link: function(scope, elem, attrs, ctrls) {
            ctrls[0].setNgModel(ctrls[1]);
        },
        template:
            '<div>' +
                '<label><span>Name:</span><input type="text" ng-model="ctrl.value.name" /></label>' +
                '<input-address ng-model="ctrl.value.address"></input-address>' +
            '</div>'
    };
});

참고 : 여기에는 객체가 입력됩니다. 즉, 적절한 생성자가 있습니다. 이것은 의무 사항은 아닙니다. 모델은 일반 JSON 객체가 될 수 있습니다. 이 경우에는 생성자 대신 angular.copy() 사용하십시오. 추가 된 장점은 컨트롤러가 두 컨트롤에 대해 동일하게되고 일부 공통 모듈에 쉽게 추출 될 수 있다는 것입니다.

바이올린 : https://jsfiddle.net/3tzyqfko/2/

컨트롤러의 공통 코드를 추출한 두 가지 버전의 바이올린 : https://jsfiddle.net/agj4cp0e/https://jsfiddle.net/ugb6Lw8b/



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow