diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c2cdfb8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + + +[*] + +# Change these settings to your own preference +indent_style = space +indent_size = 2 + +# We recommend you to keep these unchanged +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..78dbd62 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules +bower_components +.idea \ No newline at end of file diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..cd7357a --- /dev/null +++ b/.jshintrc @@ -0,0 +1,25 @@ +{ + "node": true, + "browser": true, + "esnext": true, + "bitwise": true, + "camelcase": true, + "curly": true, + "eqeqeq": true, + "immed": true, + "indent": 2, + "latedef": true, + "newcap": true, + "noarg": true, + "quotmark": "single", + "regexp": true, + "undef": true, + "unused": true, + "strict": true, + "trailing": true, + "smarttabs": true, + "jquery": true, + "globals": { + "angular": false + } +} diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..00f95a4 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,75 @@ +'use strict'; + +module.exports = function(grunt) { + + // Load all grunt tasks automatically + require('load-grunt-tasks')(grunt); + + // Time how long grunt task take. Can help when optimizing build times + require('time-grunt')(grunt); + + //Configure grunt + grunt.initConfig({ + + // The actual grunt server settings + connect: { + options: { + port: 9000, + // Change this to '0.0.0.0' to access the server from outside. + hostname: 'localhost', + livereload: 35729 + }, + livereload: { + options: { + open: true, + base: [ + '.', + 'examples' + ] + } + } + }, + + //Watch files for changes, and run tasks base on the changed files. + watch: { + js: { + files: ['src/*.js'], + tasks: ['newer:jshint:all'], + options: { + livereload: true + } + }, + livereload: { + options: { + livereload: '<%= connect.options.livereload %>' + }, + files: [ + 'examples/*.html', + 'examples/{,*/*.js}', + 'examples{,*/}*.css', + 'examples/{,*/}*.{png,jpg,jpeg,gif,webp,svg}' + ] + } + }, + + // Make sure code styles are up to par and there are no obvious mistakes + jshint: { + options: { + jshintrc: '.jshintrc', + reporter: require('jshint-stylish') + }, + all: [ + 'Gruntfile.js', + 'src/*.js', + 'examples/{,*/}*.js' + ] + + } + }); + + // Creates the 'serve' task + grunt.registerTask('serve', [ + 'connect:livereload', + 'watch' + ]); +}; diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fbac739 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2014 Nyasoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..fa3a188 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# nya-bootstrap-select # +--- + +An AngularJS directive wrapper for silviomoreto's Bootstrap-select, which supports `ngRepeat` in options to dynamically build a Bootstrap-select + +This project is not ready for publish yet. Test case is not written. So take your own risk. \ No newline at end of file diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..0d7bf60 --- /dev/null +++ b/bower.json @@ -0,0 +1,14 @@ +{ + "name": "nya-bootstrap-select", + "version": "1.0.0", + "dependencies": { + "angular": "*", + "bootstrap-select": "*", + "jquery": ">=1.7" + }, + "devDependencies": { + "bootstrap-css":"~3.0", + "angular-mocks": "*", + "angular-scenario": "*" + } +} diff --git a/examples/app.js b/examples/app.js new file mode 100644 index 0000000..db71944 --- /dev/null +++ b/examples/app.js @@ -0,0 +1,39 @@ +'use strict'; + +angular.module('demoApp',['nyaBootstrapSelect']) + .controller('MainCtrl', function($scope){ + + var options = ['Alpha', 'Bravo', 'Charlie', 'Delta', + 'Echo', 'Foxtrot', 'Golf', 'Hotel', 'Juliet', 'Kilo', 'Lima', + 'Mike', 'November', 'Oscar', 'Papa', 'Quebec', 'Romeo', 'Sierra', + 'Tango', 'Uniform', 'Victor', 'Whiskey', 'X-ray', 'Yankee', 'Zulu' + ]; + + $scope.options1 = [ + 'Alpha', + 'Bravo', + 'Charlie' + ]; + + $scope.myModel = ['Bravo']; + + $scope.changeOptions = function(targetOption) { + var length = Math.max(Math.min(Math.floor(Math.random() * options.length), 10), 3); + var newOptions = {}; + for(var i = 0; i < length; i++) { + newOptions[options[Math.floor(Math.random() * options.length)]] = true; + } + $scope[targetOption] = Object.keys(newOptions); + }; + + $scope.options2 = options.splice(0, 6); + + $scope.changeModel = function(model) { + var length = Math.floor(Math.random() * $scope.options2.length); + var newModel = {}; + for(var i = 0; i < length; i++) { + newModel[$scope.options2[Math.floor(Math.random() * $scope.options2.length)]] = true; + } + $scope[model] = Object.keys(newModel); + }; + }); \ No newline at end of file diff --git a/examples/index.html b/examples/index.html new file mode 100644 index 0000000..de5ee0d --- /dev/null +++ b/examples/index.html @@ -0,0 +1,79 @@ + + + + + nya-bootstrap-select demo + + + + + + + +
+
+

