├── .gitignore ├── .jshintrc ├── LICENSE ├── README.md ├── bower.json ├── dist └── bsDropdown.min.js ├── example ├── index.html └── js │ └── app.js ├── gulpfile.js ├── package.json ├── src └── bsDropdown.js ├── template └── defaultTemplate.html └── test ├── bsDropdown-multiselect-test.js ├── bsDropdown-test.js └── karma.conf.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | bower_components 3 | *~ -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "strict": true, 3 | "globals":{ 4 | "angular": false 5 | } 6 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 AllenFang (方紹昌) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # angular-bootstrap-dropdown 2 | It's a angular directive for bootstrap dropdown, named 'bsDropdown' 3 | 4 | The bsDropdown include all basic function in dropdown, include set default value, and listen change etc. 5 | 6 | Also support multi-select on newest version. 7 | 8 | angular-bootstrap-dropdown dependencies on AngularJS 1.2.x and Bootstrap 3 9 | 10 | You can see an online demo on [here](http://frozen-tundra-7264.herokuapp.com/examples/L8/#). 11 | 12 | ### Versions 13 | 0.1.0 support basic function in bsDropdown, include use ng-model to set default value, and ng-change to listen change 14 | 15 | 0.1.1 add bootstrap divider in bsDropdown 16 | 17 | 0.2.0 add bootstrap dropdown disabled and item disabled in bsDropdown 18 | 19 | 0.9.0(latest) add multi-select to bsDropdown!! 20 | 21 | ### Development 22 | ``` 23 | $ git clone https://github.com/AllenFang/angular-bootstrap-dropdown.git 24 | $ cd angular-practice-example 25 | $ npm install 26 | $ bower install 27 | ``` 28 | Use gulp to test the bsDropdown 29 | ``` 30 | $ npm test 31 | or 32 | $ node_modules/gulp/bin/gulp.js test 33 | ``` 34 | 35 | 36 | ### Usage 37 | Include the angular-bootstrap-dropdown library to your html page 38 | ``` 39 | 40 | ``` 41 | The ```bsDropdown.min.js``` is in the dist folder. 42 | In the next, include the ```ng.bs.dropdown``` to your angular module dependencies 43 | ``` 44 | angular.module("demoApp", ['ng.bs.dropdown']) 45 | ``` 46 | Then, you can go to use the angular-bootstrap-dropdown, below is a simple example 47 | 48 | First of all, give an angular controller 49 | ``` 50 | angular.module("demoApp", ['ng.bs.dropdown']) 51 | .controller("YearController", function($scope){ 52 | $scope.years = [ 53 | "2015", 54 | "2014", 55 | "2013", 56 | "2012", 57 | "2011", 58 | "2010" 59 | ]; 60 | $scope.selectYear = $scope.years[2]; //current select item 61 | 62 | /*changeYear function will be called if dropdown change*/ 63 | $scope.changeYear = function(){ 64 | console.log("YearController say... " + $scope.selectYear); 65 | } 66 | }); 67 | ``` 68 | 69 | So here is your partial html code 70 | ``` 71 |
72 |

You select {{selectYear}} ....


