AngularJS
NgModelController का उपयोग करने वाले निर्देश
खोज…
एक सरल नियंत्रण: रेटिंग
आइए हम एक सरल नियंत्रण बनाते हैं, एक रेटिंग विजेट, जिसका उपयोग इस प्रकार किया जाना है:
<rating min="0" max="5" nullifier="true" ng-model="data.rating"></rating>
अब के लिए कोई फैंसी सीएसएस; इस रूप में प्रस्तुत करना होगा:
0 1 2 3 4 5 x
एक नंबर पर क्लिक करने से उस रेटिंग का चयन होता है; और "x" पर क्लिक करने से रेटिंग शून्य हो जाती है।
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.$render
मॉडल के दृश्य मूल्य को आपके दृश्य में स्थानांतरितngModel.$render
लिएngModel.$render
करें। - जब भी आपको लगता है कि अद्यतन मूल्य को अपडेट किया जाना चाहिए, तो
ngModel.$setViewValue()
कॉल करें। - नियंत्रण निश्चित रूप से मानकीकृत किया जा सकता है; मापदंडों के लिए
'<'
स्कोप बाइंडिंग का उपयोग करें, यदि कोणीय> = 1.5 में इनपुट को स्पष्ट रूप से इंगित करने के लिए - एक तरह से बाध्यकारी। यदि आपको कोई पैरामीटर बदलता है तो आपको कार्रवाई करनी होगी, आप कुछ घड़ियों को बचाने के लिए एक जावास्क्रिप्ट संपत्ति (Object.defineProperty()
देखें) का उपयोग कर सकते हैं।
नोट 1: कार्यान्वयन को ctrl.options
नहीं करने के लिए, रेटिंग मानों को एक सरणी में डाला जाता है - ctrl.options
। इसकी जरूरत नहीं है; अधिक कुशल, लेकिन साथ ही अधिक जटिल, कार्यान्वयन min
/ max
परिवर्तन होने पर रेटिंग सम्मिलित / हटाने के लिए DOM हेरफेर का उपयोग कर सकता है।
नोट 2: '<'
स्कोप बाइंडिंग के अपवाद के साथ, इस उदाहरण का उपयोग कोणीय <1.5 में किया जा सकता है। यदि आप कोणीय> = 1.5 पर हैं, तो इसे एक घटक में $onInit()
और नियंत्रक के निर्माता में ऐसा करने के बजाय, min
और max
को इनिशियलाइज़ करने के लिए $onInit()
जीवनचक्र हुक का उपयोग करें।
और एक आवश्यक पहेली: 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()
साथ अद्यतन करते हैं जब भी कोई संपत्ति बदलती है। हम अपने माता-पिता को एक प्रति देते हैं। - जब भी मॉडल बाहर से बदलता है, हम इसे कॉपी करते हैं और कॉपी को अपने दायरे में सहेजते हैं। फिर से अपरिवर्तनीयता के सिद्धांत, हालांकि आंतरिक प्रति अपरिवर्तनीय नहीं है, बाहरी बहुत अच्छी तरह से हो सकता है। इसके अतिरिक्त हम मॉडल द्वारा हमारे लिए धकेल दिए गए परिवर्तनों के लिए द्रष्टा को ट्रिगर करने से बचने के लिए घड़ी (
this_unwatch();this._makeWatch();
) को फिर से बनाते हैं। (हम केवल यूआई में किए गए परिवर्तनों के लिए घड़ी को ट्रिगर करना चाहते हैं।) - अन्य कि ऊपर के बिंदु, हम
ngModel.$render()
लागूngModel.$render()
औरngModel.$setViewValue()
कॉल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()
बजाय angular.copy()
उपयोग करें। एक अतिरिक्त लाभ यह है कि नियंत्रक दो नियंत्रणों के लिए समान हो जाता है और आसानी से कुछ सामान्य मॉड्यूल में निकाला जा सकता है।
द फिडेल: https://jsfiddle.net/3tzyqfko/2/
फिडेल के दो संस्करणों ने नियंत्रकों के सामान्य कोड निकाले हैं: https://jsfiddle.net/agj4cp0e/ और https://jsfiddle.net/ugb6Lw8b/