diff options
-rw-r--r-- | README.md | 13 | ||||
-rw-r--r-- | angular-multi-select.css | 82 | ||||
-rw-r--r-- | angular-multi-select.js | 53 | ||||
-rw-r--r-- | multiselect.htm | 178 |
4 files changed, 205 insertions, 121 deletions
@@ -1,6 +1,6 @@ -Angular Multi Select +Angular Multi Select ** Updating in progress. Do not download ** ==================== -Angular Multi Select is an AngularJS directive which creates a dropdown button with multiple checkboxes. +Angular Multi Select is an AngularJS directive which creates a dropdown button with multiple selections. <br /> @@ -8,6 +8,7 @@ Demo: http://jsfiddle.net/Rg2pY/ or download all the files into a same folder an Features -- + - Use CSS & HTML tags - Reset selections to original state - Directly updates the input model - Load new input model on the fly @@ -24,6 +25,7 @@ Usage <div multi-select input-model="input_list" + button-label="firstName" item-label="firstName lastName" tick-property="selected" output-model="output_list" @@ -58,8 +60,13 @@ $scope variable. Array of objects. ]; - **IMPORTANT**: This directive updates the $scope variable (input-model) directly, therefore you cannot use the same $scope variable for multiple multi-select directives. You need to copy the input variable to a new one and use it on the second multi-select, and so on. +##### button-label (REQUIRED) +input-model property that you want to display on the button. Separate multiple values by space. +<br />Example: +item-label="firstName" + ##### item-label (REQUIRED) -input-model property that you want to display on the button & checkboxes. Separate multiple values by space. +input-model property that you want to display on the checkboxes. Separate multiple values by space. <br />Example: item-label="firstName lastName" diff --git a/angular-multi-select.css b/angular-multi-select.css index ad4372a..6ee29fc 100644 --- a/angular-multi-select.css +++ b/angular-multi-select.css @@ -1,7 +1,3 @@ -body { - font-family: "Arial"; - font-size: 14px; -} .multiSelect { } @@ -13,39 +9,32 @@ body { display: block; position: relative; margin:0 auto; - font-weight: normal; text-align: center; - vertical-align: middle; cursor: pointer; background-image: none; - border: 1px solid transparent; + border: 1px solid #999; padding: 6px 12px; font-size: 14px; - line-height: 1.428571429; - border-radius: 0px; + line-height: 1.4; + border-radius: 2px; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; -o-user-select: none; user-select: none; - background-color: #5CB85C; - border-color: #4CAE4C; - color: #fff; + color: #666; } .multiSelect .helperButton { display: inline-block; - margin-bottom: 0; - font-weight: normal; text-align: center; - vertical-align: middle; + vertical-align: top; cursor: pointer; - background-image: none; border: 1px solid #ccc; - padding: 2px 2px; + padding: 2px 7px; font-size: 14px; line-height: 1; - border-radius: 0px; + border-radius: 2px; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; @@ -83,23 +72,10 @@ body { border-bottom: 0 dotted; } -.multiSelect label { - margin-right: 0px; - margin-bottom: 3px; - padding: 5px 8px 5px 8px; -} - .multiSelect.multiSelectItem { - padding: 5px 0 5px 0; -} - -.multiSelect .checkboxSelected { - background-color: #428bca; - border-color: #357ebd; - color: #fff; - padding: 4px 7px 4px 7px; - border: 1px solid transparent; - border-radius: 1px; + display: block; + min-height: 30px; + color: #666; } .multiSelect .vertical { @@ -110,8 +86,35 @@ body { float: left; } -.multiSelect input { - margin-right: 4px; +.multiSelect .col { + display: table-cell; + min-width: 14px; + line-height: 1.4; +} + +.multiSelect img { + vertical-align: middle; + margin-bottom:4px; +} + +label.multiSelect span { + margin-right: 5px; +} + +label.multiSelect span:hover{ + cursor: pointer; + color: #000; +} + +.multiSelect .checkbox { + position: absolute; + left: -9999px; + cursor: pointer; +} + +.multiSelect .checkboxSelected { + color: #333; + font-weight: bold; } .multiSelect .show { @@ -127,6 +130,11 @@ body { } .multiSelect .helperButton + .helperButton { - margin-left: 5px; + margin-left: 1px; +} + +.multiSelect.disabled { + color: #999; + cursor: not-allowed; } diff --git a/angular-multi-select.js b/angular-multi-select.js index e46360b..897a596 100644 --- a/angular-multi-select.js +++ b/angular-multi-select.js @@ -31,7 +31,7 @@ * -------------------------------------------------------------------------------- */ -angular.module( 'multi-select', ['ng'] ).directive( 'multiSelect' , [ '$timeout', '$compile' , function ( $timeout, $compile ) { +angular.module( 'multi-select', ['ng'] ).directive( 'multiSelect' , [ '$sce', function ( $sce ) { return { restrict: 'AE', @@ -43,6 +43,7 @@ angular.module( 'multi-select', ['ng'] ).directive( 'multiSelect' , [ '$timeout' { inputModel : '=', outputModel : '=', + buttonLabel : '@', itemLabel : '@', tickProperty : '@', orientation : '@', @@ -53,10 +54,8 @@ angular.module( 'multi-select', ['ng'] ).directive( 'multiSelect' , [ '$timeout' template: '<span class="multiSelect inlineBlock">' + - '<div class="multiSelect button multiSelectButton" ng-click="toggleCheckboxes( $event ); refreshSelectedItems();">' + - '{{buttonLabel}}' + - ' ▾' + - '</div>' + + '<button type="button" class="multiSelect button multiSelectButton" ng-click="toggleCheckboxes( $event ); refreshSelectedItems();" ng-bind-html="varButtonLabel">' + + '</button>' + '<div class="multiSelect checkboxLayer hide" style="{{layerStyle}}">' + '<div class="multiSelect line">' + '<span ng-if="!isDisabled">Select: </span>' + @@ -66,13 +65,18 @@ angular.module( 'multi-select', ['ng'] ).directive( 'multiSelect' , [ '$timeout' '</div>' + '<div class="multiSelect line">' + 'Filter: <input class="multiSelect" type="text" ng-model="labelFilter" />' + - '<button type="button" class="multiSelect helperButton" ng-click="labelFilter=\'\'">Clear</button>' + + ' <button type="button" class="multiSelect helperButton" ng-click="labelFilter=\'\'">Clear</button>' + '</div>' + '<div ng-repeat="item in inputModel | filter:labelFilter" ng-class="orientation" class="multiSelect multiSelectItem">' + - '<label class="multiSelect" ng-class="{checkboxSelected:item[ tickProperty ]}">' + - '<input class="multiSelect" type="checkbox" ng-disabled="isDisabled" ng-checked="item[ tickProperty ]" ng-click="syncItems( item )" />' + - '{{writeLabel( item )}}' + - '</label> ' + + '<div class="multiSelect col">' + + '<span class="multiSelect" ng-if="item[ tickProperty ]">✔</span>' + + '</div>' + + '<div class="multiSelect col">' + + '<label class="multiSelect" ng-class="{checkboxSelected:item[ tickProperty ]}">' + + '<input class="multiSelect checkbox" type="checkbox" ng-disabled="isDisabled" ng-checked="item[ tickProperty ]" ng-click="syncItems( item )" />' + + '<span class="multiSelect" ng-class="{disabled:isDisabled}" ng-bind-html="writeLabel( item, \'itemLabel\' )"></span>' + + '</label> ' + + '</div>' + '</div>' + '</div>' + '</span>', @@ -82,7 +86,7 @@ angular.module( 'multi-select', ['ng'] ).directive( 'multiSelect' , [ '$timeout' $scope.selectedItems = []; $scope.backUp = []; $scope.layerStyle = ''; - $scope.buttonLabel = ''; + $scope.varButtonLabel = ''; // Checkbox is ticked $scope.syncItems = function( item ) { @@ -94,7 +98,7 @@ angular.module( 'multi-select', ['ng'] ).directive( 'multiSelect' , [ '$timeout' // Refresh the button to display the selected items and push into output model if specified $scope.refreshSelectedItems = function() { - $scope.buttonLabel = ''; + $scope.varButtonLabel = ''; $scope.selectedItems = []; ctr = 0; @@ -113,7 +117,7 @@ angular.module( 'multi-select', ['ng'] ).directive( 'multiSelect' , [ '$timeout' // Write label... if ( $scope.selectedItems.length === 0 ) { - $scope.buttonLabel = 'None selected'; + $scope.varButtonLabel = 'None selected'; } else { var tempMaxLabels = $scope.selectedItems.length; @@ -132,22 +136,23 @@ angular.module( 'multi-select', ['ng'] ).directive( 'multiSelect' , [ '$timeout' angular.forEach( $scope.selectedItems, function( value, key ) { if ( typeof value !== 'undefined' ) { if ( ctr < tempMaxLabels ) { - $scope.buttonLabel += ( $scope.buttonLabel.length > 0 ? ', ' : '') + $scope.writeLabel( value ); + $scope.varButtonLabel += ( $scope.varButtonLabel.length > 0 ? ', ' : '') + $scope.writeLabel( value, 'buttonLabel' ); } ctr++; } }); if ( $scope.more === true ) { - $scope.buttonLabel += ', ... (Total: ' + $scope.selectedItems.length + ')'; + $scope.varButtonLabel += ', ... (Total: ' + $scope.selectedItems.length + ')'; } } + $scope.varButtonLabel = $sce.trustAsHtml( $scope.varButtonLabel + ' ▾' ); } // A simple function to parse the item label settings - $scope.writeLabel = function( item ) { + $scope.writeLabel = function( item, type ) { var label = ''; - var temp = $scope.itemLabel.split( ' ' ); + var temp = $scope[ type ].split( ' ' ); angular.forEach( temp, function( value2, key2 ) { if ( typeof value2 !== 'undefined' ) { angular.forEach( item, function( value1, key1 ) { @@ -157,7 +162,7 @@ angular.module( 'multi-select', ['ng'] ).directive( 'multiSelect' , [ '$timeout' }); } }); - return label; + return $sce.trustAsHtml( label ); } // UI operations to show/hide checkboxes @@ -223,15 +228,19 @@ angular.module( 'multi-select', ['ng'] ).directive( 'multiSelect' , [ '$timeout' // Generic validation for required attributes validate = function() { if ( !( 'inputModel' in attrs )) { - console.log( 'Multi-select error: input model is not defined! (ID: ' + $scope.directiveId + ')' ); + console.log( 'Multi-select error: input-model is not defined! (ID: ' + $scope.directiveId + ')' ); } - if ( !( 'itemLabel' in attrs )) { - console.log( 'Multi-select error: item label is not defined! (ID: ' + $scope.directiveId + ')' ); + if ( !( 'buttonLabel' in attrs )) { + console.log( 'Multi-select error: button-label is not defined! (ID: ' + $scope.directiveId + ')' ); } + if ( !( 'itemLabel' in attrs )) { + console.log( 'Multi-select error: item-label is not defined! (ID: ' + $scope.directiveId + ')' ); + } + if ( !( 'tickProperty' in attrs )) { - console.log( 'Multi-select error: tick property is not defined! (ID: ' + $scope.directiveId + ')' ); + console.log( 'Multi-select error: tick-property is not defined! (ID: ' + $scope.directiveId + ')' ); } } diff --git a/multiselect.htm b/multiselect.htm index 4234e40..87b904d 100644 --- a/multiselect.htm +++ b/multiselect.htm @@ -9,61 +9,94 @@ <!-- css --> <link rel="stylesheet" href="angular-multi-select.css"> <meta http-equiv="X-UA-Compatible" content="IE=Edge"> + <style> + body { + font-family: 'Arial'; + font-size: 14px; + background-color: #999; + padding: 0; + margin: 20px; + } + #container { + margin: 0px auto; + width: 50%; + background-color: #ccc; + padding: 20px; + } + </style> </head> <body ng-controller="main"> - - * Open your browser console to see the $scope variables getting updated - <br /> - <br /> - - Minimal: - <br /> - <div - multi-select - input-model="minData" - item-label="firstName" - tick-property="selected" - > + <div id="container"> + * Open your browser console to see the $scope variables getting updated + + <br /> + <h3 style="width:100%;border-bottom:1px solid #888">Minimal</h3> + <div + multi-select + input-model="minData" + button-label="firstName" + item-label="firstName" + tick-property="selected" + > + </div> + <br /> + <br /> + <br /> + <br /> + + <h3 style="width:100%;border-bottom:1px solid #888">Full</h3> + <div + multi-select + input-model="fullData" + output-model="resultData" + button-label="firstName lastName" + item-label="firstName lastName" + tick-property="checked" + orientation="horizontal" + max-labels="3" + directive-id="multiSelectFull" + > + </div> + <br /> + <br /> + <br /> + <br /> + + <h3 style="width:100%;border-bottom:1px solid #888">Use element instead of attribute, dynamic input and state</h3> + <multi-select + input-model="dynamicData" + button-label="car" + item-label="car" + tick-property="ticked" + is-disabled="dinDisabled" + > + </multi-select> + + <br /><br /> + <button type="button" ng-click="switchSource( 'data1' )" >Load data 1</button> + <button type="button" ng-click="switchSource( 'data2' )" >Load data 2</button> + <button type="button" ng-click="switchSource( 'data3' )" >Load data 3</button> + + <br /><br /> + <button type="button" ng-click="dinDisabled = !dinDisabled" >Enable / Disable</button> + Is disabled: {{dinDisabled}} + <br /> + <br /> + <br /> + <br /> + + <h3 style="width:100%;border-bottom:1px solid #888">Use HTML tags</h3> + You can use whatever HTML tags accepted by AngularJS's $sce.trustAsHtml() method. + <br /><br /> + <multi-select + input-model="webBrowsers" + button-label="icon" + item-label="icon name" + tick-property="ticked" + > + </multi-select> </div> - - <br /><br /> - - Full: - <br /> - <div - multi-select - input-model="fullData" - output-model="resultData" - item-label="firstName lastName" - tick-property="checked" - orientation="horizontal" - max-labels="3" - directive-id="multiSelectFull" - > - </div> - - <br /><br /> - - Use element instead of attribute, load input model on the fly, and toggle checkbox state - <br /> - <multi-select - input-model="dynamicData" - item-label="car" - tick-property="ticked" - is-disabled="dinDisabled" - > - </multi-select> - - <br /><br /> - <button type="button" ng-click="switchSource( 'data1' )" >Load data 1</button> - <button type="button" ng-click="switchSource( 'data2' )" >Load data 2</button> - <button type="button" ng-click="switchSource( 'data3' )" >Load data 3</button> - - <br /><br /> - <button type="button" ng-click="dinDisabled = !dinDisabled" >Enable / Disable</button> - Is disabled: {{dinDisabled}} - </body> <script> @@ -149,19 +182,19 @@ myApp.controller( 'main' , [ '$scope' , function ($scope) { $scope.dynamicData = $scope[ data ]; } - // Japan cars + // Japanese cars $scope.data1 = [ - { car: 'Toyota' , ticked: true }, - { car: 'Mazda' , ticked: false }, - { car: 'Nissan' , ticked: true }, - { car: 'Honda' , ticked: false } + { car: 'Toyota', ticked: true }, + { car: 'Mazda', ticked: false }, + { car: 'Nissan', ticked: true }, + { car: 'Honda', ticked: false } ]; // German cars $scope.data2 = [ - { car: 'Audi' , ticked: false }, - { car: 'BMW' , ticked: false }, - { car: 'Mercedes' , ticked: true }, + { car: 'Audi', ticked: false }, + { car: 'BMW', ticked: false }, + { car: 'Mercedes', ticked: true }, ]; // Italian cars @@ -183,6 +216,33 @@ myApp.controller( 'main' , [ '$scope' , function ($scope) { console.log( '--------' ); } }, true ); + + ///////////// + // HTML tag + ///////////// + + // Web browsers + $scope.webBrowsers = [ + { icon: '<img class="multiSelect" src="https://cdn1.iconfinder.com/data/icons/fatcow/32/opera.png" />', name: 'Opera', ticked: true }, + { icon: '<img class="multiSelect" src="https://cdn1.iconfinder.com/data/icons/fatcow/32/internet_explorer.png" />', name: 'Internet Explorer', ticked: false }, + { icon: '<img class="multiSelect" src="https://cdn1.iconfinder.com/data/icons/humano2/32x32/apps/firefox-icon.png" />', name: 'Firefox', ticked: true }, + { icon: '<img class="multiSelect" src="https://cdn1.iconfinder.com/data/icons/fatcow/32x32/safari_browser.png" />', name: 'Safari', ticked: false }, + { icon: '<img class="multiSelect" src="https://cdn1.iconfinder.com/data/icons/google_jfk_icons_by_carlosjj/32/chrome.png" />', name: 'Chrome', ticked: true } + ]; + + // Open your console and watch dynamicData changes + $scope.$watch( "webBrowsers" , function( newVal, oldVal ) { + if ( !angular.equals( oldVal, newVal ) ) { + angular.forEach( newVal , function( value, key ) { + if ( value.ticked === true ) { + console.log( 'dynamicData .ticked === true: ' + value.car ); + } + }); + console.log( '--------' ); + } + }, true ); + + }]); </script> |