-
-
Notifications
You must be signed in to change notification settings - Fork 208
/
checklist-model.js
175 lines (148 loc) · 5.24 KB
/
checklist-model.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/**
* Checklist-model
* AngularJS directive for list of checkboxes
* https://github.com/vitalets/checklist-model
* License: MIT http://opensource.org/licenses/MIT
*/
/* commonjs package manager support (eg componentjs) */
if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){
module.exports = 'checklist-model';
}
angular.module('checklist-model', [])
.directive('checklistModel', ['$parse', '$compile', function($parse, $compile) {
// contains
function contains(arr, item, comparator) {
if (angular.isArray(arr)) {
for (var i = arr.length; i--;) {
if (comparator(arr[i], item)) {
return true;
}
}
}
return false;
}
// add
function add(arr, item, comparator) {
arr = angular.isArray(arr) ? arr : [];
if(!contains(arr, item, comparator)) {
arr.push(item);
}
return arr;
}
// remove
function remove(arr, item, comparator) {
if (angular.isArray(arr)) {
for (var i = arr.length; i--;) {
if (comparator(arr[i], item)) {
arr.splice(i, 1);
break;
}
}
}
return arr;
}
// http://stackoverflow.com/a/19228302/1458162
function postLinkFn(scope, elem, attrs) {
// exclude recursion, but still keep the model
var checklistModel = attrs.checklistModel;
attrs.$set("checklistModel", null);
// compile with `ng-model` pointing to `checked`
$compile(elem)(scope);
attrs.$set("checklistModel", checklistModel);
// getter for original model
var checklistModelGetter = $parse(checklistModel);
var checklistChange = $parse(attrs.checklistChange);
var checklistBeforeChange = $parse(attrs.checklistBeforeChange);
var ngModelGetter = $parse(attrs.ngModel);
var comparator = function (a, b) {
if(!isNaN(a) && !isNaN(b)) {
return String(a) === String(b);
} else {
return angular.equals(a,b);
}
};
if (attrs.hasOwnProperty('checklistComparator')){
if (attrs.checklistComparator[0] == '.') {
var comparatorExpression = attrs.checklistComparator.substring(1);
comparator = function (a, b) {
return a[comparatorExpression] === b[comparatorExpression];
};
} else {
comparator = $parse(attrs.checklistComparator)(scope.$parent);
}
}
// watch UI checked change
var unbindModel = scope.$watch(attrs.ngModel, function(newValue, oldValue) {
if (newValue === oldValue) {
return;
}
if (checklistBeforeChange && (checklistBeforeChange(scope) === false)) {
ngModelGetter.assign(scope, contains(checklistModelGetter(scope.$parent), getChecklistValue(), comparator));
return;
}
setValueInChecklistModel(getChecklistValue(), newValue);
if (checklistChange) {
checklistChange(scope);
}
});
// watches for value change of checklistValue
var unbindCheckListValue = scope.$watch(getChecklistValue, function(newValue, oldValue) {
if( newValue != oldValue && angular.isDefined(oldValue) && scope[attrs.ngModel] === true ) {
var current = checklistModelGetter(scope.$parent);
checklistModelGetter.assign(scope.$parent, remove(current, oldValue, comparator));
checklistModelGetter.assign(scope.$parent, add(current, newValue, comparator));
}
}, true);
var unbindDestroy = scope.$on('$destroy', destroy);
function destroy() {
unbindModel();
unbindCheckListValue();
unbindDestroy();
}
function getChecklistValue() {
return attrs.checklistValue ? $parse(attrs.checklistValue)(scope.$parent) : attrs.value;
}
function setValueInChecklistModel(value, checked) {
var current = checklistModelGetter(scope.$parent);
if (angular.isFunction(checklistModelGetter.assign)) {
if (checked === true) {
checklistModelGetter.assign(scope.$parent, add(current, value, comparator));
} else {
checklistModelGetter.assign(scope.$parent, remove(current, value, comparator));
}
}
}
// declare one function to be used for both $watch functions
function setChecked(newArr, oldArr) {
if (checklistBeforeChange && (checklistBeforeChange(scope) === false)) {
setValueInChecklistModel(getChecklistValue(), ngModelGetter(scope));
return;
}
ngModelGetter.assign(scope, contains(newArr, getChecklistValue(), comparator));
}
// watch original model change
// use the faster $watchCollection method if it's available
if (angular.isFunction(scope.$parent.$watchCollection)) {
scope.$parent.$watchCollection(checklistModel, setChecked);
} else {
scope.$parent.$watch(checklistModel, setChecked, true);
}
}
return {
restrict: 'A',
priority: 1000,
terminal: true,
scope: true,
compile: function(tElement, tAttrs) {
if (!tAttrs.checklistValue && !tAttrs.value) {
throw 'You should provide `value` or `checklist-value`.';
}
// by default ngModel is 'checked', so we set it if not specified
if (!tAttrs.ngModel) {
// local scope var storing individual checkbox model
tAttrs.$set("ngModel", "checked");
}
return postLinkFn;
}
};
}]);