73 |
78 |
79 | ``` 80 | 81 | ### Setting 82 | Use ```bs-dropdown-display``` attribute to display default text on dropdown if there is no any default value selected 83 | Use ```bs-dropdown-items``` attribute to specify the dropdown options 84 | Use ```ng-model``` to set the default selected value 85 | Use ```ng-change``` to listen up the change event on dropdown 86 | Use ```bs-dropdown-divider``` to specify the divider, for example bs-dropdown-divider="{{[2,5]}}". 87 | Use ```bs-dropdown-item-disabled``` to specify which option should be disabled,for example bs-dropdown-item-disabled="{{[2,5]}}". 88 | Use ```bs-dropdown-disabled``` to set dropdown disabled, for example bs-dropdown-disabled="true". 89 | Use ```bs-dropdown-multi``` to specify bsDropdown to be a multi-select dropdown. -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-bootstrap-dropdown", 3 | "version": "0.9.0", 4 | "homepage": "https://github.com/AllenFang/angular-bootstrap-dropdown", 5 | "authors": [ 6 | "AllenFang " 7 | ], 8 | "description": "It's a angular directive for bootstrap dropdown and multi-select", 9 | "keywords": [ 10 | "angular", 11 | "directive", 12 | "bootstrap", 13 | "dropdown", 14 | "select", 15 | "multiSelect" 16 | ], 17 | "main": [ 18 | "dist/bsDropdown.min.js" 19 | ], 20 | "license": "MIT", 21 | "ignore": [ 22 | "node_modules", 23 | "bower_components", 24 | "test", 25 | "example" 26 | ], 27 | "dependencies": { 28 | "angular": "~1.3.14", 29 | "bootstrap": "~3.3.2" 30 | }, 31 | "devDependencies": { 32 | "angular-mocks": "~1.3.14" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /dist/bsDropdown.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"use strict";var i=t.module("ng.bs.dropdown",[]);i.constant("bsDropdownCfg",{display:"DropDown",disabled:!1,divider:[],disabledItems:[]}),i.run(["$templateCache",function(e){e.put("bsDropdown/templates/defaultTemplate.html",['"].join("")),e.put("bsDropdown/templates/multiSelectTemplate.html",['"].join(""))}]),i.controller("bsDropdownController",["$scope","$element","$attrs",function(e){var i,n=this;this.init=function(d){i=d,i.$render=function(){var d=t.isDefined(e.selected);d&&i.$setViewValue(e.selected),n.$render(i.$viewValue),e.multiSelect&&(d||(e.selected=i.$viewValue),n.checkMultiOptions())}},this.checkMultiOptions=function(){for(var t=0;t 2 | 3 | bsDropdown demo 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
A basic function demo for bsDropdown.
15 |
16 |

You select {{selectYear}} ....

17 |
22 |
23 |
24 |
25 |
A divider demo for bsDropdown.
26 |
27 |
32 |
33 |
34 |
35 |
A disabled items demo for bsDropdown.
36 |
37 |
42 |
43 |
44 |
45 |
A disabled dropdown demo for bsDropdown.
46 |
47 |
52 |
53 |
54 |
55 |
A multiselect dropdown demo for bsDropdown.
56 |
57 |
65 |
66 |
67 |
68 |

Tutorial

69 |

70 | 
71 |         1. Use bs-dropdown-display attribute in bsDropdown to specify the default text on dropdown
72 | 
73 |         2. Use bs-dropdown-items(required) attribute in bsDropdown to specify the options you want to display in dropdown
74 | 
75 |         3. Use ng-model(required) attribute in bsDropdown to specify the current selected option in dropndown.
76 | If no data you want to select on doopwodn default, you can give null value.
77 | For example, you can modify $scope.selectYear = null; in YearController in this example. 78 | 79 | 4. Use ng-change to listen a change event on bsDropdown.
80 | 81 | 5. Use bs-dropdown-divider to specify the divider, for example bs-dropdown-divider="{{[2,5]}}".
82 | 83 | 6. Use bs-dropdown-item-disabled to specify which option should be disabled,
84 | for example bs-dropdown-item-disabled="{{[2,5]}}".
85 | 86 | 7. Use bs-dropdown-disabled to set bsDropdown disabled, for example bs-dropdown-disabled="true".
87 | 88 | 8. Use bs-dropdown-multi to specify bsDropdown to be a multi-select dropdown.
89 |
90 | 91 | -------------------------------------------------------------------------------- /example/js/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module("demoApp", ['ng.bs.dropdown']) 4 | .controller("YearController", function($scope){ 5 | $scope.years = [ 6 | "2015", 7 | "2014", 8 | "2013", 9 | "2012", 10 | "2011", 11 | "2010" 12 | ]; 13 | $scope.selectYear = $scope.years[2]; 14 | $scope.changeYear = function(){ 15 | console.log("YearController say... " + $scope.selectYear); 16 | } 17 | }) 18 | .controller("DividerController", function($scope){ 19 | $scope.actions = [ 20 | "Action", 21 | "Another action", 22 | "Something else here", 23 | "separated link1", 24 | "Action anain", 25 | "Nothing else", 26 | "separated link2" 27 | ]; 28 | $scope.selectAction = null; 29 | }) 30 | .controller("MultiSelectController", function($scope){ 31 | $scope.actions = [ 32 | "Action", 33 | "Another action", 34 | "Something else here", 35 | "separated link1", 36 | "Action anain", 37 | "Nothing else", 38 | "separated link2" 39 | ]; 40 | $scope.selectAction = [$scope.actions[0],$scope.actions[3]]; 41 | $scope.change = function(){ 42 | console.log("MultiSelectController say... " + $scope.selectAction); 43 | } 44 | });; -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var gulp = require('gulp'); 4 | var jshint = require('gulp-jshint'); 5 | var uglify = require('gulp-uglify'); 6 | var rename = require('gulp-rename'); 7 | var karma = require('gulp-karma'); 8 | 9 | var srcFile = 'src/bsDropdown.js'; 10 | var testFile = [ 11 | './bower_components/angular/angular.min.js', 12 | './bower_components/angular-mocks/angular-mocks.js', 13 | './src/bsDropdown.js', 14 | './test/bsDropdown-test.js' 15 | ]; 16 | 17 | gulp.task('default', ['test', 'jshint', 'uglify']); 18 | gulp.task('dev', function(){ 19 | return gulp.watch([srcFile], ['default']) 20 | }); 21 | 22 | 23 | gulp.task('jshint', function(){ 24 | return gulp.src(srcFile) 25 | .pipe(jshint()) 26 | .pipe(jshint.reporter('default')); 27 | }); 28 | 29 | gulp.task('uglify', function(){ 30 | return gulp.src(srcFile) 31 | .pipe(uglify({ 32 | preserveComments: 'some' 33 | })) 34 | .pipe(rename({ 35 | suffix: '.min' 36 | })) 37 | .pipe(gulp.dest('dist')); 38 | }); 39 | 40 | gulp.task('test', function(){ 41 | return gulp.src(testFile) 42 | .pipe(karma({ 43 | configFile: 'test/karma.conf.js', 44 | action: 'run' 45 | })) 46 | .on('error', function(err){ 47 | throw err; 48 | }); 49 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-bootstrap-dropdown", 3 | "version": "0.9.0", 4 | "description": "It's a angular directive for bootstrap dropdown and multi-select.", 5 | "scripts": { 6 | "test": "node_modules/karma/bin/karma start test/karma.conf.js" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/AllenFang/angular-bootstrap-dropdown.git" 11 | }, 12 | "keywords": [ 13 | "angular", 14 | "directive", 15 | "bootstrap", 16 | "dropdown", 17 | "select", 18 | "multiSelect" 19 | ], 20 | "author": "AllenFang", 21 | "license": "MIT", 22 | "devDependencies": { 23 | "gulp": "~3.8.11", 24 | "gulp-jshint": "~1.9.2", 25 | "gulp-uglify": "~1.1.0", 26 | "gulp-rename": "~1.2.0", 27 | "karma": "~0.12.31", 28 | "karma-cli": "0.0.4", 29 | "jasmine-core": "~2.2.0", 30 | "karma-jasmine": "~0.3.5", 31 | "karma-chrome-launcher": "~0.1.7", 32 | "gulp-karma": "0.0.4" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/bsDropdown.js: -------------------------------------------------------------------------------- 1 | (function(window, angular, undefined){ 2 | 'use strict'; 3 | 4 | var bd = angular.module("ng.bs.dropdown", []); 5 | bd.constant('bsDropdownCfg', { 6 | display: 'DropDown', 7 | disabled: false, 8 | divider:[], 9 | disabledItems: [] 10 | }); 11 | bd.run(['$templateCache', function($templateCache){ 12 | $templateCache.put('bsDropdown/templates/defaultTemplate.html',[ 13 | '' 24 | ].join('')); 25 | 26 | $templateCache.put('bsDropdown/templates/multiSelectTemplate.html',[ 27 | '' 40 | ].join('')); 41 | }]); 42 | bd.controller("bsDropdownController", 43 | ["$scope", "$element", "$attrs", function($scope, $element, $attrs){ 44 | var ngModelCtrl, self = this; 45 | this.init = function(ngModelCtrl_){ 46 | ngModelCtrl = ngModelCtrl_; 47 | ngModelCtrl.$render = function(){ 48 | var isSelectValDefined = angular.isDefined($scope.selected); 49 | if(isSelectValDefined) 50 | ngModelCtrl.$setViewValue($scope.selected); 51 | 52 | self.$render(ngModelCtrl.$viewValue); 53 | if($scope.multiSelect) { 54 | if(!isSelectValDefined) 55 | $scope.selected = ngModelCtrl.$viewValue; 56 | self.checkMultiOptions(); 57 | } 58 | }; 59 | }; 60 | 61 | this.checkMultiOptions = function(){ 62 | for(var i=0;i<$scope._bsDropdownItems.length;i++){ 63 | var item = $scope._bsDropdownItems[i]; 64 | if($scope.selected.indexOf(item.text) != -1){ 65 | $scope._bsDropdownItems[i].checked = true; 66 | }else{ 67 | $scope._bsDropdownItems[i].checked = false; 68 | } 69 | } 70 | }; 71 | 72 | $scope.selectItem = function(item){ 73 | var text = item.text; 74 | if(!item.isDisabled){ 75 | if($scope.multiSelect) { 76 | var index = -1; 77 | if((index=$scope.selected.indexOf(text)) == -1){ 78 | var newSelected = []; 79 | for(var i=0;i<$scope.selected.length;i++){ 80 | newSelected.push($scope.selected[i]); 81 | } 82 | newSelected.push(text); 83 | $scope.selected = newSelected; 84 | } 85 | else{ 86 | var newSelected = []; 87 | for(var i=0;i<$scope.selected.length;i++){ 88 | if(i != index) newSelected.push($scope.selected[i]); 89 | } 90 | $scope.selected = newSelected; 91 | } 92 | } else{ 93 | $scope.selected = text; 94 | } 95 | ngModelCtrl.$render(); 96 | } 97 | }; 98 | }]); 99 | bd.directive("bsDropdown", ['bsDropdownCfg', function(bsDropdownCfg){ 100 | return{ 101 | scope:{ 102 | bsDropdownItems: "=" 103 | }, 104 | require: ['bsDropdown','?ngModel'], 105 | controller: "bsDropdownController", 106 | templateUrl: function(elem, attr){ 107 | return angular.isDefined(attr.bsDropdownMulti)? 108 | "bsDropdown/templates/multiSelectTemplate.html": 109 | "bsDropdown/templates/defaultTemplate.html"; 110 | }, 111 | link: function(scope, el, attr, ctrls){ 112 | var bsDropdownCtrl = ctrls[0], ngModelCtrl = ctrls[1]; 113 | var defaultDisplay = angular.isDefined(attr.bsDropdownDisplay)? 114 | attr.bsDropdownDisplay:bsDropdownCfg.display; 115 | 116 | scope._bsDropdownItems = scope.bsDropdownItems; 117 | scope.divider = angular.isDefined(attr.bsDropdownDivider)? 118 | scope.$eval(attr.bsDropdownDivider):bsDropdownCfg.divider; 119 | scope.disabledItems = angular.isDefined(attr.bsDropdownItemDisabled)? 120 | scope.$eval(attr.bsDropdownItemDisabled):bsDropdownCfg.disabledItems; 121 | scope.disabled = angular.isDefined(attr.bsDropdownDisabled)? 122 | scope.$eval(attr.bsDropdownDisabled):bsDropdownCfg.disabled; 123 | scope.multiSelect = angular.isDefined(attr.bsDropdownMulti); 124 | 125 | bsDropdownCtrl.init(ngModelCtrl); 126 | bsDropdownCtrl.$render = function(displayText){ 127 | changeShowText(displayText); 128 | }; 129 | 130 | scope._bsDropdownItems = createDropdownItems(); 131 | bsDropdownRender(); 132 | 133 | function bsDropdownRender(){ 134 | var id = (+new Date() + Math.floor(Math.random() * 999999)).toString(36); 135 | el.find("#bsDropDown").attr("id", id); 136 | el.find(".dropdown-menu").attr("aria-labelledby", id); 137 | changeShowText(defaultDisplay); 138 | if(scope.disabled){ 139 | el.find("button").addClass("disabled"); 140 | } 141 | } 142 | 143 | function changeShowText(text){ 144 | var _text = text; 145 | if(angular.isArray(_text)) { 146 | if(_text.length == 0) 147 | _text = null; 148 | else 149 | _text = _text.join(); 150 | } 151 | scope.showText = _text !== null?_text:defaultDisplay; 152 | } 153 | 154 | function createDropdownItems(){ 155 | var dropdownItem = []; 156 | var _k = 0; 157 | for(var i=0;i 2 | 6 | 11 | -------------------------------------------------------------------------------- /test/bsDropdown-multiselect-test.js: -------------------------------------------------------------------------------- 1 | describe("bsDropdown multi-select Tests", function(){ 2 | var mockScope; 3 | var compile; 4 | var el; 5 | var dropdownItem = [ 6 | "Option0", 7 | "Option1", 8 | "Option2", 9 | "Option3", 10 | "Option4", 11 | "Option5" 12 | ]; 13 | 14 | beforeEach(angular.mock.module("ng.bs.dropdown")); 15 | 16 | describe("A basic multi-select dropdown test", function(){ 17 | beforeEach(inject(function($rootScope, $compile){ 18 | mockScope = $rootScope.$new(); 19 | compile = $compile; 20 | mockScope.data = dropdownItem; 21 | //default select value 22 | mockScope.selectData = [mockScope.data[3], mockScope.data[0]]; 23 | 24 | el = "
"; 25 | el = $compile(el)(mockScope); 26 | mockScope.$digest(); 27 | })); 28 | 29 | it("the isolateScope in bsDropdown directive should be correct", function(){ 30 | var isolated = el.isolateScope(); 31 | var _bsDropdownItems = isolated._bsDropdownItems; 32 | expect(_bsDropdownItems).not.toBeNull(); 33 | expect(_bsDropdownItems).toBeDefined(); 34 | expect(mockScope.data.length).toBe(_bsDropdownItems.length); 35 | for(var i=0;i<_bsDropdownItems.length;i++){ 36 | var item = _bsDropdownItems[i]; 37 | expect(item).toBeDefined(); 38 | expect(item._k).toBe(i); 39 | expect(item.text).toBe(mockScope.data[i]); 40 | expect(item.isDivider).toBe(false); 41 | expect(item.isDisabled).toBe(false); 42 | } 43 | expect(isolated.showText).toBeDefined(); 44 | expect(isolated.showText).toBe(mockScope.selectData.join()); 45 | expect(isolated.divider).toBeDefined(); 46 | expect(isolated.divider.length).toBe(0); 47 | expect(isolated.disabledItems).toBeDefined(); 48 | expect(isolated.disabledItems.length).toBe(0); 49 | expect(isolated.disabled).toBeDefined(); 50 | expect(isolated.disabled).toBe(false); 51 | expect(isolated.multiSelect).toBeDefined(); 52 | expect(isolated.multiSelect).toBe(true); 53 | }); 54 | 55 | it("the display text should be default selected data", function(){ 56 | expect(el.find("button").text()).toBe(mockScope.selectData.join()); 57 | }); 58 | 59 | it("the item on dropdown should be correct", function(){ 60 | var itemElms = el.find("li"); 61 | expect(itemElms.length).toBe(mockScope.data.length); 62 | for(var i=0;i