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

Add choices grouping using 'group by' expression #49

Merged
merged 2 commits into from
Jul 11, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions examples/bootstrap.html
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,21 @@
</div>
</div>

<div class="form-group">
<label class="col-sm-3 control-label">Grouped</label>
<div class="col-sm-6">

<ui-select ng-model="person.selected" theme="bootstrap">
<ui-select-match placeholder="Select or search a person in the list...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices group-by="'group'" repeat="item in people | filter: $select.search">
<span ng-bind-html="item.name | highlight: $select.search"></span>
<small ng-bind-html="item.email | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>

</div>
</div>

<div class="form-group">
<label class="col-sm-3 control-label">With a clear button</label>
<div class="col-sm-6">
Expand Down
16 changes: 8 additions & 8 deletions examples/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,14 @@ app.controller('DemoCtrl', function($scope, $http) {

$scope.person = {};
$scope.people = [
{ name: 'Adam', email: '[email protected]', age: 10 },
{ name: 'Amalie', email: '[email protected]', age: 12 },
{ name: 'Wladimir', email: 'wladimir@email.com', age: 30 },
{ name: 'Samantha', email: 'samantha@email.com', age: 31 },
{ name: 'Estefanía', email: 'estefanía@email.com', age: 16 },
{ name: 'Natasha', email: 'natasha@email.com', age: 54 },
{ name: 'Nicole', email: '[email protected]', age: 43 },
{ name: 'Adrian', email: 'adrian@email.com', age: 21 }
{ name: 'Adam', email: '[email protected]', group: 'Foo', age: 12 },
{ name: 'Amalie', email: '[email protected]', group: 'Foo', age: 12 },
{ name: 'Estefanía', email: 'estefanía@email.com', group: 'Foo', age: 21 },
{ name: 'Adrian', email: 'adrian@email.com', group: 'Foo', age: 21 },
{ name: 'Wladimir', email: 'wladimir@email.com', group: 'Foo', age: 30 },
{ name: 'Samantha', email: 'samantha@email.com', group: 'bar', age: 30 },
{ name: 'Nicole', email: '[email protected]', group: 'bar', age: 43 },
{ name: 'Natasha', email: 'natasha@email.com', group: 'Baz', age: 54 }
];

$scope.address = {};
Expand Down
15 changes: 15 additions & 0 deletions examples/select2-bootstrap3.html
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,21 @@
</div>
</div>

<div class="form-group">
<label class="col-sm-3 control-label">Grouped</label>
<div class="col-sm-6">

<ui-select ng-model="person.selected" theme="select2" class="form-control">
<ui-select-match placeholder="Select or search a person in the list...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices group-by="'group'" repeat="item in people | filter: $select.search">
<span ng-bind-html="item.name | highlight: $select.search"></span>
<small ng-bind-html="item.email | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>

</div>
</div>

<div class="form-group">
<label class="col-sm-3 control-label">With a clear button</label>
<div class="col-sm-6">
Expand Down
15 changes: 15 additions & 0 deletions examples/selectize-bootstrap3.html
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,21 @@
</div>
</div>

<div class="form-group">
<label class="col-sm-3 control-label">Grouped</label>
<div class="col-sm-6">

<ui-select ng-model="person.selected" theme="selectize">
<ui-select-match placeholder="Select or search a person in the list...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices group-by="'group'" repeat="item in people | filter: $select.search">
<span ng-bind-html="item.name | highlight: $select.search"></span>
<small ng-bind-html="item.email | highlight: $select.search"></small>
</ui-select-choices>
</ui-select>

</div>
</div>

<div class="form-group">
<label class="col-sm-3 control-label">With a clear button</label>
<div class="col-sm-6">
Expand Down
1 change: 1 addition & 0 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module.exports = function(config) {
'bower_components/angular-mocks/angular-mocks.js',

'dist/select.js',
'test/helpers.js',
'test/**/*.spec.js'
],

Expand Down
8 changes: 6 additions & 2 deletions src/bootstrap/choices.tpl.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
<ul class="ui-select-choices ui-select-choices-content dropdown-menu"
role="menu" aria-labelledby="dLabel"
ng-show="$select.items.length > 0">
<li class="ui-select-choices-row" ng-class="{active: $select.activeIndex === $index}">
<a class="ui-select-choices-row-inner" href="javascript:void(0)"></a>
<li class="ui-select-choices-group">
<div class="divider" ng-show="$index > 0"></div>
<div class="ui-select-choices-group-label dropdown-header">{{$group}}</div>
<div class="ui-select-choices-row" ng-class="{active: $select.isActive(this)}">
<a href="javascript:void(0)" class="ui-select-choices-row-inner"></a>
</div>
</li>
</ul>
23 changes: 23 additions & 0 deletions src/select.css
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,29 @@
overflow-x: hidden;
}

.ui-select-bootstrap .ui-select-choices-row>a {
display: block;
padding: 3px 20px;
clear: both;
font-weight: 400;
line-height: 1.42857143;
color: #333;
white-space: nowrap;
}

.ui-select-bootstrap .ui-select-choices-row>a:hover, .ui-select-bootstrap .ui-select-choices-row>a:focus {
text-decoration: none;
color: #262626;
background-color: #f5f5f5;
}

.ui-select-bootstrap .ui-select-choices-row.active>a {
color: #fff;
text-decoration: none;
outline: 0;
background-color: #428bca;
}

/* fix hide/show angular animation */
.ui-select-match.ng-hide-add,
.ui-select-search.ng-hide-add {
Expand Down
71 changes: 57 additions & 14 deletions src/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,12 @@
};
};

