AngularJS multi-select widget
In most web applications, there are entities that have many-to-many relationships. The most common example is a user-role scenario. In this case, there are typically many roles in a system, as well as many users, and each exist independent of the other. A user can be associated with zero or more roles, while a role can be applied to zero or more users.
So how do we present a user-friendly interface for modifying these relationships? This is a common problem for web applications, and there seems to exist a common interface with which most users are familiar. You simply present two lists of entities side-by-side, with “add” and “remove” buttons to move objects from one side to the other. The left side represents “selected” entities, and the right the “available” ones not already selected.
I wasn’t able to find an Angular directive implementing such an interface, so, naturally, I made my own. It relies on bootstrap for styling and uses a
<select type="multiple">
to represent each list of entities. Click “Result” below to see it in action:
The directive expects two arrays: An “available” array of entities, and “ng-model”, which should be a subset of those entities. These arrays don’t even need to have been initialized yet, the directive will wait until they are first initialized before creating the widget. Also, comparisons are done by reference, not equality, so the ng-model entities should be the same objects as those in “available.” This could easily be changed through usage of angular’s angular.equals
method.
Try it out, let me know what you think, and fork away!
Update (Oct 7, 2013): I tweaked this to use a list of checkboxes rather than <select type="multiple">
. This functions much better on mobile devices, and as arguably a better user experience overall. See the fiddle here.
Update (Jan 2, 2014): Added more flexibility for configuring the display of an item. The directive now accepts a simple expression (e.g. 'user as user | fullName'
) for the display
attribute. Also, refactored the directive into its own module and added to GitHub.
Trackbacks and Pingbacks
Comments are closed.
Is this directive compatible with IE 8 in Compat mode?
Thanks for Sharing!
Is this compatible with AngularJS v1.2.13 ? I have try to implemet in my project and checking on check-box getting error like “Error: b is undefined”
Hi Pratik,
Yes, it should be compatible with the latest Angular. Make sure angular is loaded BEFORE the multi-select module. If you post your code on jsfiddle I could take a look for you.
Is it possible to order the items? When I move them they lost the order
did you fix the ordering?
Hi and thank you for sharing! This directive works very well with a manyToMany relationship and the records get stored in mySQL database smoothly 🙂
However, I tried to move the template part of the directive out, and used templateUrl instead. This
unfortunately did not work for me.
Did I forget something here: http://plnkr.co/hlarjNE9lXHWOlhXEngt
Thanks in advandced
Hi Pedro, extracting the template should work fine. In fact, I did this in the latest version (see GitHub). Just override the
angular.module("template/multiSelect.html", []).run(["$templateCache", function($templateCache) {
$templateCache.put("template/multiSelect.html",
'<div class="multiSelect">' + ...
in your own app.
You also forgot to include a couple files in your plunkr so it didn’t work. Try: http://plnkr.co/edit/f6LEpeqldPMCjyvXytnv?p=preview
Thank you Alec! Its working now !
Im using the directive from a modal registration form. The only thing that still is an issue is when I click the directive’s arrow-buttons from the modal; the modal dissapear without updating the form. 🙁
But this is more a modal issue I think…
Keep up with the good work Alec and thanks again for sharing 🙂
When “model” and “available” load async, then model must be loaded first for correct work. I use nested http calls.
How do I increase the height of the select boxes, perfrably to make them adjust with the height of the page. I have used Chrome debugger to find out where the height of the boxes is set but can’t find anything.
Which boxes are you referring to? The headers?
How can I display the error message based on the ‘inputModel.$setValidity’ on the page which uses the multiselect component?
Works well until Angular 1.3.0. the options are unselectable if an ng version above 1.3.0 used. Anyone faced any issues?
Great tutorial, helped me a lot. I do have a question. Lets say you have a database that this is being stored into, how can you show the selected choices by default from a database.
Lets say my database fields are stored in an ‘object’ and you want to display all the choices. Assuming I saved the choices as a TextField seperated by commas. E.g “Super Administrator, Admin, reg user”
I tried this:
new_placements = object.new_placement.split(“,”);
new_placement_length = new_placements.length;
for(var i=0; i < new_placement_length; i++){
$scope.user = {
siteId: i,
placements: [new_placements[i]]
}
}
I know the above won't work but I am just trying to figure out how to show already existing choices in the available roles select options.
I believe you could just use the new_placements array set to the ‘available’ attribute on your multi-select element.