scratch – Blame information for rev 125
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
125 | office | 1 | angular.module('bootstrap-tagsinput', []) |
2 | .directive('bootstrapTagsinput', [function() { |
||
3 | |||
4 | function getItemProperty(scope, property) { |
||
5 | if (!property) |
||
6 | return undefined; |
||
7 | |||
8 | if (angular.isFunction(scope.$parent[property])) |
||
9 | return scope.$parent[property]; |
||
10 | |||
11 | return function(item) { |
||
12 | return item[property]; |
||
13 | }; |
||
14 | } |
||
15 | |||
16 | return { |
||
17 | restrict: 'EA', |
||
18 | scope: { |
||
19 | model: '=ngModel' |
||
20 | }, |
||
21 | template: '<select multiple></select>', |
||
22 | replace: false, |
||
23 | link: function(scope, element, attrs) { |
||
24 | $(function() { |
||
25 | if (!angular.isArray(scope.model)) |
||
26 | scope.model = []; |
||
27 | |||
28 | var select = $('select', element); |
||
29 | var typeaheadSourceArray = attrs.typeaheadSource ? attrs.typeaheadSource.split('.') : null; |
||
30 | var typeaheadSource = typeaheadSourceArray ? |
||
31 | (typeaheadSourceArray.length > 1 ? |
||
32 | scope.$parent[typeaheadSourceArray[0]][typeaheadSourceArray[1]] |
||
33 | : scope.$parent[typeaheadSourceArray[0]]) |
||
34 | : null; |
||
35 | |||
36 | select.tagsinput(scope.$parent[attrs.options || ''] || { |
||
37 | typeahead : { |
||
38 | source : angular.isFunction(typeaheadSource) ? typeaheadSource : null |
||
39 | }, |
||
40 | itemValue: getItemProperty(scope, attrs.itemvalue), |
||
41 | itemText : getItemProperty(scope, attrs.itemtext), |
||
42 | confirmKeys : getItemProperty(scope, attrs.confirmkeys) ? JSON.parse(attrs.confirmkeys) : [13], |
||
43 | tagClass : angular.isFunction(scope.$parent[attrs.tagclass]) ? scope.$parent[attrs.tagclass] : function(item) { return attrs.tagclass; } |
||
44 | }); |
||
45 | |||
46 | for (var i = 0; i < scope.model.length; i++) { |
||
47 | select.tagsinput('add', scope.model[i]); |
||
48 | } |
||
49 | |||
50 | select.on('itemAdded', function(event) { |
||
51 | if (scope.model.indexOf(event.item) === -1) |
||
52 | scope.model.push(event.item); |
||
53 | }); |
||
54 | |||
55 | select.on('itemRemoved', function(event) { |
||
56 | var idx = scope.model.indexOf(event.item); |
||
57 | if (idx !== -1) |
||
58 | scope.model.splice(idx, 1); |
||
59 | }); |
||
60 | |||
61 | // create a shallow copy of model's current state, needed to determine |
||
62 | // diff when model changes |
||
63 | var prev = scope.model.slice(); |
||
64 | scope.$watch("model", function() { |
||
65 | var added = scope.model.filter(function(i) {return prev.indexOf(i) === -1;}), |
||
66 | removed = prev.filter(function(i) {return scope.model.indexOf(i) === -1;}), |
||
67 | i; |
||
68 | |||
69 | prev = scope.model.slice(); |
||
70 | |||
71 | // Remove tags no longer in binded model |
||
72 | for (i = 0; i < removed.length; i++) { |
||
73 | select.tagsinput('remove', removed[i]); |
||
74 | } |
||
75 | |||
76 | // Refresh remaining tags |
||
77 | select.tagsinput('refresh'); |
||
78 | |||
79 | // Add new items in model as tags |
||
80 | for (i = 0; i < added.length; i++) { |
||
81 | select.tagsinput('add', added[i]); |
||
82 | } |
||
83 | }, true); |
||
84 | }); |
||
85 | } |
||
86 | }; |
||
87 | }]); |