self.getNgRepeatExpression = function(lhs, rhs, trackByExp) {
var expression = lhs + ' in ' + rhs;
self.getGroupNgRepeatExpression = function() {
return '($group, $items) in $select.groups';
};

self.getNgRepeatExpression = function(lhs, rhs, trackByExp, grouped) {
var expression = lhs + ' in ' + (grouped ? '$items' : rhs);
if (trackByExp) {
expression += ' track by ' + trackByExp;
}
Expand Down Expand Up @@ -153,8 +157,33 @@
}
};

ctrl.parseRepeatAttr = function(repeatAttr) {
var repeat = RepeatParser.parse(repeatAttr);
ctrl.parseRepeatAttr = function(repeatAttr, groupByExp) {
function updateGroups(items) {
ctrl.groups = {};
angular.forEach(items, function(item) {
var groupFn = $scope.$eval(groupByExp);
var groupValue = angular.isFunction(groupFn) ? groupFn(item) : item[groupFn];
if(!ctrl.groups[groupValue]) {
ctrl.groups[groupValue] = [item];
}
else {
ctrl.groups[groupValue].push(item);
}
});
ctrl.items = [];
angular.forEach(Object.keys(ctrl.groups).sort(), function(group) {
ctrl.items = ctrl.items.concat(ctrl.groups[group]);
});
}

function setPlainItems(items) {
ctrl.items = items;
}

var repeat = RepeatParser.parse(repeatAttr),
setItemsFn = groupByExp ? updateGroups : setPlainItems;

ctrl.itemProperty = repeat.lhs;

// See https://github.com/angular/angular.js/blob/v1.2.15/src/ng/directive/ngRepeat.js#L259
$scope.$watchCollection(repeat.rhs, function(items) {
Expand All @@ -169,7 +198,7 @@
throw uiSelectMinErr('items', "Expected an array but got '{0}'.", items);
} else {
// Regular case
ctrl.items = items;
setItemsFn(items);
}
}

Expand Down Expand Up @@ -198,6 +227,14 @@
}
};

ctrl.setActiveItem = function(item) {
ctrl.activeIndex = ctrl.items.indexOf(item);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When using the down/up keys to move selected value in "grouped mode", it isn't working correctly since the activeIndex is not in same order are if the items aren't grouped. Maybe saving current group could help, but some refactor will be needed at https://github.com/angular-ui/ui-select/blob/master/src/select.js#L225

};

ctrl.isActive = function(itemScope) {
return ctrl.items.indexOf(itemScope[ctrl.itemProperty]) === ctrl.activeIndex;
};

