Skip to content
This repository was archived by the owner on Oct 2, 2019. It is now read-only.

Commit 63889f6

Browse files
committed
Refactor directives, using controller from uiSelect
1 parent e23e9b4 commit 63889f6

File tree

5 files changed

+134
-83
lines changed

5 files changed

+134
-83
lines changed

demo/index.html

+2-5
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,8 @@
44
<meta charset="utf-8">
55
<title>Angular ui-select</title>
66

7-
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js"></script>
7+
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.2/angular.min.js"></script>
88

9-
<!-- https://github.com/angular-ui/ui-utils/blob/master/modules/keypress/keypress.js -->
10-
<script src="keypress.js"></script>
11-
129
<script src="../src/select.js"></script>
1310
<script src="demo.js"></script>
1411

@@ -40,7 +37,7 @@
4037

4138
<ui-select ng-model="data.custom" style="width:300px">
4239
<match placeholder="Pick one...">{{$select.selected.name}}</match>
43-
<choices data="item in data.items | filter : $select.search">
40+
<choices data="data.items | filter : $select.search">
4441
<div ng-bind-html="trustAsHtml((item.name | highlight:$select.search))"/></div>
4542
<div> {{item.email}} </div>
4643
</choices>

src/select.js

+128-74
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
angular.module('ui.select', ['ui.keypress'])
1+
angular.module('ui.select', [])
22

