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

feat(choices): use object as source iterating over its properties #1208

Merged
merged 2 commits into from
Sep 29, 2015
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
107 changes: 107 additions & 0 deletions examples/demo-object-as-source.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<!DOCTYPE html>
<html lang="en" ng-app="demo">
<head>
<meta charset="utf-8">
<title>AngularJS ui-select</title>

<!--
IE8 support, see AngularJS Internet Explorer Compatibility http://docs.angularjs.org/guide/ie
For Firefox 3.6, you will also need to include jQuery and ECMAScript 5 shim
-->
<!--[if lt IE 9]>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/es5-shim/2.2.0/es5-shim.js"></script>
<script>
document.createElement('ui-select');
document.createElement('ui-select-match');
document.createElement('ui-select-choices');
</script>
<![endif]-->

<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.18/angular.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.18/angular-sanitize.js"></script>
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.css">

<!-- ui-select files -->
<script src="../dist/select.js"></script>
<link rel="stylesheet" href="../dist/select.css">

<script src="demo.js"></script>

<!-- Select2 theme -->
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/select2/3.4.5/select2.css">

<!--
Selectize theme
Less versions are available at https://github.com/brianreavis/selectize.js/tree/master/dist/less
-->
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/selectize.js/0.8.5/css/selectize.default.css">
<!-- <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/selectize.js/0.8.5/css/selectize.bootstrap2.css"> -->
<!-- <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/selectize.js/0.8.5/css/selectize.bootstrap3.css"> -->

<style>
body {
padding: 15px;
}

.select2 > .select2-choice.ui-select-match {
/* Because of the inclusion of Bootstrap */
height: 29px;
}

.selectize-control > .selectize-dropdown {
top: 36px;
}
</style>
</head>

<body ng-controller="DemoCtrl">

<button class="btn btn-default btn-xs" ng-click="enable()">Enable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="disable()">Disable ui-select</button>
<button class="btn btn-default btn-xs" ng-click="clear()">Clear ng-model</button>

<h1>(key, value) format</h1>

<h2>Using value for binding</h2>

<p>Selected: {{person.selectedValue}}</p>
<ui-select ng-model="person.selectedValue" theme="select2" ng-disabled="disabled" style="min-width: 300px;" title="Choose a person">
<ui-select-match placeholder="Select a person in the list or search his name/age...">{{$select.selected.value.name}}</ui-select-match>
<ui-select-choices repeat="person.value as (key, person) in peopleObj | filter: {'value':$select.search}">
<div ng-bind-html="person.value.name | highlight: $select.search"></div>
<small>
email: {{person.value.email}}
age: <span ng-bind-html="''+person.value.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>

<h2>Using single property for binding</h2>
<p>Selected: {{person.selectedSingle}}</p>
<ui-select ng-model="person.selectedSingle" theme="select2" ng-disabled="disabled" style="min-width: 300px;" title="Choose a person">
<ui-select-match placeholder="Select a person in the list or search his name/age...">{{$select.selected.value.name}}</ui-select-match>
<ui-select-choices repeat="person.value.name as (key, person) in peopleObj | filter: {'value':$select.search}">
<div ng-bind-html="person.value.name | highlight: $select.search"></div>
<small>
email: {{person.value.email}}
age: <span ng-bind-html="''+person.value.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>

<h2>Using key for binding</h2>
<p>Selected: {{person.selectedSingleKey}}</p>
<ui-select ng-model="person.selectedSingleKey" theme="select2" ng-disabled="disabled" style="min-width: 300px;" title="Choose a person">
<ui-select-match placeholder="Select a person in the list or search his name/age...">{{$select.selected.value.name}}</ui-select-match>
<ui-select-choices repeat="person.key as (key, person) in peopleObj | filter: {'value':$select.search}">
<div ng-bind-html="person.value.name | highlight: $select.search"></div>
<small>
email: {{person.value.email}}
age: <span ng-bind-html="''+person.value.age | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>