// When the user clicks on an item inside the dropdown
ctrl.select = function(item) {
ctrl.selected = item;
Expand Down Expand Up @@ -493,28 +530,34 @@

compile: function(tElement, tAttrs) {
var repeat = RepeatParser.parse(tAttrs.repeat);
var groupByExp = tAttrs.groupBy;
return function link(scope, element, attrs, $select, transcludeFn) {

var rows = element.querySelectorAll('.ui-select-choices-row');
if (rows.length !== 1) {
throw uiSelectMinErr('rows', "Expected 1 .ui-select-choices-row but got '{0}'.", rows.length);

if(groupByExp) {
var groups = element.querySelectorAll('.ui-select-choices-group');
groups.attr('ng-repeat', RepeatParser.getGroupNgRepeatExpression());
}

var choices = element.querySelectorAll('.ui-select-choices-row');
if (choices.length !== 1) {
throw uiSelectMinErr('rows', "Expected 1 .ui-select-choices-row but got '{0}'.", choices.length);
}

rows.attr('ng-repeat', RepeatParser.getNgRepeatExpression(repeat.lhs, '$select.items', repeat.trackByExp))
.attr('ng-mouseenter', '$select.activeIndex = $index')
choices.attr('ng-repeat', RepeatParser.getNgRepeatExpression(repeat.lhs, '$select.items', repeat.trackByExp, groupByExp))
.attr('ng-mouseenter', '$select.setActiveItem('+repeat.lhs+')')
.attr('ng-click', '$select.select(' + repeat.lhs + ')');


transcludeFn(function(clone) {
var rowsInner = element.querySelectorAll('.ui-select-choices-row-inner');
if (rowsInner.length !== 1)
throw uiSelectMinErr('rows', "Expected 1 .ui-select-choices-row-inner but got '{0}'.", rowsInner.length);

rowsInner.append(clone);
$compile(element)(scope);
});

$select.parseRepeatAttr(attrs.repeat);
$select.parseRepeatAttr(attrs.repeat, groupByExp);

scope.$watch('$select.search', function() {
$select.activeIndex = 0;
Expand Down Expand Up @@ -565,4 +608,4 @@
return query && matchItem ? matchItem.replace(new RegExp(escapeRegexp(query), 'gi'), '<span class="ui-select-highlight">$&</span>') : matchItem;
};
});
}());
}());
9 changes: 7 additions & 2 deletions src/select2/choices.tpl.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
<ul class="ui-select-choices ui-select-choices-content select2-results">
<li class="ui-select-choices-row" ng-class="{'select2-highlighted': $select.activeIndex === $index}">
<div class="select2-result-label ui-select-choices-row-inner"></div>
<li class="ui-select-choices-group">
<div class="ui-select-choices-group-label select2-result-label">{{$group}}</div>
<ul class="select2-result-sub">
<li class="ui-select-choices-row" ng-class="{'select2-highlighted': $select.isActive(this)}">
<div class="select2-result-label ui-select-choices-row-inner"></div>
</li>
</ul>
</li>
</ul>
8 changes: 5 additions & 3 deletions src/selectize/choices.tpl.html
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
<div ng-show="$select.open" class="ui-select-choices selectize-dropdown single">
<div class="ui-select-choices-content selectize-dropdown-content">
<div class="ui-select-choices-row"
ng-class="{'active': $select.activeIndex === $index}">
<div class="option ui-select-choices-row-inner" data-selectable></div>
<div class="ui-select-choices-group optgroup">
<div class="ui-select-choices-group-label optgroup-header">{{$group}}</div>
<div class="ui-select-choices-row" ng-class="{active: $select.isActive(this)}">
<div class="option ui-select-choices-row-inner" data-selectable></div>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe cleaner will be if we separate in 2 different structures, one for simple and other grouped cases. This way we can set special css and avoid having unnecessary tags.

<ul class="ui-select-choices ui-select-choices-content select2-results">
  <!-- Simple -->
  <li ng-if="!$select.isGrouped" class="ui-select-choices-row" ng-class="{'select2-highlighted': $select.activeIndex === $index}">
    <div class="select2-result-label ui-select-choices-row-inner"></div>
  </li>
  <!-- Grouped -->
  <li ng-if="$select.isGrouped" class="ui-select-choices-group select2-result-with-children">
    <div class="ui-select-choices-group-label select2-result-label">{{$group}}</div>
    <ul class="select2-result-sub">
      <li class="ui-select-choices-row" ng-class="{'select2-highlighted': $select.isActive(this)}">
        <div class="select2-result-label ui-select-choices-row-inner"></div>
      </li>
    </ul>
  </li>
</ul>

</div>
</div>
</div>
15 changes: 15 additions & 0 deletions test/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
beforeEach(function() {
jasmine.addMatchers({
toHaveClass: function(util, customEqualityTesters) {
return {
compare: function(actual, cls) {
var pass = actual.hasClass(cls);
return {
pass: pass,
message: "Expected '" + actual + "'" + (pass ? ' not ' : ' ') + "to have class '" + cls + "'."
}
}
}
}
});
});
Loading