nya-bootstrap-select

+

An angular wrapper for Bootstrap-select with dynamic loading options support

+ View on Github +
+
+
+
+

Overview

+

This is an angular directive wrapping for popular custom select Bootstrap-select. + Unlike other directives. This directive support ng-repeat in option. +

+

All of the Bootstrap-select's function is supported. + The select result is bound to a model, so you don't need to use + Bootstrap-select's api to get value. + just write your select in the angular way +

+

ngOptions directive is not supported yet.

+
+
+

Example

+

Dynamic modify options

+

The select has three options initially. Click the Change Options button to change the options randomly.

+ +
+

The options of the select is {{options1}}

+

You have select {{myModel}}

+ + ----> + +
+ +

Dynamic modify selection

+

The model change will affect the selection. Click the Change Model button to change the model randomly.

+
+

The options of the select is {{options2}}

+

You have select {{model2}}

+ + ----> + +
+ +
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/main.css b/examples/main.css new file mode 100644 index 0000000..347d5b4 --- /dev/null +++ b/examples/main.css @@ -0,0 +1,33 @@ +.masthead { + color: #ffffff; + padding-top: 10px; + padding-bottom: 20px; + background: #000000; /* Old browsers */ + background: -moz-linear-gradient(top, #000000 0%, #45484d 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#000000), color-stop(100%,#45484d)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #000000 0%,#45484d 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #000000 0%,#45484d 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #000000 0%,#45484d 100%); /* IE10+ */ + background: linear-gradient(to bottom, #000000 0%,#45484d 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#000000', endColorstr='#45484d',GradientType=0 ); /* IE6-9 */ +} + +.masthead h1 { + margin-top: 20px; + margin-bottom: 15px; +} + +.footer { + text-align: center; + padding: 30px 0; + margin-top: 70px; + color: #ffffff; + background: #000000; /* Old browsers */ + background: -moz-linear-gradient(top, #45484d 0%, #000000 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#45484d), color-stop(100%,#000000)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #45484d 0%,#000000 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #45484d 0%,#000000 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #45484d 0%,#000000 100%); /* IE10+ */ + background: linear-gradient(to bottom, #45484d 0%,#000000 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#45484d', endColorstr='#000000',GradientType=0 ); /* IE6-9 */ +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..d3d4799 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "nya-bootstrap-select", + "version": "1.0.0", + "devDependencies": { + "grunt": "~0.4.2", + "grunt-contrib-connect": "~0.6.0", + "grunt-contrib-watch": "~0.5.3", + "load-grunt-tasks": "~0.3.0", + "time-grunt": "~0.2.9", + "jshint-stylish": "~0.1.5", + "grunt-newer": "~0.6.1", + "grunt-contrib-jshint": "~0.8.0", + "karma-phantomjs-launcher": "~0.1.2", + "karma": "~0.10.9" + } +} diff --git a/src/nya-bootstrap-select.js b/src/nya-bootstrap-select.js new file mode 100644 index 0000000..88675a9 --- /dev/null +++ b/src/nya-bootstrap-select.js @@ -0,0 +1,73 @@ +'use strict'; + +angular.module('nyaBootstrapSelect',[]) + .directive('nyaSelectpicker', function () { + return { + restrict: 'CA', + scope: false, + require: ['^ngModel', 'select'], + + link: function(scope, element, attrs, ctrls) { + var ngCtrl = ctrls[0]; + var selectCtrl = ctrls[1]; + // prevent selectDirective render an unknownOption. + selectCtrl.renderUnknownOption = angular.noop; + var optionArray = []; + scope.$watch(function optionDOMWatch(){ + // check every option if has changed. + var optionElements = $(element).find('option.ng-scope'); + + if(optionElements.length !== optionArray.length) { + optionArray = makeOptionArray(optionElements); + buildSelector(); + } else { + var hasChanged = false; + optionElements.each(function(index, value){ + if(optionArray[index].text !== value.text || optionArray[index].value !== value.value) { + hasChanged = true; + + } + }); + if(hasChanged) { + buildSelector(); + } + optionArray = makeOptionArray(optionElements); + } + + }); + + ngCtrl.$render = function() { + // model -> view + var data = $(element).data('selectpicker'); + if(data) { + $(element).val(ngCtrl.$viewValue).selectpicker('render'); + } + }; + + function makeOptionArray(optionElements) { + var optionArray = []; + optionElements.each(function(index, childNode){ + optionArray.push({ + value: childNode.value, + text: childNode.text + }); + }); + return optionArray; + } + + function buildSelector() { + // build new selector. if previous select exists. remove previous data and DOM + var oldSelectPicker = $(element).data('selectpicker'); + if(oldSelectPicker) { + oldSelectPicker.$menu.parent().remove(); + oldSelectPicker.$newElement.remove(); + $(element).removeData('selectpicker'); + } + $(element).selectpicker(); +// console.log(ngCtrl.$modelValue); + $(element).val(ngCtrl.$modelValue).selectpicker('render'); + } + + } + }; + });