</body>
</html>
24 changes: 21 additions & 3 deletions examples/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ app.controller('DemoCtrl', function($scope, $http, $timeout, $interval) {

$scope.setInputFocus = function (){
$scope.$broadcast('UiSelectDemo1');
}
};

$scope.enable = function() {
$scope.disabled = false;
Expand All @@ -58,11 +58,11 @@ app.controller('DemoCtrl', function($scope, $http, $timeout, $interval) {

$scope.enableSearch = function() {
$scope.searchEnabled = true;
}
};

$scope.disableSearch = function() {
$scope.searchEnabled = false;
}
};

$scope.clear = function() {
$scope.person.selected = undefined;
Expand Down Expand Up @@ -130,7 +130,25 @@ app.controller('DemoCtrl', function($scope, $http, $timeout, $interval) {
return item;
};

$scope.peopleObj = {
'1' : { name: 'Adam', email: '[email protected]', age: 12, country: 'United States' },
'2' : { name: 'Amalie', email: '[email protected]', age: 12, country: 'Argentina' },
'3' : { name: 'Estefanía', email: '[email protected]', age: 21, country: 'Argentina' },
'4' : { name: 'Adrian', email: '[email protected]', age: 21, country: 'Ecuador' },
'5' : { name: 'Wladimir', email: '[email protected]', age: 30, country: 'Ecuador' },
'6' : { name: 'Samantha', email: '[email protected]', age: 30, country: 'United States' },
'7' : { name: 'Nicole', email: '[email protected]', age: 43, country: 'Colombia' },
'8' : { name: 'Natasha', email: '[email protected]', age: 54, country: 'Ecuador' },
'9' : { name: 'Michael', email: '[email protected]', age: 15, country: 'Colombia' },
'10' : { name: 'Nicolás', email: '[email protected]', age: 43, country: 'Colombia' }
};

$scope.person = {};

$scope.person.selectedValue = $scope.peopleObj[3];
$scope.person.selectedSingle = 'Samantha';
$scope.person.selectedSingleKey = '5';

$scope.people = [
{ name: 'Adam', email: '[email protected]', age: 12, country: 'United States' },
{ name: 'Amalie', email: '[email protected]', age: 12, country: 'Argentina' },
Expand Down
2 changes: 1 addition & 1 deletion src/uiSelectChoicesDirective.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ uis.directive('uiSelectChoices',
throw uiSelectMinErr('rows', "Expected 1 .ui-select-choices-row but got '{0}'.", choices.length);
}

choices.attr('ng-repeat', RepeatParser.getNgRepeatExpression($select.parserResult.itemName, '$select.items', $select.parserResult.trackByExp, groupByExp))
choices.attr('ng-repeat', $select.parserResult.repeatExpression(groupByExp))
.attr('ng-if', '$select.open') //Prevent unnecessary watches when dropdown is closed
.attr('ng-mouseenter', '$select.setActiveItem('+$select.parserResult.itemName +')')
.attr('ng-click', '$select.select(' + $select.parserResult.itemName + ',false,$event)');
Expand Down
31 changes: 28 additions & 3 deletions src/uiSelectController.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
* put as much logic in the controller (instead of the link functions) as possible so it can be easily tested.
*/
uis.controller('uiSelectCtrl',
['$scope', '$element', '$timeout', '$filter', 'uisRepeatParser', 'uiSelectMinErr', 'uiSelectConfig',
function($scope, $element, $timeout, $filter, RepeatParser, uiSelectMinErr, uiSelectConfig) {
['$scope', '$element', '$timeout', '$filter', 'uisRepeatParser', 'uiSelectMinErr', 'uiSelectConfig', '$parse',
function($scope, $element, $timeout, $filter, RepeatParser, uiSelectMinErr, uiSelectConfig, $parse) {

var ctrl = this;

Expand Down Expand Up @@ -92,6 +92,9 @@ uis.controller('uiSelectCtrl',
$timeout(function() {
ctrl.search = initSearchValue || ctrl.search;
ctrl.searchInput[0].focus();
if(!ctrl.tagging.isActivated && ctrl.items.length > 1) {
_ensureHighlightVisible();
}
});
}
};
Expand Down Expand Up @@ -141,6 +144,28 @@ uis.controller('uiSelectCtrl',
ctrl.isGrouped = !!groupByExp;
ctrl.itemProperty = ctrl.parserResult.itemName;

//If collection is an Object, convert it to Array

var originalSource = ctrl.parserResult.source;

//When an object is used as source, we better create an array and use it as 'source'
var createArrayFromObject = function(){
$scope.$uisSource = Object.keys(originalSource($scope)).map(function(v){
var result = {};
result[ctrl.parserResult.keyName] = v;
result.value = $scope.peopleObj[v];
return result;
});
};

if (ctrl.parserResult.keyName){ // Check for (key,value) syntax
createArrayFromObject();
ctrl.parserResult.source = $parse('$uisSource' + ctrl.parserResult.filters);
$scope.$watch(originalSource, function(newVal, oldVal){
if (newVal !== oldVal) createArrayFromObject();
}, true);
}

ctrl.refreshItems = function (data){
data = data || ctrl.parserResult.source($scope);
var selectedItems = ctrl.selected;
Expand All @@ -164,7 +189,7 @@ uis.controller('uiSelectCtrl',
ctrl.items = [];
} else {
if (!angular.isArray(items)) {
throw uiSelectMinErr('items', "Expected an array but got '{0}'.", items);
throw uiSelectMinErr('items', "Expected an array but got '{0}'.", items);
} else {
//Remove already selected items (ex: while searching)
//TODO Should add a test
Expand Down
29 changes: 17 additions & 12 deletions src/uisRepeatParserService.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,30 @@ uis.service('uisRepeatParser', ['uiSelectMinErr','$parse', function(uiSelectMinE
*/
self.parse = function(expression) {

var match = expression.match(/^\s*(?:([\s\S]+?)\s+as\s+)?([\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);

//0000000000000000000000000000000000011111111100000000000000022222222222222003333333333333333333333000044444444444444444400000000000000005555500000666666666666600000000000000000000007777777770000000
var match = expression.match(/^\s*(?:([\s\S]+?)\s+as\s+)?(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\w]+)\s*(|\s*[\s\S]+?)?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);

if (!match) {
throw uiSelectMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
expression);
}

return {
itemName: match[2], // (lhs) Left-hand side,
source: $parse(match[3]),
trackByExp: match[4],
modelMapper: $parse(match[1] || match[2])
itemName: match[4] || match[2], // (lhs) Left-hand side,
keyName: match[3], //for (key, value) syntax
source: $parse(!match[3] ? match[5] + (match[6] || ''): match[5]), //concat source with filters if its an array
sourceName: match[5],
filters: match[6],
trackByExp: match[7],
modelMapper: $parse(match[1] || match[4] || match[2]),
repeatExpression: function (grouped) {
var expression = this.itemName + ' in ' + (grouped ? '$group.items' : '$select.items');
if (this.trackByExp) {
expression += ' track by ' + this.trackByExp;
}
return expression;
}
};

};
Expand All @@ -40,11 +52,4 @@ uis.service('uisRepeatParser', ['uiSelectMinErr','$parse', function(uiSelectMinE
return '$group in $select.groups';
};

self.getNgRepeatExpression = function(itemName, source, trackByExp, grouped) {
var expression = itemName + ' in ' + (grouped ? '$group.items' : source);
if (trackByExp) {
expression += ' track by ' + trackByExp;
}
return expression;
};
}]);
Loading