diff options
-rw-r--r-- | README.md | 18 | ||||
-rw-r--r-- | angular-multi-select.css | 4 | ||||
-rw-r--r-- | angular-multi-select.js | 125 | ||||
-rw-r--r-- | multiselect.htm | 120 |
4 files changed, 195 insertions, 72 deletions
@@ -21,7 +21,7 @@ Usage multi-select input-model="input_list" item-label="firstName lastName" - item-ticker="selected" + tick-property="selected" output-model="output_list" orientation="vertical" max-labels="4" @@ -50,7 +50,7 @@ $scope variable. Array of objects. <br /> { id: 3, firstName: "Bruce", lastName: "Wayne", selected: true }, <br /> { id: 4, firstName: "David", lastName: "Banner", selected: false }, <br /> { id: 5, firstName: "Natalia", lastName: "Romanova", selected: false }, -<br /> { id: 6, firstName: "Clark", lastName: "Kent", selected: true }, +<br /> { id: 6, firstName: "Clark", lastName: "Kent", selected: true } <br />]; - #### item-label (REQUIRED) @@ -59,7 +59,7 @@ input-model property that you want to display on the button & checkboxes. Separa item-label="firstName lastName" -- #### item-ticker (IMPORTANT): +- #### tick-property (REQUIRED): input-model property with a boolean value that represent the state of a checkbox. <br />For example: - item-ticker is "selected" @@ -69,8 +69,6 @@ input-model property with a boolean value that represent the state of a checkbox - if isOn === true, checkbox will be ticked. <br />If isOn === false, checkbox will not be ticked. - - If not specified, the default will be "selected" - #### output-model: A $scope variable. If specified, will list all the selected checkboxes model. @@ -96,6 +94,16 @@ Example -- Download all the files into a same folder and open multiselect.htm +Requirements +-- +AngularJS v1.2.15 is used in the script + +Browser Support +-- +Tested on: +- IE8 +- Firefox 27 + Licence -- Released under the MIT license. diff --git a/angular-multi-select.css b/angular-multi-select.css index 3fddf58..521604e 100644 --- a/angular-multi-select.css +++ b/angular-multi-select.css @@ -122,3 +122,7 @@ .multiSelect .line { margin-bottom: 15px; } + +.multiSelect .helperButton + .helperButton { + margin-left: 5px; +} diff --git a/angular-multi-select.js b/angular-multi-select.js index 7dd1470..29fa87c 100644 --- a/angular-multi-select.js +++ b/angular-multi-select.js @@ -41,14 +41,14 @@ angular.module( 'multi-select', ['ng'] ).directive( 'multiSelect' , [ '$timeout' scope: { - inputModel : '=', - outputModel : '=', - itemLabel : '@', - itemTicker : '@', - orientation : '@', - maxLabels : '@', - maxHeight : '@', - isDisabled : '=' + inputModel : '=', + outputModel : '=', + itemLabel : '@', + tickProperty : '@', + orientation : '@', + maxLabels : '@', + maxHeight : '@', + isDisabled : '=' }, template: @@ -72,8 +72,8 @@ angular.module( 'multi-select', ['ng'] ).directive( 'multiSelect' , [ '$timeout' '<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[ itemSelector ]}">' + - '<input class="multiSelect" type="checkbox" ng-disabled="isDisabled" ng-checked="item[ itemSelector ]" ng-click="syncItems( item )" />' + + '<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>' + @@ -88,20 +88,24 @@ angular.module( 'multi-select', ['ng'] ).directive( 'multiSelect' , [ '$timeout' $scope.varMaxLabels = 0; // Checkbox is ticked - $scope.syncItems = function( item ) { - item[ $scope.itemSelector ] = !item[ $scope.itemSelector ]; + $scope.syncItems = function( item ) { + index = $scope.inputModel.indexOf( item ); + $scope.inputModel[ index ][ $scope.tickProperty ] = !$scope.inputModel[ index ][ $scope.tickProperty ]; $scope.refreshSelectedItems(); } // Refresh the button to display the selected items and push into output model if specified $scope.refreshSelectedItems = function() { + $scope.selectedItems = []; angular.forEach( $scope.inputModel, function( value, key ) { - if ( value[ $scope.itemSelector ] === true || value[ $scope.itemSelector ] === 'true' ) { - $scope.selectedItems.push( value ); + if ( typeof value !== 'undefined' ) { + if ( value[ $scope.tickProperty ] === true || value[ $scope.tickProperty ] === 'true' ) { + $scope.selectedItems.push( value ); + } } }); - + // Push into output model if ( typeof attrs.outputModel !== 'undefined' ) { $scope.outputModel = angular.copy( $scope.selectedItems ); @@ -119,7 +123,7 @@ angular.module( 'multi-select', ['ng'] ).directive( 'multiSelect' , [ '$timeout' } else { $scope.varMaxLabels = $scope.maxLabels; - } + } } // UI operations to show/hide checkboxes @@ -158,12 +162,16 @@ angular.module( 'multi-select', ['ng'] ).directive( 'multiSelect' , [ '$timeout' switch( type.toUpperCase() ) { case 'ALL': angular.forEach( $scope.inputModel, function( value, key ) { - value[ $scope.itemSelector ] = true; + if ( typeof value !== 'undefined' ) { + value[ $scope.tickProperty ] = true; + } }); break; case 'NONE': angular.forEach( $scope.inputModel, function( value, key ) { - value[ $scope.itemSelector ] = false; + if ( typeof value !== 'undefined' ) { + value[ $scope.tickProperty ] = false; + } }); break; case 'RESET': @@ -175,29 +183,76 @@ angular.module( 'multi-select', ['ng'] ).directive( 'multiSelect' , [ '$timeout' } // What's written on the button - $scope.writeLabel= function( item ) { + $scope.writeLabel = function( item ) { var label = ''; var temp = $scope.itemLabel.split( ' ' ); angular.forEach( temp, function( value2, key2 ) { - angular.forEach( item, function( value1, key1 ) { - if ( key1 == value2 ) { - label += ' ' + value1; - } - }); + if ( typeof value2 !== 'undefined' ) { + angular.forEach( item, function( value1, key1 ) { + if ( key1 == value2 ) { + label += ' ' + value1; + } + }); + } }); return label; } + validateProperties = function( arrProperties, arrObject ) { + var notThere = false; + var missingProperty = ''; + angular.forEach( arrProperties, function( value1, key1 ) { + if ( typeof value1 !== 'undefined' ) { + var keepGoing = true; + angular.forEach( arrObject, function( value2, key2 ) { + if ( typeof value2 !== 'undefined' && keepGoing ) { + if (!( value1 in value2 )) { + notThere = true; + keepGoing = false; + missingLabel = value1; + } + } + }); + } + }); + if ( notThere === true ) { + alert( 'multi-select ERROR:\n\nProperty "' + missingLabel + '" is not available in the input model.' ); + } + + } + ///////////////////// // Logic starts here ///////////////////// - $scope.itemSelector = ( typeof $scope.itemTicker === 'string' ? $scope.itemTicker : 'selected' ); + + + // Validations.. + validate = function() { + if ( typeof $scope.inputModel === 'undefined' ) { + alert( 'multi-select ERROR - input model is not defined!' ); + } + + if ( typeof $scope.itemLabel === 'undefined' ) { + alert( 'multi-select ERROR - item label is not defined!' ); + } + + if ( typeof $scope.tickProperty === 'undefined' ) { + alert( 'multi-select ERROR - tick property is not defined!' ); + } + + validateProperties( $scope.itemLabel.split( ' ' ), $scope.inputModel ); + validateProperties( new Array( $scope.tickProperty ), $scope.inputModel ); + } + + validate(); + $scope.refreshSelectedItems(); // Watch for changes in input model (allow dynamic input) - $scope.$watch( 'inputModel' , function( newVal ) { - $scope.refreshSelectedItems(); - $scope.backUp = angular.copy( $scope.inputModel ); + $scope.$watch( 'inputModel' , function( oldVal, newVal ) { + validate(); + $scope.backUp = angular.copy( $scope.inputModel ); + $scope.refreshSelectedItems(); }); // Watch for changes in directive state (disabled or enabled) @@ -220,7 +275,19 @@ angular.module( 'multi-select', ['ng'] ).directive( 'multiSelect' , [ '$timeout' $scope.$apply(); e.stopPropagation(); } - }); + }); + + if ( !Array.prototype.indexOf ) { + Array.prototype.indexOf = function(what, i) { + i = i || 0; + var L = this.length; + while (i < L) { + if(this[i] === what) return i; + ++i; + } + return -1; + }; + } } } }]); diff --git a/multiselect.htm b/multiselect.htm index 23e00f3..385772e 100644 --- a/multiselect.htm +++ b/multiselect.htm @@ -6,8 +6,10 @@ <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js"></script> <!-- multi select directive --> <script src="angular-multi-select.js"></script> + <script src="debug.js"></script> <!-- css --> <link rel="stylesheet" href="angular-multi-select.css"> + <meta http-equiv="X-UA-Compatible" content="IE=Edge"> </head> <body ng-controller="main"> @@ -18,6 +20,7 @@ multi-select input-model="minData" item-label="firstName" + tick-property="selected" > </div> @@ -25,23 +28,38 @@ Full: <br /> - <button type="button" ng-click="fullDisabled = !fullDisabled" >Enable / Disable</button> - <br /> <div multi-select input-model="fullData" + output-model="resultData" item-label="firstName lastName" - item-ticker="checked" - output-model="resultData" + tick-property="checked" orientation="vertical" - max-height="800" - max-labels="3" - is-disabled="fullDisabled" + max-labels="3" > </div> <br /><br /> - <button type="button" ng-click="switchSource()" >Dynamically switch data source</button> + + Dynamic input model and checkbox state + <br /> + <div + multi-select + input-model="dynamicData" + item-label="car" + tick-property="ticked" + is-disabled="dinDisabled" + > + </div> + + <br /><br /> + <button type="button" ng-click="dinDisabled = !dinDisabled" >Enable / Disable</button> + Is disabled: {{dinDisabled}} + + <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> </body> @@ -52,71 +70,97 @@ var myApp = angular.module('myApp', [ 'multi-select' ]); // Controller myApp.controller( 'main' , [ '$scope' , function ($scope) { - - $scope.switchSource = function() { - var temp = angular.copy( $scope.minData ); - $scope.minData = angular.copy( $scope.fullData ); - $scope.fullData = angular.copy( temp ); - alert( 'You have switched the data source. If you don\'t see the pre-selected items, maybe you need to change the "item-ticker" from \'selected\' <=> \'checked\' in your directive tag :) '); - } ///////////// // Minimal ///////////// $scope.minData = [ - { id: 1, firstName: "Peter", lastName: "Parker", selected: false }, - { id: 2, firstName: "Mary", lastName: "Jane", selected: false }, - { id: 3, firstName: "Bruce", lastName: "Wayne", selected: true }, - { id: 4, firstName: "David", lastName: "Banner", selected: false }, - { id: 5, firstName: "Natalia", lastName: "Romanova", selected: false }, - { id: 6, firstName: "Clark", lastName: "Kent", selected: true }, + { id: 1, firstName: "Peter", lastName: "Parker", selected: false, hoho:'hoho' }, + { id: 2, firstName: "Mary", lastName: "Jane", selected: false, hoho:'hoho' }, + { id: 3, firstName: "Bruce", lastName: "Wayne", selected: true, hoho:'hoho' }, + { id: 4, firstName: "David", lastName: "Banner", selected: false, hoho:'hoho' }, + { id: 5, firstName: "Natalia", lastName: "Romanova", selected: false, hoho:'hoho' }, + { id: 6, firstName: "Clark", lastName: "Kent", selected: true, hoho:'hoho' }, ]; - // Watch minData changed - $scope.$watch( "minData" , function( newVal ) { + // Open your console and watch minData changes + $scope.$watch( "minData" , function( newVal ) { angular.forEach( newVal , function( value, key ) { if ( value.selected === true ) { - console.log( '.selected === true: ' + value.firstName + ' ' + value.lastName ); + console.log( 'minData .selected === true: ' + value.firstName + ' ' + value.lastName ); } }); - }, true ); + }, true ); ///////////// // Full ///////////// - // You can try to disable/enable the checkboxes here - $scope.fullDisabled = false; $scope.resultData = []; // Data. For the sake of testing, this time around we try to use 'checked' to hold checkbox state. $scope.fullData = [ - { id: 1, firstName: "Tom", lastName: "Cruise", checked: true }, - { id: 2, firstName: "Brad", lastName: "Pitt", checked: false }, - { id: 3, firstName: "Angelina", lastName: "Jolie", checked: false }, - { id: 4, firstName: "Scarlett", lastName: "Johansson", checked: false }, - { id: 5, firstName: "Gerard", lastName: "Buttler", checked: true }, - { id: 6, firstName: "Leonardo", lastName: "DiCaprio", checked: false }, + { id: 1, firstName: "Tom", lastName: "Cruise", checked: true, yeye:'yeye' }, + { id: 2, firstName: "Brad", lastName: "Pitt", checked: false, yeye:'yeye' }, + { id: 3, firstName: "Angelina", lastName: "Jolie", checked: false, yeye:'yeye' }, + { id: 4, firstName: "Scarlett", lastName: "Johansson", checked: false, yeye:'yeye' }, + { id: 5, firstName: "Gerard", lastName: "Buttler", checked: true, yeye:'yeye' }, + { id: 6, firstName: "Leonardo", lastName: "DiCaprio", checked: false, yeye:'yeye' }, ]; - // Watch fullData changed + // Open your console and watch fullData changes $scope.$watch( "fullData" , function( newVal ) { angular.forEach( newVal , function( value, key ) { if ( value.checked === true ) { - console.log( '.checked === true: ' + value.firstName + ' ' + value.lastName ); + console.log( 'fullData .checked === true: ' + value.firstName + ' ' + value.lastName ); } }); }, true ); - // Watch resultData changed + // Open your console and watch resultData changes $scope.$watch( "resultData" , function( newVal ) { angular.forEach( newVal , function( value, key ) { if ( value.checked === true ) { - console.log( '.checked === true: ' + value.firstName + ' ' + value.lastName ); + console.log( 'resultData .checked === true: ' + value.firstName + ' ' + value.lastName ); } }); - }, true ); + }, true ); + +///////////// +// Dynamic +///////////// + + + $scope.dynamicData = []; + $scope.dinDisabled = false; + + $scope.switchSource = function( data ) { + $scope.dynamicData = $scope[ data ]; + } + + // Japan cars + $scope.data1 = [ + { 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 }, + ]; + + // Italian cars + $scope.data3 = [ + { car: 'Porsche' , ticked: false }, + { car: 'Ferrari' , ticked: false }, + { car: 'Fiat' , ticked: true }, + { car: 'Lamborgini' , ticked: true }, + ]; }]); |