AngularJS
ngModelController를 사용한 지시어
수색…
간단한 제어 : 등급
다음과 같이 사용하기위한 간단한 컨트롤, 등급 위젯을 만들어 보겠습니다.
<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">✖</span>'
};
});
키 포인트:
-
ngModel.$render
를 구현하여 모델의 뷰 값 을 보기 로 전송합니다. - 뷰 값을 업데이트해야한다고 생각할 때마다
ngModel.$setViewValue()
호출ngModel.$setViewValue()
. - 물론 컨트롤을 매개 변수화 할 수 있습니다. 앵글> = 1.5 인 경우 매개 변수에
'<'
범위 바인딩을 사용하여 입력 - 단방향 바인딩을 명확하게 나타냅니다. 매개 변수가 변경 될 때마다 조치를 취해야하는 경우 JavaScript 속성 (Object.defineProperty()
참조)을 사용하여 몇 개의 시계를 저장할 수 있습니다.
참고 1 : 구현을 지나치게 복잡하게 만들지 않으려면 rating 값이 배열에 삽입됩니다 ( ctrl.options
. 이것은 필요하지 않습니다. 보다 효율적이지만 복잡한 구현은 min
/ max
변경시 등급 삽입 / 제거에 DOM 조작을 사용할 수 있습니다.
참고 2 : '<'
범위 바인딩을 제외하고이 예제는 Angular <1.5로 사용할 수 있습니다. Angular> = 1.5 인 경우 컨트롤러의 생성자에서 수행하는 대신 $onInit()
라이프 사이클 후크를 사용하여 min
및 max
를 초기화하는 것이 좋습니다.
그리고 필요한 바이올린 : 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>'
};
});
키 포인트:
- 우리는 객체를 편집 중입니다. 우리는 부모로부터 우리에게 주어진 객체를 직접 변경하고 싶지 않습니다 (우리는 모델이 불변의 원칙과 호환되기를 바랍니다). 그래서 우리는 편집중인 객체에 얕은 시계를 만들고 속성이 변경 될 때마다
$setViewValue()
모델을 업데이트합니다. 우리는 부모에게 사본 을 전달합니다. - 모델이 바깥에서 바뀔 때마다 우리는 복사본을 복사하여 범위에 저장합니다. 불변의 원칙은 내부 사본이 불변은 아니지만 외부는 매우 잘 될 수 있습니다. 또한 모델에서 푸시 한 변경 사항을 감시자가 트리거하지 않도록 watch (
this_unwatch();this._makeWatch();
)를 다시 작성합니다. UI에서 변경 한 사항에 대해서만 트리거가 실행되기를 원합니다. - 다른 점은 위, 우리가 구현하는 것이
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/