33
.constant('uiSelectConfig', {
44
defaultTheme: 'select2',
@@ -13,75 +13,86 @@ angular.module('ui.select', ['ui.keypress'])
1313
return '../src/' + theme + '/select.tpl.html';
1414
},
1515
replace: true,
16-
require: 'ngModel',
16+
require: ['uiSelect', 'ngModel'],
1717
transclude: true,
1818
scope: true,
19-
compile: function(tElement, tAttrs, transcludeFn) {
20-
return function($scope, $elm, $attrs, ngModel){
21-
transcludeFn($scope, function(clone) {
22-
$scope.open = false;
19+
controller: ['$scope', '$element', '$attrs',
20+
function uiSelectCtrl($scope, $element, $attrs) {
21+
22+
var ctrl = this;
2323

24-
//Set transcluded elements to their correct position on template
25-
var transcluded = angular.element('<div/>').append(clone);
26-
var transMatch = uiSelectElements.byClassName(transcluded[0],'ui-select-match');
27-
var transChoices = uiSelectElements.byClassName(transcluded[0],'ui-select-choices');
28-
uiSelectElements.byClassName($elm[0],'ui-select-match').replaceWith(transMatch);
29-
uiSelectElements.byClassName($elm[0],'ui-select-choices').replaceWith(transChoices);
30-
31-
var input = $elm.find('input');
32-
$scope.activate = function($event){
33-
if ($event) $event.stopPropagation(); // Prevent bubbling
34-
$scope.open = true;
35-
//Give it time to appear before focus
36-
$timeout(function(){
37-
input[0].focus();
38-
});
39-
};
40-
$scope.$watch('$select.search', function(){
41-
$scope.$select.index = 0;
24+
this.activate = function($event){
25+
if ($event) $event.stopPropagation(); // Prevent bubbling
26+
$scope.open = true;
27+
//Give it time to appear before focus
28+
$timeout(function(){
29+
ctrl.input[0].focus();
4230
});
43-
$scope.up = function(){
44-
if ($scope.$select.index > 0){
45-
$scope.$select.index--;
46-
$scope.ensureHighlightVisible();
47-
}
48-
};
49-
$scope.down = function(){
50-
items = uiSelectElements.byClassName($elm[0],'ui-select-choices-row').length -1;
51-
if ($scope.$select.index < items) {
52-
$scope.$select.index++;
53-
$scope.ensureHighlightVisible();
54-
} else {
55-
$scope.$select.index = items;
56-
}
57-
};
58-
$scope.$select = function(item){
59-
$scope.$select.selected = item;
60-
ngModel.$setViewValue(item);
61-
ngModel.$render(item);
62-
$scope.close();
63-
};
64-
$scope.close = function() {
65-
$scope.open = false;
66-
$scope.$select.search = "";
67-
};
68-
var dismissClickHandler = function (evt) {
69-
if (angular.element(evt.target).hasClass('ui-select-search')){
70-
return;
71-
}
72-
$scope.close();
73-
$scope.$digest();
74-
};
75-
$document.bind('click', dismissClickHandler);
76-
});
77-
};
31+
};
32+
33+
this.select = function(item){
34+
$scope.$select.selected = item;
35+
this.close();
36+
// Using a watch instead of $scope.ngModel.$setViewValue(item)
37+
};
38+
39+
this.close = function() {
40+
$scope.open = false;
41+
$scope.$select.search = "";
42+
};
43+
44+
this.input = $element.find('input'); //TODO could break if input is at other template
45+
46+
}],
47+
controllerAs: 'uiSelectCtrl',
48+
link: function(scope, element, attrs, controllers, transcludeFn){
49+
50+
scope.open = false;
51+
scope.$select = {}; //Namespace
52+
53+
var uiSelectCtrl = controllers[0];
54+
var ngModelCtrl = controllers[1];
55+
56+
scope.$watch('$select.selected',function(newVal,oldVal){
57+
ngModelCtrl.$setViewValue(newVal);
58+
});
59+
60+
$document.bind('click', function (evt) {
61+
if (angular.element(evt.target).hasClass('ui-select-search')){
62+
return;
63+
}
64+
uiSelectCtrl.close(); //Close if clicking outside
65+
scope.$digest();
66+
});
67+
68+
//Move transcluded elements to their correct position on main template
69+
transcludeFn(scope, function(clone) {
70+
71+
var transcluded = angular.element('<div/>').append(clone);
72+
73+
//Child directives could be uncompiled at this point, so we check both alternatives,
74+
//first for compiled version (by class) or uncompiled (by tag). We place the directives
75+
//at the insertion points that are marked with ui-select-* classes at select.tpl.html
76+
//TODO: If we change directive restrict attribute to EA, we should do some changes here.
77+
78+
var transMatch = uiSelectElements.byClassName(transcluded[0],'ui-select-match');
79+
transMatch = !transMatch.length ? transcluded.find('match') : transMatch;
80+
uiSelectElements.byClassName(element[0],'ui-select-match').replaceWith(transMatch);
81+
82+
var transChoices = uiSelectElements.byClassName(transcluded[0],'ui-select-choices');
83+
transChoices = !transChoices.length ? transcluded.find('choices') : transChoices;
84+
uiSelectElements.byClassName(element[0],'ui-select-choices').replaceWith(transChoices);
85+
86+
});
87+
7888
}
7989
};
8090
})
8191

8292
.directive('choices', function($sce,uiSelectConfig,uiSelectElements) {
93+
var HOT_KEYS = [9, 13, 27, 38, 40];
8394
return {
84-
// require: '^uiSelect',
95+
require: '^uiSelect',
8596
restrict: 'E',
8697
transclude: true,
8798
replace: true,
@@ -90,23 +101,71 @@ angular.module('ui.select', ['ui.keypress'])
90101
var theme = tElement[0].parentElement.getAttribute('theme') || uiSelectConfig.defaultTheme;
91102
return '../src/' + theme + '/choices.tpl.html';
92103
},
93-
compile: function(tElement, tAttrs, transcludeFn) {
94-
uiSelectElements.byClassName(tElement[0],'ui-select-choices-row').attr("ng-repeat", tAttrs.data);
95-
return function(scope, element, attrs){
104+
compile: function(tElement, tAttrs) {
105+
106+
uiSelectElements.byClassName(tElement[0],'ui-select-choices-row')
107+
.attr("ng-repeat", 'item in ' + tAttrs.data)
108+
.attr("ng-mouseenter", '$select.activeIdx=$index')
109+
.attr("ng-click", 'uiSelectCtrl.select(item)');
110+
111+
return function(scope, element, attrs, uiSelectCtrl){
112+
96113
scope.trustAsHtml = function(value) {
97114
return $sce.trustAsHtml(value);
98115
};
116+
99117
var container = element.hasClass('ui-select-choices-content') ? element[0] : uiSelectElements.byClassName(element[0],'ui-select-choices-content')[0];
100-
scope.ensureHighlightVisible = function(){
101-
var highlighted = uiSelectElements.byClassName(element[0],'ui-select-choices-row')[scope.$select.index],
118+
var ensureHighlightVisible = function(){
119+
var rows = uiSelectElements.byClassName(element[0],'ui-select-choices-row');
120+
if (!rows.length) return; //In case its empty
121+
var highlighted = rows[scope.$select.activeIdx],
102122
posY = highlighted.offsetTop + highlighted.clientHeight - container.scrollTop,
103123
maxHeight = 200; //TODO Need to get this value from container.max-height
104-
if (posY>maxHeight){
124+
if (posY > maxHeight){
105125
container.scrollTop += posY-maxHeight;
106-
}else if (posY<highlighted.clientHeight){
126+
}else if (posY < highlighted.clientHeight){
107127
container.scrollTop -= highlighted.clientHeight-posY;
108128
}
109129
};
130+
131+
scope.$watch('$select.search', function(){
132+
scope.$select.activeIdx = 0;
133+
ensureHighlightVisible();
134+
});
135+
136+
//Bind keyboard events related to choices
137+
uiSelectCtrl.input.bind('keydown', function (evt) {
138+
139+
if (HOT_KEYS.indexOf(evt.which) === -1) return; //Exit on regular key
140+
evt.preventDefault();
141+
142+
var rows = uiSelectElements.byClassName(element[0],'ui-select-choices-row');
143+
144+
if (evt.which === 40) { // down(40)
145+
if (scope.$select.activeIdx < rows.length) {
146+
scope.$select.activeIdx = (scope.$select.activeIdx + 1) % rows.length || rows.length - 1 ;
147+
ensureHighlightVisible();
148+
scope.$digest();
149+
}
150+
151+
} else if (evt.which === 38) { // up(38)
152+
if (scope.$select.activeIdx > 0){
153+
scope.$select.activeIdx--;
154+
ensureHighlightVisible();
155+
scope.$digest();
156+
}
157+
158+
} else if (evt.which === 13 || evt.which === 9) { // enter(13) and tab(9)
159+
rows[scope.$select.activeIdx].click();
160+
161+
} else if (evt.which === 27) { // esc(27)
162+
evt.stopPropagation();
163+
uiSelectCtrl.close();
164+
scope.$digest();
165+
166+
}
167+
});
168+
110169
};
111170
}
112171
};
@@ -122,13 +181,8 @@ angular.module('ui.select', ['ui.keypress'])
122181
var theme = tElement[0].parentElement.getAttribute('theme') || uiSelectConfig.defaultTheme;
123182
return '../src/' + theme + '/match.tpl.html';
124183
},
125-
compile: function(tElement, tAttrs, transcludeFn) {
126-
return function($scope, $elm, $attrs, ngModel){
127-
transcludeFn($scope, function(clone) {
128-
$scope.placeholder = tAttrs.placeholder || uiSelectConfig.defaultPlaceholder;
129-
$elm.append(clone);
130-
});
131-
};
184+
link: function(scope, element, attrs){
185+
scope.placeholder = attrs.placeholder || uiSelectConfig.defaultPlaceholder;
132186
}
133187
};
134188
})

src/select2/choices.tpl.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<ul class="ui-select-choices ui-select-choices-content select2-results">
2-
<li class="ui-select-choices-row" ng-class="{'select2-highlighted':$select.index==$index}" ng-click="$select(item)" ng-mouseenter="$select.index=$index">
2+
<li class="ui-select-choices-row" ng-class="{'select2-highlighted':$select.activeIdx==$index}">
33
<div class="select2-result-label" ng-transclude></div>
44
</li>
55
</ul>

src/select2/match.tpl.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
<a class="ui-select-match select2-choice" ng-click="activate($event)">
1+
<a class="ui-select-match select2-choice" ng-click="uiSelectCtrl.activate($event)">
22
<span class="select2-arrow"><b></b></span>
3-
<span class="select2-chosen">{{selectedLabel}}</span>
43
<div ng-hide='$select.selected'>{{placeholder}}</div>
4+
<div ng-transclude></div>
55
</a>

src/select2/select.tpl.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<div class="ui-select-match"></div>
33
<div ng-class="{'select2-display-none':!open}" class="select2-drop select2-with-searchbox select2-drop-active">
44
<div class="select2-search">
5-
<input class="ui-select-search select2-input" type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" ui-keydown="{up:'up()', down:'down()', esc:'close()', enter:'$select((data.items|filter: $select.search)[$select.index])'}" ng-model="$select.search">
5+
<input class="ui-select-search select2-input" type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" ng-model="$select.search">
66
</div>
77
<div class="ui-select-choices" />
88
</div>

0 commit comments

Comments
 (0)