Reputation: 478
I want to build a list that functions like a select or a multiselect given a param. eg. if the parameter is false, select A->select B, unselects A and selects B.
<ul>
<li>a,</li>
<li>b, and</li>
<li>c</li>
</ul>
My initial approach looks something like:
<ul myDirective>
<li ng-model="a">a,</li>
<li ng-model="b">b, and</li>
<li ng-model="c">c</li>
</ul>
Where mydirective watches for clicks on the LIs. On Click it determines if it needs to unselect or unset anything. Then selects the new value an and applies a selected class.
I don't think I'm follows the angular paradigms and I'm looking for some help finding the right path forward.
** Edit ** I'm looking to provide a user experience like: http://jsfiddle.net/qbt2myj8/
Upvotes: 1
Views: 1634
Reputation: 721
in my opinion a custom directive will make your code much clean since you already use AngularJS.
Below is a complete solution using AngularJS Custom Directive that is completely reusable for code organization.
HTML
custom directive default behavior
<ul data-my-directive class="list">
<li>a</li>
<li>b</li>
<li>c</li>
</ul>
(This is the screenshot preview only. See PLAYGROUND
link below to play around)
HTML
with custom attribute
<ul data-my-directive data-multi-select="true" class="list">
<li>a</li>
<li>b</li>
<li>c</li>
</ul>
(This is the screenshot preview only. See PLAYGROUND
link below to play around)
Javascript
angular.module('app',[])
.directive('myDirective', function(){
return {
transclude: true,
template: '<div ng-transclude></div>',
link: function(scope, element, attrs){
function addClickListener(i, callback){
selections[i].addEventListener('click', function(){
callback(i);
}, false);
}
// Set up attribute value. Goes to default if attribute is not set up
attrs.multiSelect = attrs.multiSelect || 'false';
scope.isSelected = [];
var selections = element[0].getElementsByTagName('li');
if(attrs.multiSelect === "true"){
for(var i = 0; i < selections.length; i++){
scope.isSelected[i] = false;
addClickListener(i, function(i){
if(scope.isSelected[i] === true){
// if previously it is selected (red color), now make it unselected (black color / default color)
angular.element(selections[i]).removeClass('selected');
scope.isSelected[i] = false;
} else {
// previously black color, so change it to red color
angular.element(selections[i]).addClass('selected');
scope.isSelected[i] = true;
}
});
}
} else {
var currentSelection = -1;
for(var i = 0; i < selections.length; i++){
scope.isSelected[i] = false;
addClickListener(i, function(i){
if(scope.isSelected[i] === true){
// do nothing
} else {
// previously black color, so change it to red color
angular.element(selections[i]).addClass('selected');
scope.isSelected[i] = true;
angular.element(selections[currentSelection]).removeClass('selected');
scope.isSelected[currentSelection] = false;
currentSelection = i;
}
});
}
}
}
}
});
And finally, the ultimate
to play around!
I hope it clears up your requirements. Have a nice day :)
Upvotes: 2
Reputation: 6051
My approach would be (at first) not to make a directive at all, but a custom JavaScript data structure. Quick example out of my mind :
/**
* Makes a selector data structure
* @param items elements to be listed
* @param multi whether multiple selection is supported
* @returns {{cells: *, selectedItems: selectedItems, unselectAll: unselectAll}}
*/
function makeSelector (items, multi) {
var cells = items.map(function (item) {
// each cell wraps an item and a selected flag
return {
item: item,
selected: false
};
});
// returns an array of the currently selected items
function selectedItems() {
return cells
.filter(function (cell) {
return cell.selected;
})
.map(function (cell) {
return cell.item;
});
}
// unselects all items
function unselectAll() {
cells.forEach(function (cell) {
cell.selected = false;
})
}
// adding methods to cells
cells.forEach(function (cell) {
cell.selectMe = (multi
? function () {
cell.selected = true;
}
: function () {
unselectAll();
cell.selected = true;
});
cell.unselectMe = function () {
cell.selected = false;
};
cell.toggle = function () {
if (cell.selected) {
cell.unselectMe();
} else {
cell.selectMe();
}
}
});
return {
cells: cells,
selectedItems: selectedItems,
unselectAll: unselectAll
};
}
This will allow you to easily retrieve your list of selected items in your controller:
// ...
$scope.abcSelector = makeSelector(['A','B','C'],false);
var getSelectedItems = $scope.abcSelector.selectedItems; // to do something with them in your controller
// ...
Then use Angular data-binding to reflect this data-structure in your HTML:
<h3>List of items :</h3>
<ul>
<li ng-repeat="cell in abcSelector.cells" ng-click="cell.toggle()"
ng-class="{'active': cell.selected}">
{{cell.item}}
</li>
</ul>
<h3>List of selected items :</h3>
<ul>
<li ng-repeat="item in abcSelector.selectedItems()">
{{item}}
</li>
</ul>
and if you fell like it factor this out in a directive, which will typically bind the list of the selected items to an ng-model
.
Upvotes: 1