├── .babelrc ├── .gitignore ├── src ├── module.js ├── bootstrap │ ├── actions-trcl.html │ ├── help.html │ ├── section.html │ ├── fieldset.html │ ├── tabs.html │ ├── submit.html │ ├── actions.html │ ├── checkbox.html │ ├── radios.html │ ├── select.html │ ├── radios-inline.html │ ├── checkboxes.html │ ├── radio-buttons.html │ ├── array.html │ ├── textarea.html │ ├── default.html │ └── tabarray.html └── bootstrap-decorator.js ├── gulp ├── tasks │ ├── default.js │ ├── jscs.js │ ├── serve.js │ └── protractor.js └── index.js ├── test └── protractor │ ├── conf.js │ └── specs │ ├── validation-messages.js │ ├── custom-validation.js │ └── tabarray.js ├── .jscsrc ├── gulpfile.js ├── .github ├── PULL_REQUEST_TEMPLATE.md └── ISSUE_TEMPLATE.md ├── examples ├── data │ ├── types.json │ ├── simple-oneOf.json │ ├── simple.json │ ├── conditional-required.json │ ├── tabarray-sortable.json │ ├── tabarray-add-disabled.json │ ├── tabarray-remove-disabled.json │ ├── grid.json │ ├── complex-keys.json │ ├── tabarray.json │ ├── titlemaps.json │ ├── array.json │ ├── array-deep.json │ └── sink.json ├── custom-validators.html └── example.html ├── webpack.config.dist.js ├── bower.json ├── CONTRIBUTING.md ├── package.json ├── webpack.config.js ├── README.md └── dist ├── angular-schema-form-bootstrap.min.js └── angular-schema-form-bootstrap.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | 4 | -------------------------------------------------------------------------------- /src/module.js: -------------------------------------------------------------------------------- 1 | require('bootstrap-decorator.js'); 2 | -------------------------------------------------------------------------------- /gulp/tasks/default.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | 3 | gulp.task('default', [ 4 | 'jscs' 5 | ]); 6 | -------------------------------------------------------------------------------- /test/protractor/conf.js: -------------------------------------------------------------------------------- 1 | exports.config = { 2 | seleniumAddress: 'http://localhost:4444/wd/hub' 3 | } 4 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "google", 3 | "maximumLineLength": 1000, 4 | "requireSemicolons": true 5 | } 6 | -------------------------------------------------------------------------------- /src/bootstrap/actions-trcl.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /src/bootstrap/help.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 2 | //all of the tasks themselves are contained in the gulp/tasks directory, 3 | //which is accessed through gulp/index.js 4 | 5 | require('./gulp'); 6 | -------------------------------------------------------------------------------- /gulp/tasks/jscs.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | jscs = require('gulp-jscs'); 3 | 4 | gulp.task('jscs', function() { 5 | gulp.src('./src/**/*.js') 6 | .pipe(jscs()); 7 | }); 8 | -------------------------------------------------------------------------------- /src/bootstrap/section.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /gulp/index.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | tasks = fs.readdirSync('./gulp/tasks'), 3 | gulp = require('gulp'); 4 | 5 | tasks.forEach(function(task) { 6 | require('./tasks/' + task); 7 | }); 8 | -------------------------------------------------------------------------------- /gulp/tasks/serve.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | webserver = require('gulp-webserver'); 3 | 4 | gulp.task('serve', function() { 5 | gulp.src('.') 6 | .pipe(webserver({ 7 | livereload: true, 8 | directoryListing: true, 9 | open: true 10 | })); 11 | }); 12 | 13 | -------------------------------------------------------------------------------- /src/bootstrap/fieldset.html: -------------------------------------------------------------------------------- 1 |
2 | {{ form.title }} 3 |
4 |
5 | -------------------------------------------------------------------------------- /src/bootstrap/tabs.html: -------------------------------------------------------------------------------- 1 |
2 | 10 | 11 |
12 |
13 |
14 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | #### Description 2 | 3 | Add your description here 4 | 5 | #### Fixes Related issues 6 | - add related 7 | - issues here 8 | 9 | #### Checklist 10 | - [ ] I have read and understand the CONTRIBUTIONS.md file 11 | - [ ] I have searched for and linked related issues 12 | - [ ] I have created test cases to ensure quick resolution of the PR is easier 13 | - [ ] I am NOT targeting main branch 14 | - [ ] I did NOT include the dist folder in my PR 15 | 16 | @json-schema-form/angular-schema-form-bootstrap-lead 17 | -------------------------------------------------------------------------------- /examples/data/types.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": { 3 | "type": "object", 4 | "title": "Types", 5 | "properties": { 6 | "string": { 7 | "type": "string", 8 | "minLength": 3 9 | }, 10 | "integer": { 11 | "type": "integer" 12 | }, 13 | "number": { 14 | "type": "number" 15 | }, 16 | "boolean": { 17 | "type": "boolean" 18 | } 19 | }, 20 | "required": ["number"] 21 | }, 22 | "form": [ 23 | "*", 24 | {"type": "submit", "title": "OK"} 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /webpack.config.dist.js: -------------------------------------------------------------------------------- 1 | const config = require('./webpack.config.js'); 2 | const path = require('path'); 3 | 4 | config.entry = { 5 | 'angular-schema-form-bootstrap': [ path.join(__dirname, 'src', 'module') ], 6 | 'angular-schema-form-bootstrap-bundled': [ 'angular-schema-form', path.join(__dirname, 'src', 'module') ], 7 | 'angular-schema-form-bootstrap.min': [ path.join(__dirname, 'src', 'module') ], 8 | 'angular-schema-form-bootstrap-bundled.min': [ 'angular-schema-form', path.join(__dirname, 'src', 'module') ], 9 | } 10 | 11 | module.exports = config; 12 | -------------------------------------------------------------------------------- /examples/data/simple-oneOf.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": { 3 | "type": "object", 4 | "title": "Comment", 5 | "properties": { 6 | "name": { 7 | "oneOf": [ 8 | { 9 | "type": "string", 10 | "enum": ["Bob"] 11 | }, 12 | { 13 | "type": "string", 14 | "pattern": "^\\S+ \\S+$" 15 | } 16 | ] 17 | } 18 | }, 19 | "required": ["name"] 20 | }, 21 | "form": [ 22 | { 23 | "key": "name", 24 | "title": "Name" 25 | } 26 | 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | #### Enhancement 9 | As a user/developer, when I ... I should be able to ... 10 | 11 | #### Expected behaviour 12 | I expected ... 13 | 14 | #### Actual behaviour 15 | It actually ... 16 | 17 | #### Gist/Plunker/Demo 18 | [Description](url) 19 | 20 | #### Related issues 21 | This is/maybe related to ... 22 | 23 | @json-schema-form/angular-schema-form-bootstrap-lead 24 | -------------------------------------------------------------------------------- /src/bootstrap/submit.html: -------------------------------------------------------------------------------- 1 |
2 | 7 | 15 |
16 | -------------------------------------------------------------------------------- /src/bootstrap/actions.html: -------------------------------------------------------------------------------- 1 |
2 | 7 | 13 |
14 | -------------------------------------------------------------------------------- /src/bootstrap/checkbox.html: -------------------------------------------------------------------------------- 1 |
8 | 18 |
19 |
20 | -------------------------------------------------------------------------------- /examples/data/simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": { 3 | "type": "object", 4 | "title": "Comment", 5 | "properties": { 6 | "name": { 7 | "title": "Name", 8 | "type": "string" 9 | }, 10 | "email": { 11 | "title": "Email", 12 | "type": "string", 13 | "pattern": "^\\S+@\\S+$", 14 | "description": "Email will be used for evil." 15 | }, 16 | "comment": { 17 | "title": "Comment", 18 | "type": "string", 19 | "maxLength": 20, 20 | "validationMessage": "Don't be greedy!" 21 | } 22 | }, 23 | "required": ["name","email","comment"] 24 | }, 25 | "form": [ 26 | "name", 27 | { "key": "email", "ngModelOptions": {"updateOn": "blur"}}, 28 | { 29 | "key": "comment", 30 | "type": "textarea", 31 | "placeholder": "Make a comment" 32 | }, 33 | { 34 | "type": "submit", 35 | "style": "btn-info", 36 | "title": "OK" 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /src/bootstrap/radios.html: -------------------------------------------------------------------------------- 1 |
7 | 10 |
11 | 21 |
22 |
23 |
24 | -------------------------------------------------------------------------------- /src/bootstrap/select.html: -------------------------------------------------------------------------------- 1 |
8 | 11 | 20 |
21 |
22 | -------------------------------------------------------------------------------- /src/bootstrap/radios-inline.html: -------------------------------------------------------------------------------- 1 |
7 | 10 |
11 | 21 |
22 |
23 |
24 | -------------------------------------------------------------------------------- /examples/data/conditional-required.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": { 3 | "type": "object", 4 | "properties": { 5 | "switch": { 6 | "title": "Spam me, please", 7 | "type": "boolean" 8 | }, 9 | "email": { 10 | "title": "Email", 11 | "type": "string", 12 | "pattern": "^\\S+@\\S+$", 13 | "description": "Email will be used for evil." 14 | } 15 | }, 16 | "required": ["switch"] 17 | }, 18 | "form": [ 19 | { 20 | "type": "help", 21 | "helpvalue": "

Schema Form does not support oneOf (yet), but you can do a workaround and simulate certain scenarios with 'condition' and 'required' (and/or 'readonly') in the form.

" 22 | }, 23 | "switch", 24 | { 25 | "key": "email", 26 | "condition": "model.switch", 27 | "required": true 28 | }, 29 | { 30 | "key": "email", 31 | "condition": "!model.switch" 32 | }, 33 | { 34 | "type": "submit", 35 | "style": "btn-info", 36 | "title": "OK" 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-schema-form-bootstrap", 3 | "authors": [ 4 | "David Jensen (Textalk) ", 5 | "Marcel Bennett " 6 | ], 7 | "description": "Bootstrap 3 decorator for Angular Schema Form", 8 | "main": "dist/bootstrap-decorator.js", 9 | "keywords": [ 10 | "angular-schema-form-decorator", 11 | "json-schema", 12 | "json-schema-form", 13 | "angular-schema-form" 14 | ], 15 | "license": "MIT", 16 | "homepage": "schemaform.io", 17 | "ignore": [ 18 | "**/.*", 19 | "node_modules", 20 | "bower_components", 21 | "test", 22 | "tests" 23 | ], 24 | "dependencies": { 25 | "angular-schema-form": "1.0.0-alpha.4" 26 | }, 27 | "devDependencies": { 28 | "angular-ui-ace": "bower", 29 | "angular-schema-form-datepicker": ">= 0.1.0", 30 | "angular-schema-form-colorpicker": ">= 0.1.0", 31 | "jquery": "~2.1.1", 32 | "angular-mocks": ">= 1.2", 33 | "tx-tinymce": ">= 0.0.5", 34 | "angular-ui-sortable": ">=0.12.11", 35 | "bootstrap-vertical-tabs": "~1.2.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/data/tabarray-sortable.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": { 3 | "type": "object", 4 | "title": "Tab Array: Sortable (Drag and Drop)", 5 | "properties": { 6 | "sortableTabArray": { 7 | "type": "array", 8 | "items": { 9 | "type": "object", 10 | "properties": { 11 | "name": { "type": "string" }, 12 | "nick": { "type": "string" } 13 | } 14 | } 15 | } 16 | } 17 | }, 18 | "form": [ 19 | { 20 | "type": "section", 21 | "htmlCss": "row", 22 | "items": [ 23 | { 24 | "type": "help", 25 | "helpvalue": "

Drag and drop sortable tab array

" 26 | }, 27 | { 28 | "key": "sortableTabArray", 29 | "type": "tabarray", 30 | "title": "My name is: {{ value.name }}", 31 | "items" : [ 32 | {"key": "sortableTabArray[].name", "htmlClass": "nameField"}, 33 | {"key": "sortableTabArray[].nick", "htmlClass": "nickField"} 34 | ] 35 | } 36 | ] 37 | } 38 | ], 39 | "model": {} 40 | } 41 | -------------------------------------------------------------------------------- /src/bootstrap/checkboxes.html: -------------------------------------------------------------------------------- 1 |
9 | 13 | 14 |
15 | 24 | 25 |
26 |
27 |
28 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ------------ 3 | We love contributions! 4 | 5 | **Please base any merge request on the *development* branch instead of *master*.** 6 | 7 | The reason for this is that we're trying to use 8 | [git flow](http://danielkummer.github.io/git-flow-cheatsheet/), and it makes merging your pull 9 | request heck of a lot easier for us. 10 | 11 | Please avoid including anything from the `dist/` directory as that can make merging harder, and we 12 | always generate these files when we make a new release. 13 | 14 | If its a new field type consider making it an add-on instead, 15 | especially if it has external dependencies. See [extending Schema Form documentation.](https://github.com/json-schema-form/angular-schema-form/blob/development/docs/extending.md) 16 | 17 | With new features we love to see updates to the docs as well as tests, that makes it super 18 | easy and fast for us to merge it! 19 | 20 | Also consider running any code through the code style checker [jscs](https://github.com/mdevils/node-jscs) 21 | (or even better use it in your editor) with preset set to `google`. You can also use `gulp jscs` to 22 | check your code. 23 | -------------------------------------------------------------------------------- /examples/data/tabarray-add-disabled.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": { 3 | "type": "object", 4 | "title": "Tab Array: Add Disabled", 5 | "properties": { 6 | "addDisabledTabArray": { 7 | "type": "array", 8 | "items": { 9 | "type": "object", 10 | "properties": { 11 | "name": { "type": "string" }, 12 | "nick": { "type": "string" } 13 | } 14 | } 15 | } 16 | } 17 | }, 18 | "form": [ 19 | { 20 | "type": "section", 21 | "htmlCss": "row", 22 | "items": [ 23 | { 24 | "type": "help", 25 | "helpvalue": "

Tab array with add link hidden

" 26 | }, 27 | { 28 | "key": "addDisabledTabArray", 29 | "type": "tabarray", 30 | "add": null, 31 | "title": "My name is: {{ value.name }}", 32 | "sortOptions": { 33 | "disabled": true 34 | }, 35 | "items" : [ 36 | {"key": "addDisabledTabArray[].name", "htmlClass": "nameField"}, 37 | {"key": "addDisabledTabArray[].nick", "htmlClass": "nickField"} 38 | ] 39 | } 40 | ] 41 | } 42 | ], 43 | "model": {} 44 | } 45 | -------------------------------------------------------------------------------- /examples/data/tabarray-remove-disabled.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": { 3 | "type": "object", 4 | "title": "Tab Array: Remove Disabled", 5 | "properties": { 6 | "removeDisabledTabArray": { 7 | "type": "array", 8 | "items": { 9 | "type": "object", 10 | "properties": { 11 | "name": { "type": "string" }, 12 | "nick": { "type": "string" } 13 | } 14 | } 15 | } 16 | } 17 | }, 18 | "form": [ 19 | { 20 | "type": "section", 21 | "htmlCss": "row", 22 | "items": [ 23 | { 24 | "type": "help", 25 | "helpvalue": "

Tab array with remove button hidden

" 26 | }, 27 | { 28 | "key": "removeDisabledTabArray", 29 | "type": "tabarray", 30 | "remove": null, 31 | "title": "My name is: {{ value.name }}", 32 | "sortOptions": { 33 | "disabled": true 34 | }, 35 | "items" : [ 36 | {"key": "removeDisabledTabArray[].name", "htmlClass": "nameField"}, 37 | {"key": "removeDisabledTabArray[].nick", "htmlClass": "nickField"} 38 | ] 39 | } 40 | ] 41 | } 42 | ], 43 | "model": {} 44 | } 45 | -------------------------------------------------------------------------------- /src/bootstrap/radio-buttons.html: -------------------------------------------------------------------------------- 1 |
7 |
8 | 9 |
10 |
11 | 25 |
26 |
27 |
28 | -------------------------------------------------------------------------------- /gulp/tasks/protractor.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | 3 | // The protractor task 4 | var protractor = require('gulp-protractor'); 5 | 6 | // Start a standalone server 7 | var webdriver_standalone = protractor.webdriver_standalone; 8 | 9 | // Download and update the selenium driver 10 | var webdriver_update = protractor.webdriver_update; 11 | 12 | // Downloads the selenium webdriver 13 | gulp.task('webdriver-update', webdriver_update); 14 | 15 | // Start the standalone selenium server 16 | // NOTE: This is not needed if you reference the 17 | // seleniumServerJar in your protractor.conf.js 18 | gulp.task('webdriver-standalone', webdriver_standalone); 19 | 20 | 21 | // Setting up the test task 22 | gulp.task('protractor', ['webdriver-update'], function(cb) { 23 | gulp.src(['test/protractor/specs/**/*.js']).pipe(protractor.protractor({ 24 | configFile: 'test/protractor/conf.js', 25 | })).on('error', function(e) { 26 | console.log(e); 27 | }).on('end', cb); 28 | }); 29 | 30 | ['validation-messages', 'custom-validation', 'tabarray'].forEach(function(name) { 31 | gulp.task('protractor:' + name, ['webdriver-update'], function(cb) { 32 | gulp.src(['test/protractor/specs/' + name + '.js']).pipe(protractor.protractor({ 33 | configFile: 'test/protractor/conf.js', 34 | })).on('error', function(e) { 35 | console.log(e); 36 | }).on('end', cb); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /examples/data/grid.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": { 3 | "type": "object", 4 | "title": "Comment", 5 | "properties": { 6 | "name": { 7 | "title": "Name", 8 | "type": "string" 9 | }, 10 | "email": { 11 | "title": "Email", 12 | "type": "string", 13 | "pattern": "^\\S+@\\S+$", 14 | "description": "Email will be used for evil." 15 | }, 16 | "comment": { 17 | "title": "Comment", 18 | "type": "string", 19 | "maxLength": 20, 20 | "validationMessage": "Don't be greedy!" 21 | } 22 | }, 23 | "required": ["name","email","comment"] 24 | }, 25 | "form": [ 26 | { 27 | "type": "help", 28 | "helpvalue": "
Grid it up with bootstrap
" 29 | }, 30 | { 31 | "type": "section", 32 | "htmlClass": "row", 33 | "items": [ 34 | { 35 | "type": "section", 36 | "htmlClass": "col-xs-6", 37 | "items": ["name"] 38 | }, 39 | { 40 | "type": "section", 41 | "htmlClass": "col-xs-6", 42 | "items": ["email"] 43 | } 44 | ] 45 | }, 46 | { 47 | "key": "comment", 48 | "type": "textarea", 49 | "placeholder": "Make a comment" 50 | }, 51 | { 52 | "type": "submit", 53 | "style": "btn-info", 54 | "title": "OK" 55 | } 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /examples/data/complex-keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": { 3 | "type": "object", 4 | "title": "Complex Key Support", 5 | "properties": { 6 | "a[\"b\"].c": { 7 | "type": "string" 8 | }, 9 | "simple": { 10 | "type": "object", 11 | "properties": { 12 | "prøp": { 13 | "title": "UTF8 in both dot and bracket notation", 14 | "type": "string" 15 | } 16 | } 17 | }, 18 | "array-key": { 19 | "type": "array", 20 | "items": { 21 | "type": "object", 22 | "properties": { 23 | "a'rr[\"l": { 24 | "title": "Control Characters", 25 | "type": "string" 26 | }, 27 | "˙∆∂∞˚¬": { 28 | "type": "string" 29 | } 30 | }, 31 | "required": [ 32 | "a'rr[\"l", 33 | "˙∆∂∞˚¬" 34 | ] 35 | } 36 | } 37 | } 38 | }, 39 | "form": [ 40 | { 41 | "type": "help", 42 | "helpvalue": "Complex keys are only supported with AngularJS version 1.3.x, see known limitations in the docs." 43 | }, 44 | "['a[\"b\"].c']", 45 | { 46 | "key": "array-key", 47 | "items": [ 48 | "['array-key'][]['a'rr[\"l']", 49 | { 50 | "key": "['array-key'][]['˙∆∂∞˚¬']", 51 | "title": "Unicode Characters" 52 | } 53 | ] 54 | }, 55 | { 56 | "key": "simple", 57 | "items": [ 58 | "simple.prøp" 59 | ] 60 | } 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /examples/data/tabarray.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": { 3 | "type": "object", 4 | "title": "Comment", 5 | "properties": { 6 | "comments": { 7 | "type": "array", 8 | "minItems": 2, 9 | "items": { 10 | "type": "object", 11 | "properties": { 12 | "name": { 13 | "title": "Name", 14 | "type": "string" 15 | }, 16 | "email": { 17 | "title": "Email", 18 | "type": "string", 19 | "pattern": "^\\S+@\\S+$", 20 | "description": "Email will be used for evil." 21 | }, 22 | "comment": { 23 | "title": "Comment", 24 | "type": "string", 25 | "maxLength": 20, 26 | "validationMessage": "Don't be greedy!" 27 | } 28 | }, 29 | "required": ["name","email","comment"] 30 | } 31 | } 32 | } 33 | }, 34 | "form": [ 35 | { 36 | "type": "help", 37 | "helpvalue": "

Tabbed Array Example

Tab arrays can have tabs to the left, top or right.

" 38 | }, 39 | { 40 | "key": "comments", 41 | "type": "tabarray", 42 | "add": "New", 43 | "remove": "Delete", 44 | "style": { 45 | "remove": "btn-danger" 46 | }, 47 | "title": "{{ value.name || 'Tab '+$index }}", 48 | "items": [ 49 | "comments[].name", 50 | "comments[].email", 51 | { 52 | "key": "comments[].comment", 53 | "type": "textarea" 54 | } 55 | ] 56 | }, 57 | { 58 | "type": "submit", 59 | "style": "btn-default", 60 | "title": "OK" 61 | } 62 | ] 63 | } 64 | -------------------------------------------------------------------------------- /src/bootstrap/array.html: -------------------------------------------------------------------------------- 1 |
4 | 5 |
    6 |
  1. 9 | 16 |
    17 |
  2. 18 |
19 |
20 |
23 | 24 | 32 |
33 |
34 | -------------------------------------------------------------------------------- /src/bootstrap/textarea.html: -------------------------------------------------------------------------------- 1 |
8 | 9 | 10 | 19 | 20 |
22 | 25 | 33 | 36 |
37 | 38 | 39 |
40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-schema-form-bootstrap", 3 | "version": "1.0.0-alpha.5", 4 | "description": "Bootstrap 3 decorator for Angular Schema Form", 5 | "main": "dist/angular-schema-form-bootstrap.js", 6 | "scripts": { 7 | "build": "webpack", 8 | "dist": "webpack --config webpack.config.dist.js", 9 | "watch": "webpack --watch", 10 | "test": "echo \"Error: test not currently available\" && exit 1", 11 | "test-me": "karma start --log-level debug" 12 | }, 13 | "keywords": [ 14 | "angular-schema-form-decorator" 15 | ], 16 | "author": "json-schema-form", 17 | "contributors": [ 18 | "David Jensen (https://github.com/davidlgj)", 19 | "Marcel J Bennett (https://github.com/Anthropic)" 20 | ], 21 | "license": "MIT", 22 | "dependencies": { 23 | "angular": ">= 1.2", 24 | "angular-messages": "^1.5.0", 25 | "angular-sanitize": ">= 1.2", 26 | "angular-schema-form": "^1.0.0-alpha.4", 27 | "tv4": "~1.0.15" 28 | }, 29 | "devDependencies": { 30 | "babel": "^6.5.2", 31 | "babel-core": "^6.17.0", 32 | "babel-loader": "^6.2.5", 33 | "babel-polyfill": "^6.16.0", 34 | "babel-preset-es2015": "^6.16.0", 35 | "chai": "^3.5.0", 36 | "coveralls": "^2.11.0", 37 | "html": "^1.0.0", 38 | "html-loader": "^0.4.4", 39 | "html-webpack-plugin": "^2.25.0", 40 | "karma": "^0.13.22", 41 | "karma-chai-sinon": "^0.1.5", 42 | "karma-coverage": "^1.0.0", 43 | "karma-growler-reporter": "0.0.1", 44 | "karma-mocha": "^1.0.1", 45 | "karma-phantomjs-launcher": "^0.1.4", 46 | "karma-webpack": "^1.7.0", 47 | "mocha": "^2.5.3", 48 | "mocha-lcov-reporter": "0.0.1", 49 | "ngtemplate-loader": "^1.3.1", 50 | "protractor": "^2.5.1", 51 | "sinon": "^1.17.4", 52 | "sinon-chai": "^2.8.0", 53 | "streamqueue": "^0.1.3", 54 | "uglify-js": "github:mishoo/UglifyJS2#harmony", 55 | "webpack": "^2.1.0-beta.27", 56 | "webpack-dev-server": "^2.1.0-beta.12" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /* global __dirname */ 2 | /** 3 | * NOTE in order to build with angular-schema-form you must 4 | * have it cloned as a sibling directory to this one or npm 5 | * installed with the version you wish to build with. 6 | */ 7 | const webpack = require('webpack'); 8 | const path = require('path'); 9 | const package = require('./package.json'); 10 | const buildDate = new Date(); 11 | console.log('Angular Schema Form Bootstrap v' + package.version); 12 | const plugins = [ 13 | new webpack.BannerPlugin( 14 | 'angular-schema-form-bootstrap\n' + 15 | '@version ' + package.version + '\n' + 16 | '@date ' + buildDate.toUTCString() + '\n' + 17 | '@link https://github.com/json-schema-form/angular-schema-form-bootstrap\n' + 18 | '@license MIT\n' + 19 | 'Copyright (c) 2014-' + buildDate.getFullYear() + ' JSON Schema Form'), 20 | /* Minification only occurs if the output is named .min */ 21 | new webpack.optimize.UglifyJsPlugin( 22 | { 23 | include: /\.min\.js$/, 24 | minimize: true 25 | }) 26 | ]; 27 | 28 | module.exports = { 29 | entry: { 30 | 'angular-schema-form-bootstrap': [ path.join(__dirname, 'src', 'module') ], 31 | 'angular-schema-form-bootstrap-bundled': [ 'angular-schema-form', path.join(__dirname, 'src', 'module') ], 32 | }, 33 | output: { 34 | path: path.join(__dirname, 'dist'), 35 | filename: '[name].js', 36 | sourceMapFilename: '[name].map' 37 | }, 38 | resolve: { 39 | modules: [ 40 | path.join(__dirname, "src"), 41 | path.join(__dirname, "src", "bootstrap"), 42 | path.join(__dirname, "..", "angular-schema-form", "dist"), 43 | 'node_modules', 44 | ], 45 | extensions: [ '.js', '.html' ] 46 | }, 47 | module: { 48 | rules: [ 49 | { 50 | test: /\.js$/, 51 | use: [{ 52 | loader: 'babel-loader', 53 | options: { 54 | presets: [ 55 | [ "es2015", { "modules": false }] 56 | ] 57 | } 58 | }], 59 | exclude: /(node_modules|angular-schema-form)/ 60 | }, 61 | { 62 | test: /\.html$/, 63 | use: [{ 64 | loader: 'ngtemplate-loader', 65 | options: { 66 | relativeTo: path.join(__dirname, 'src') 67 | } 68 | }, 'html-loader'], 69 | exclude: /(index)/ 70 | } 71 | ] 72 | }, 73 | externals: { 74 | 'angular': 'var angular', 75 | 'tv4': 'var tv4', 76 | 'bundle!angular-schema-form': 'commonjs angular-schema-form' 77 | }, 78 | plugins: plugins 79 | }; 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Angular Bootstrap Decorator 2 | ========================== 3 | 4 | For https://github.com/json-schema-form/angular-schema-form 5 | 6 | This is the new Bootstrap Decorator! That means a Bootstrap 3 frontend for the Angular Schema Form 7 | project. The former Bootstrap decorator used to be included in the main repo, but has now moved 8 | here. 9 | 10 | The big difference is that it now uses new builder methods, for more info on the builder see 11 | [our blog](https://medium.com/@SchemaFormIO/the-new-builder-pt-1-61fadde3c678). 12 | 13 | The biggest change for users is that the form no longer contains any `` tags 14 | since they are no longer needed. 15 | 16 | Install 17 | ------- 18 | ```sh 19 | npm install angular-schema-form-bootstrap 20 | ``` 21 | **note** we do not recommend using bower as even the bower team recommend using yarn and webpack now. 22 | 23 | The package.json 'main' script is this library alone and unminified so that minification can be handled by webpack or another script bundler. 24 | 25 | **Note when using webpack angular-schema-form versions match this repo so ASF 1.0.0-alpha.4 works with Bootstrap 1.0.0-alpha.4**. 26 | 27 | If you are unsure, check the bundled version in this repo and see which versions are used as both repo now include a version header. 28 | 29 | Look for this: 30 | ```js 31 | /*! 32 | * angular-schema-form 33 | * @version 1.0.0-alpha.4 34 | * @date Mon, 17 Apr 2017 08:55:13 GMT 35 | * @link https://github.com/json-schema-form/angular-schema-form 36 | * @license MIT 37 | * Copyright (c) 2014-2017 JSON Schema Form 38 | */ 39 | ``` 40 | 41 | Old versions pre-alpha work with 0.8.13 or ASF, but the alphas should be more stable than those versions with more bugs fixed. 42 | 43 | If you include `angular-schema-form-bootstrap-bundled.min.js` you **DO NOT** need to include angular-schema-form, it is now **embedded** within the bundled above file. If you wish to include the files separately you can still use `angular-schema-form-bootstrap.js` or `angular-schema-form-bootstrap.min.js` 44 | 45 | Future 46 | ------ 47 | Using the new builder opens up for a lot of optimization. Primarily we can get rid of a lot of small 48 | watches by using build helpers. For instance, slapping on a `sf-changed` directive *only* if the 49 | form definition has an `onChange` option. 50 | 51 | Developer Install 52 | ----------------- 53 | ```sh 54 | bower install 55 | npm install 56 | ``` 57 | Then read package.json for the available scripts. 58 | **Note** templates are compiled so the templates script must be run after changes. 59 | -------------------------------------------------------------------------------- /test/protractor/specs/validation-messages.js: -------------------------------------------------------------------------------- 1 | /* global browser, it, describe, element, by */ 2 | 3 | describe('Schema Form validation messages', function() { 4 | 5 | describe('#string', function() { 6 | var URL = 'http://localhost:8080/examples/bootstrap-example.html#/86fb7505a8ab6a43bc70'; 7 | 8 | it('should not complain if it gets a normal string', function() { 9 | browser.get(URL); 10 | var input = element.all(by.css('form[name=ngform] input')).first(); 11 | input.sendKeys('string'); 12 | 13 | expect(input.getAttribute('value')).toEqual('string'); 14 | expect(input.evaluate('ngModel.$valid')).toEqual(true); 15 | 16 | }); 17 | 18 | 19 | var validationMessageTestBuider = function(nr, value, validationMessage) { 20 | it('should say "' + validationMessage + '" when input is ' + value, function() { 21 | browser.get(URL); 22 | var input = element.all(by.css('form[name=ngform] input')).get(nr); 23 | input.sendKeys(value); 24 | 25 | var message = element.all(by.css('form[name=ngform] div[sf-message]')).get(nr); 26 | expect(input.evaluate('ngModel.$valid')).toEqual(false); 27 | expect(message.getText()).toEqual(validationMessage); 28 | 29 | }); 30 | }; 31 | 32 | var stringTests = { 33 | 's': 'String is too short (1 chars), minimum 3', 34 | 'tooo long string': 'String is too long (11 chars), maximum 10', 35 | 'foo 66': 'String does not match pattern: ^[a-zA-Z ]+$' 36 | }; 37 | 38 | Object.keys(stringTests).forEach(function(value) { 39 | validationMessageTestBuider(0, value, stringTests[value]); 40 | }); 41 | 42 | 43 | var integerTests = { 44 | '3': '3 is less than the allowed minimum of 6', 45 | '66': '66 is greater than the allowed maximum of 50', 46 | '11': 'Value is not a multiple of 3', 47 | // Chrome no longer lets you input anything but numbers in a type="number" input. 48 | 'aaa': 'Required' //'Value is not a valid number' 49 | }; 50 | 51 | Object.keys(integerTests).forEach(function(value) { 52 | validationMessageTestBuider(1, value, integerTests[value]); 53 | }); 54 | 55 | 56 | it('should say "Required" when fields are required', function() { 57 | browser.get(URL); 58 | element.all(by.css('form[name=ngform]')).submit(); 59 | var input = element.all(by.css('form[name=ngform] input')).get(1); 60 | 61 | var message = element.all(by.css('form[name=ngform] div[sf-message]')).get(1); 62 | expect(input.evaluate('ngModel.$valid')).toEqual(false); 63 | expect(message.getText()).toEqual('Required'); 64 | 65 | }); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /src/bootstrap/default.html: -------------------------------------------------------------------------------- 1 |
8 | 9 | 10 | 23 | 24 |
26 | 29 | 41 | 42 | 45 |
46 | 47 | 51 | 52 | {{ hasSuccess() ? '(success)' : '(error)' }} 55 | 56 |
57 |
58 | -------------------------------------------------------------------------------- /examples/data/titlemaps.json: -------------------------------------------------------------------------------- 1 | { 2 | "model": { 3 | "select": "a", 4 | "array": ["b"] 5 | }, 6 | "schema": { 7 | "type": "object", 8 | "properties": { 9 | "select": { 10 | "title": "Select without titleMap", 11 | "type": "string", 12 | "enum": ["a","b","c"] 13 | }, 14 | "select2": { 15 | "title": "Select with titleMap (old style)", 16 | "type": "string", 17 | "enum": ["a","b","c"] 18 | }, 19 | "noenum": { "type": "string", "title": "No enum, but forms says it's a select" }, 20 | "array": { 21 | "title": "Array with enum defaults to 'checkboxes'", 22 | "type": "array", 23 | "items": { 24 | "type": "string", 25 | "enum": ["a","b","c"] 26 | } 27 | }, 28 | "array2": { 29 | "title": "Array with titleMap", 30 | "type": "array", 31 | "default": ["b","c"], 32 | "items": { 33 | "type": "string", 34 | "enum": ["a","b","c"] 35 | } 36 | }, 37 | "radios": { 38 | "title": "Basic radio button example", 39 | "type": "string", 40 | "enum": ["a","b","c"] 41 | }, 42 | "radiobuttons": { 43 | "title": "Radio buttons used to switch a boolean", 44 | "type": "boolean", 45 | "default": false 46 | } 47 | } 48 | }, 49 | "form": [ 50 | "select", 51 | { 52 | "key": "select2", 53 | "type": "select", 54 | "placeholder": "Please choose", 55 | "titleMap": { 56 | "a": "A", 57 | "b": "B", 58 | "c": "C" 59 | } 60 | }, 61 | { 62 | "key": "noenum", 63 | "type": "select", 64 | "placeholder": "Please choose", 65 | "titleMap": [ 66 | { "value":"a", "name": "A" }, 67 | { "value":"b", "name":"B" }, 68 | { "value":"c", "name":"C" } 69 | ] 70 | }, 71 | "array", 72 | { 73 | "key": "array2", 74 | "type": "checkboxes", 75 | "titleMap": [ 76 | { "value":"a", "name": "A" }, 77 | { "value":"b", "name":"B" }, 78 | { "value":"c", "name":"C" } 79 | ] 80 | }, 81 | { 82 | "key": "radios", 83 | "type": "radios", 84 | "titleMap": [ 85 | { "value":"c", "name": "C" }, 86 | { "value":"b", "name":"B" }, 87 | { "value":"a", "name":"A" } 88 | ] 89 | }, 90 | { 91 | "key":"radiobuttons", 92 | "type": "radiobuttons", 93 | "titleMap": [ 94 | {"value": false, "name": "No way"}, 95 | {"value": true, "name": "OK"} 96 | ] 97 | } 98 | ] 99 | } 100 | -------------------------------------------------------------------------------- /test/protractor/specs/custom-validation.js: -------------------------------------------------------------------------------- 1 | describe('Schema Form custom validators', function() { 2 | it('should have a form with content', function() { 3 | browser.get('http://localhost:8080/examples/custom-validators.html'); 4 | 5 | expect(element(by.css('form')).getInnerHtml()).not.toEqual(''); 6 | }); 7 | 8 | describe('#name', function() { 9 | it('should not complain if it gets a normal name', function() { 10 | browser.get('http://localhost:8080/examples/custom-validators.html'); 11 | var input = element.all(by.css('form input')).first(); 12 | input.sendKeys('Joe Schmoe'); 13 | 14 | expect(input.getAttribute('value')).toEqual('Joe Schmoe'); 15 | expect(input.evaluate('ngModel.$valid')).toEqual(true); 16 | 17 | }); 18 | 19 | it('should complain if it gets a "Bob" as a name', function() { 20 | browser.get('http://localhost:8080/examples/custom-validators.html'); 21 | var input = element.all(by.css('form input')).first(); 22 | input.sendKeys('Bob'); 23 | 24 | expect(input.getAttribute('value')).toEqual('Bob'); 25 | expect(input.evaluate('ngModel.$valid')).toEqual(false); 26 | }); 27 | }); 28 | 29 | describe('#email', function() { 30 | it('should not complain if it gets a normal email', function() { 31 | browser.get('http://localhost:8080/examples/custom-validators.html'); 32 | var input = element.all(by.css('form input')).get(1); 33 | input.sendKeys('foo@mailinator.com'); 34 | 35 | expect(input.getAttribute('value')).toEqual('foo@mailinator.com'); 36 | expect(input.evaluate('ngModel.$valid')).toEqual(true); 37 | 38 | }); 39 | 40 | it('should complain if it gets a my email', function() { 41 | browser.get('http://localhost:8080/examples/custom-validators.html'); 42 | var input = element.all(by.css('form input')).get(1); 43 | input.sendKeys('david.lgj@gmail.com'); 44 | 45 | expect(input.getAttribute('value')).toEqual('david.lgj@gmail.com'); 46 | expect(input.evaluate('ngModel.$valid')).toEqual(false); 47 | }); 48 | }); 49 | 50 | describe('#comment', function() { 51 | it('should not complain if it gets a normal email', function() { 52 | browser.get('http://localhost:8080/examples/custom-validators.html'); 53 | var input = element.all(by.css('form input')).get(1); 54 | input.sendKeys('foo@mailinator.com'); 55 | 56 | expect(input.getAttribute('value')).toEqual('foo@mailinator.com'); 57 | expect(input.evaluate('ngModel.$valid')).toEqual(true); 58 | 59 | }); 60 | 61 | it('should complain if it gets a my email', function() { 62 | browser.get('http://localhost:8080/examples/custom-validators.html'); 63 | var input = element.all(by.css('form input')).get(1); 64 | input.sendKeys('david.lgj@gmail.com'); 65 | 66 | expect(input.getAttribute('value')).toEqual('david.lgj@gmail.com'); 67 | expect(input.evaluate('ngModel.$valid')).toEqual(false); 68 | }); 69 | }); 70 | 71 | 72 | 73 | 74 | }); 75 | -------------------------------------------------------------------------------- /src/bootstrap/tabarray.html: -------------------------------------------------------------------------------- 1 |
6 | 27 | 28 |
29 |
30 |
35 | 36 |
37 | 38 | 46 |
47 |
50 |
51 |
52 |
53 | 54 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /examples/data/array.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": { 3 | "type": "object", 4 | "title": "Comment", 5 | "required": ["comments"], 6 | "properties": { 7 | "tags": { 8 | "type": "array", 9 | "maxItems": 4, 10 | "minItems": 2, 11 | "uniqueItems": true, 12 | "items": { 13 | "type": "string" 14 | } 15 | }, 16 | "comments": { 17 | "type": "array", 18 | "maxItems": 2, 19 | "items": { 20 | "type": "object", 21 | "properties": { 22 | "name": { 23 | "title": "Name", 24 | "type": "string" 25 | }, 26 | "email": { 27 | "title": "Email", 28 | "type": "string", 29 | "pattern": "^\\S+@\\S+$", 30 | "description": "Email will be used for evil." 31 | }, 32 | "spam": { 33 | "title": "Spam", 34 | "type": "boolean", 35 | "default": true 36 | }, 37 | "comment": { 38 | "title": "Comment", 39 | "type": "string", 40 | "maxLength": 20, 41 | "validationMessage": "Don't be greedy!" 42 | } 43 | }, 44 | "required": ["name","comment"] 45 | } 46 | }, 47 | "matrix": { 48 | "type": "array", 49 | "items": { 50 | "type": "array", 51 | "items": { 52 | "type": "string" 53 | } 54 | } 55 | }, 56 | "subs": { 57 | "type": "array", 58 | "items": { 59 | "type": "object", 60 | "properties": { 61 | "sub": { 62 | "type": "array", 63 | "items": { 64 | "type": "string" 65 | } 66 | } 67 | } 68 | } 69 | }, 70 | "triplesubs": { 71 | "type": "array", 72 | "items": { 73 | "type": "object", 74 | "properties": { 75 | "sub": { 76 | "type": "array", 77 | "items": { 78 | "type": "array", 79 | "items": { 80 | "type": "string" 81 | } 82 | } 83 | } 84 | } 85 | } 86 | } 87 | } 88 | }, 89 | "form": [ 90 | { 91 | "type": "help", 92 | "helpvalue": "

Array Example

Try adding a couple of forms, reorder by drag'n'drop.

" 93 | }, 94 | "tags", 95 | { 96 | "key": "comments", 97 | "add": "New", 98 | "style": { 99 | "add": "btn-success" 100 | }, 101 | "items": [ 102 | "comments[].name", 103 | "comments[].email", 104 | { 105 | "key": "comments[].spam", 106 | "type": "checkbox", 107 | "title": "Yes I want spam.", 108 | "condition": "model.comments[arrayIndex].email" 109 | }, 110 | { 111 | "key": "comments[].comment", 112 | "type": "textarea" 113 | } 114 | ] 115 | }, 116 | "matrix", 117 | { 118 | "type": "array", 119 | "key": "subs", 120 | "items": [ 121 | "subs[].sub" 122 | ] 123 | }, 124 | "triplesubs", 125 | { 126 | "type": "submit", 127 | "style": "btn-info", 128 | "title": "OK" 129 | } 130 | ], 131 | "model": { 132 | "tags": ["one", "two"], 133 | "comments": [{}], 134 | "matrix": [["one","two"],["three"]], 135 | "subs": [{ "sub":["yes!"] }] 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /examples/custom-validators.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Custom validators, async validators etc 5 | 6 | 7 | 8 | 9 | 10 |

Demo of custom validators, async validators and parsers

11 | Check the source 12 |
13 |
14 |
15 | The form is pristinedirty 16 | and validinvalid. 17 |
18 |
{{prettyModel}}
19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /test/protractor/specs/tabarray.js: -------------------------------------------------------------------------------- 1 | describe('tab array', function () { 2 | it('form should exist', function () { 3 | browser.get('http://localhost:8080/examples/bootstrap-example.html'); 4 | 5 | element(by.css('#selectTest')).all(by.cssContainingText('option', 'Tab Array')).first().click().then(function() { 6 | expect(element(by.css('form.ng-valid-schema-form')).getInnerHtml()).not.toEqual(''); 7 | }); 8 | }); 9 | 10 | it('add link should be hidden', function () { 11 | browser.get('http://localhost:8080/examples/bootstrap-example.html'); 12 | 13 | /* select the add disabled example */ 14 | element(by.css('#selectTest')).element(by.cssContainingText('option', 'Tab Array: Add Disabled')).click().then(function() { 15 | 16 | /* Add link should not be displayed */ 17 | var tabs = element.all(by.css('.nav-tabs li')); 18 | expect(tabs.get(0).isDisplayed()).toBeTruthy(); 19 | expect(tabs.get(1).isDisplayed()).toBeFalsy(); 20 | 21 | var addLink = element.all(by.partialLinkText('Add')); 22 | expect(addLink.count()).toBe(0); 23 | 24 | /*** control tests ***/ 25 | /* Remove button should be displayed */ 26 | var removeButton = element.all(by.partialButtonText('Remove')).get(0); 27 | expect(removeButton.isDisplayed()).toBeTruthy(); 28 | }); 29 | }); 30 | 31 | it('remove button should be hidden', function () { 32 | browser.get('http://localhost:8080/examples/bootstrap-example.html'); 33 | 34 | /* select the remove disabled example */ 35 | element(by.css('#selectTest')).element(by.cssContainingText('option', 'Tab Array: Remove Disabled')).click().then(function() { 36 | 37 | /* Remove button should not be displayed */ 38 | var removeButton = element.all(by.partialButtonText('Remove')).get(0); 39 | expect(removeButton.isDisplayed()).toBeFalsy(); 40 | 41 | /*** control tests ***/ 42 | /* Add link should not be displayed */ 43 | var tabs = element.all(by.css('.nav-tabs li')); 44 | expect(tabs.get(0).isDisplayed()).toBeTruthy(); 45 | expect(tabs.get(1).isDisplayed()).toBeTruthy(); 46 | 47 | var addLink = element.all(by.partialLinkText('Add')); 48 | expect(addLink.count()).toBe(1); 49 | }); 50 | }); 51 | 52 | it('should be able order elements in array by dragging the tabs', function () { 53 | browser.get('http://localhost:8080/examples/bootstrap-example.html'); 54 | 55 | function checkDragDrop(i) { 56 | browser.driver.wait(protractor.until.elementLocated(by.xpath("//ol/li[1]/a[text()='My name is: Name " + (i + 1) +"']")), 10000); 57 | expect(element.all(by.css('.nav-tabs li a')).get(0).getText()).toBe('My name is: Name ' + (i + 1)); 58 | } 59 | 60 | function populateTab(i) { 61 | browser.driver.wait(protractor.until.elementLocated(by.css('.tab-pane.active.index' + i)), 5000); 62 | 63 | browser.driver.wait(protractor.until.elementLocated(by.css('.tab-pane.index' + i + ' div.nickField > input')), 5000); 64 | input = element.all(by.css('.tab-pane.index' + i + ' div.nickField > input')).first(); 65 | input.sendKeys('Nickname ' + i); 66 | 67 | browser.driver.wait(protractor.until.elementLocated(by.css('.tab-pane.index' + i + ' div.nameField > input')), 5000); 68 | input = element.all(by.css('.tab-pane.index' + i + ' div.nameField > input')).first(); 69 | input.sendKeys('Name ' + i); 70 | 71 | browser.driver.wait(protractor.until.elementLocated(by.linkText('My name is: Name ' + i)), 10000); 72 | } 73 | 74 | /* select the sortable example */ 75 | element(by.css('#selectTest')).element(by.cssContainingText('option', 'Tab Array: Sortable')).click().then(function() { 76 | 77 | var i; 78 | var elementsToAdd = 9; 79 | 80 | /* the array starts with 1 element, populate the first element */ 81 | populateTab(0); 82 | 83 | /* add elements and populate */ 84 | for (i = 1; i <= elementsToAdd; i++) { 85 | var tabLink = element.all(by.css('.glyphicon-plus')); 86 | tabLink.click().then(populateTab(i)); 87 | } 88 | 89 | /* continue when all tabs have been populated*/ 90 | browser.driver.wait(protractor.until.elementLocated(by.linkText('My name is: Name ' + elementsToAdd)), 10000); 91 | 92 | /* check the number of tabs */ 93 | var tabs = element.all(by.css('.nav-tabs li')); 94 | expect(tabs.count()).toBe(elementsToAdd + 2); //Extra 1 for the "+ Add" link 95 | 96 | /* drag the tabs into reverse order (descending) */ 97 | for (i = 0; i < elementsToAdd; i++) { 98 | var draggable_element = element.all(by.css('.nav-tabs li')).get(0); 99 | var target_element = element.all(by.css('.nav-tabs li')).get(elementsToAdd - i); 100 | expect(draggable_element.isPresent()).toEqual(true); 101 | expect(target_element.isPresent()).toEqual(true); 102 | browser.actions().dragAndDrop(draggable_element, target_element).perform().then(checkDragDrop(i)); 103 | } 104 | 105 | /* final check of the reverse ordered tabs */ 106 | for (i = 0; i <= elementsToAdd; i++) { 107 | expect(element.all(by.css('.nav-tabs li a')).get(i).getText()).toBe('My name is: Name ' + (elementsToAdd - i)); 108 | } 109 | }); 110 | }); 111 | }); 112 | -------------------------------------------------------------------------------- /src/bootstrap-decorator.js: -------------------------------------------------------------------------------- 1 | // ngtemplate-loader embeds the html on build 2 | import actionsTemplate from './bootstrap/actions.html'; 3 | import arrayTemplate from './bootstrap/array.html'; 4 | import checkboxTemplate from './bootstrap/checkbox.html'; 5 | import checkboxesTemplate from './bootstrap/checkboxes.html'; 6 | import defaultTemplate from './bootstrap/default.html'; 7 | import fieldsetTemplate from './bootstrap/fieldset.html'; 8 | import helpTemplate from './bootstrap/help.html'; 9 | import radiobuttonsTemplate from './bootstrap/radio-buttons.html'; 10 | import radiosTemplate from './bootstrap/radios.html'; 11 | import radiosInlineTemplate from './bootstrap/radios-inline.html'; 12 | import sectionTemplate from './bootstrap/section.html'; 13 | import selectTemplate from './bootstrap/select.html'; 14 | import submitTemplate from './bootstrap/submit.html'; 15 | import tabarrayTemplate from './bootstrap/tabarray.html'; 16 | import tabsTemplate from './bootstrap/tabs.html'; 17 | import textareaTemplate from './bootstrap/textarea.html'; 18 | 19 | angular 20 | .module('schemaForm') 21 | .config(bootstrapDecoratorConfig); 22 | 23 | bootstrapDecoratorConfig.$inject = [ 24 | 'schemaFormProvider', 'schemaFormDecoratorsProvider', 'sfBuilderProvider', 'sfPathProvider', '$injector' 25 | ]; 26 | 27 | function bootstrapDecoratorConfig( 28 | schemaFormProvider, decoratorsProvider, sfBuilderProvider, sfPathProvider, $injector) { 29 | var base = 'decorators/bootstrap/'; 30 | 31 | var simpleTransclusion = sfBuilderProvider.builders.simpleTransclusion; 32 | var ngModelOptions = sfBuilderProvider.builders.ngModelOptions; 33 | var ngModel = sfBuilderProvider.builders.ngModel; 34 | var sfField = sfBuilderProvider.builders.sfField; 35 | var condition = sfBuilderProvider.builders.condition; 36 | var array = sfBuilderProvider.builders.array; 37 | var numeric = sfBuilderProvider.builders.numeric; 38 | 39 | // Tabs is so bootstrap specific that it stays here. 40 | var tabs = function(args) { 41 | if (args.form.tabs && args.form.tabs.length > 0) { 42 | var tabContent = args.fieldFrag.querySelector('.tab-content'); 43 | 44 | args.form.tabs.forEach(function(tab, index) { 45 | var evalExpr = '(evalExpr(' + args.path + '.tabs[' + index + ']' + 46 | '.condition, { model: model, "arrayIndex": $index}))'; 47 | var div = document.createElement('div'); 48 | div.className = 'tab-pane'; 49 | div.setAttribute('ng-disabled', 'form.readonly'); 50 | div.setAttribute('ng-show', 'selected.tab === ' + index); 51 | div.setAttribute('ng-class', '{active: selected.tab === ' + index + '}'); 52 | if(!!tab.condition) { 53 | div.setAttribute('ng-if', evalExpr); 54 | }; 55 | 56 | var childFrag = args.build(tab.items, args.path + '.tabs[' + index + '].items', args.state); 57 | div.appendChild(childFrag); 58 | tabContent.appendChild(div); 59 | }); 60 | } 61 | }; 62 | 63 | var selectPlaceholder = function(args) { 64 | if (args.form.placeholder) { 65 | var selectBox = args.fieldFrag.querySelector('select'); 66 | var option = document.createElement('option'); 67 | option.setAttribute('value', ''); 68 | 69 | /* We only want the placeholder to show when we do not have a value on the model. 70 | * We make ngModel builder replace all so we can use $$value$$. 71 | */ 72 | option.setAttribute('sf-field-model', 'replaceAll'); 73 | 74 | /* https://github.com/angular/angular.js/issues/12190#issuecomment-115277040 75 | * angular > 1.4 does a emptyOption.attr('selected', true) 76 | * which does not like the ng-if comment. 77 | */ 78 | if (angular.version.major === 1 && angular.version.minor < 4) { 79 | option.setAttribute('ng-if', '$$value$$ === undefined'); 80 | } else { 81 | option.setAttribute('ng-show', '$$value$$ === undefined'); 82 | } 83 | 84 | option.textContent = args.form.placeholder; 85 | 86 | selectBox.appendChild(option); 87 | } 88 | }; 89 | 90 | var defaults = [sfField, ngModel, ngModelOptions, condition]; 91 | decoratorsProvider.defineDecorator('bootstrapDecorator', { 92 | actions: {template: actionsTemplate, builder: defaults}, 93 | array: {template: arrayTemplate, builder: [ sfField, ngModelOptions, ngModel, array, condition ]}, 94 | button: {template: submitTemplate, builder: defaults}, 95 | checkbox: {template: checkboxTemplate, builder: defaults}, 96 | checkboxes: {template: checkboxesTemplate, builder: [ sfField, ngModelOptions, ngModel, array, condition ]}, 97 | conditional: {template: sectionTemplate, builder: [ sfField, simpleTransclusion, condition ]}, 98 | 'default': {template: defaultTemplate, builder: defaults}, 99 | fieldset: {template: fieldsetTemplate, builder: [ sfField, simpleTransclusion, condition ]}, 100 | help: {template: helpTemplate, builder: defaults}, 101 | number: {template: defaultTemplate, builder: defaults.concat(numeric)}, 102 | password: {template: defaultTemplate, builder: defaults}, 103 | radios: {template: radiosTemplate, builder: defaults}, 104 | 'radios-inline': {template: radiosInlineTemplate, builder: defaults}, 105 | radiobuttons: {template: radiobuttonsTemplate, builder: defaults}, 106 | section: {template: sectionTemplate, builder: [ sfField, simpleTransclusion, condition ]}, 107 | select: {template: selectTemplate, builder: [ selectPlaceholder ].concat(defaults)}, 108 | submit: {template: submitTemplate, builder: defaults}, 109 | tabarray: {template: tabarrayTemplate, builder: [ sfField, ngModelOptions, ngModel, array, condition ]}, 110 | tabs: {template: tabsTemplate, builder: [ sfField, ngModelOptions, tabs, condition ]}, 111 | textarea: {template: textareaTemplate, builder: defaults}, 112 | }, []); 113 | }; 114 | -------------------------------------------------------------------------------- /examples/data/array-deep.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": { 3 | "type": "object", 4 | "properties": { 5 | "transportCategory": { 6 | "type": "array", 7 | "items": { 8 | "type": "object", 9 | "properties": { 10 | "mode": { "type": "string", "enum": ["Car", "Motorbike", "Horse"] }, 11 | "transportOption": { 12 | "type": "array", 13 | "items": { 14 | "type": "object", 15 | "properties": { 16 | "name": { "type": "string" }, 17 | "numberOfWheels": { "type": "number" }, 18 | "forSale": { "type": "string", "enum": ["yes", "no"] }, 19 | "price": { "type": "number" }, 20 | "history": { 21 | "type": "object", 22 | "properties": { 23 | "historyKnown": { "type": "string", "enum": ["yes", "no"] }, 24 | "previousOwners": { 25 | "type": "array", 26 | "items": { 27 | "type": "object", 28 | "properties": { 29 | "ownerName": { "type": "string" }, 30 | "purchaseDate": { "type": "string" }, 31 | "logBookProvided": { "type": "string", "enum": ["yes", "no"] }, 32 | "logBookEntry": { 33 | "type": "array", 34 | "items": { 35 | "type": "object", 36 | "properties": { 37 | "entryId": { "type": "number" }, 38 | "entryDate": { "type": "string" }, 39 | "entryNote": { "type": "string" } 40 | } 41 | } 42 | } 43 | } 44 | } 45 | } 46 | } 47 | } 48 | } 49 | } 50 | } 51 | } 52 | } 53 | } 54 | } 55 | }, 56 | "form": [ 57 | { 58 | "key": "transportCategory", 59 | "type": "tabarray", 60 | "add": "New", 61 | "style": { 62 | "add": "btn-success" 63 | }, 64 | "title": "{{ 'Tab '+$index + ' ' + value.mode + ' ' || 'Tab' + $index }}", 65 | "items": [ 66 | "transportCategory[].mode", 67 | { 68 | "key": "transportCategory[].transportOption", 69 | "items": [ 70 | "transportCategory[].transportOption[].name", 71 | { 72 | "key": "transportCategory[].transportOption[].numberOfWheels", 73 | "condition": "model.transportCategory[arrayIndices[0]].mode != 'Horse'" 74 | }, 75 | "transportCategory[].transportOption[].forSale", 76 | { 77 | "key": "transportCategory[].transportOption[].price", 78 | "condition": "model.transportCategory[arrayIndices[0]].transportOption[arrayIndices[1]].forSale == 'yes'" 79 | }, 80 | "transportCategory[].transportOption[].history.historyKnown", 81 | { 82 | "key": "transportCategory[].transportOption[].history.previousOwners", 83 | "condition": "model.transportCategory[arrayIndices[0]].transportOption[arrayIndices[1]].history.historyKnown == 'yes'", 84 | "items": [ 85 | "transportCategory[].transportOption[].history.previousOwners[].ownerName", 86 | { 87 | "key": "transportCategory[].transportOption[].history.previousOwners[].purchaseDate", 88 | "condition": "model.transportCategory[arrayIndices[0]].transportOption[arrayIndices[1]].history.previousOwners[arrayIndices[2]].ownerName.length > 2" 89 | }, 90 | { 91 | "key": "transportCategory[].transportOption[].history.previousOwners[].logBookProvided", 92 | "condition": "model.transportCategory[arrayIndices[0]].mode != 'Horse' && model.transportCategory[arrayIndices[0]].transportOption[arrayIndices[1]].history.previousOwners[arrayIndices[2]].ownerName.length > 2" 93 | }, 94 | { 95 | "key": "transportCategory[].transportOption[].history.previousOwners[].logBookEntry", 96 | "condition": "model.transportCategory[arrayIndices[0]].transportOption[arrayIndices[1]].history.previousOwners[arrayIndices[2]].logBookProvided == 'yes'", 97 | "items": [ 98 | "transportCategory[].transportOption[].history.previousOwners[].logBookEntry[].entryId", 99 | "transportCategory[].transportOption[].history.previousOwners[].logBookEntry[].entryDate", 100 | { 101 | "key": "transportCategory[].transportOption[].history.previousOwners[].logBookEntry[].entryNote", 102 | "condition": "model.transportCategory[arrayIndices[0]].transportOption[arrayIndices[1]].history.previousOwners[arrayIndices[2]].logBookEntry[arrayIndices[3]].entryDate.length > 2" 103 | } 104 | ] 105 | } 106 | ] 107 | } 108 | ] 109 | } 110 | ] 111 | } 112 | ], 113 | "model": { 114 | "transportCategory": [ 115 | { 116 | "mode": "Car", 117 | "transportOption": [ 118 | { 119 | "name": "Bertie", 120 | "forSale": "yes", 121 | "price": 100, 122 | "history": { 123 | "historyKnown": "no" 124 | } 125 | }, 126 | { 127 | "name": "Lightning McQueen", 128 | "forSale": "no", 129 | "history": { 130 | "historyKnown": "yes", 131 | "previousOwners": [ 132 | { 133 | "ownerName": "" 134 | }, 135 | { 136 | "ownerName": "Arlo", 137 | "logBookProvided": "yes", 138 | "logBookEntry": [ 139 | { 140 | "entryId": 2, 141 | "entryDate": "2015-06-23" 142 | }, 143 | { 144 | "entryId": 4 145 | } 146 | ] 147 | } 148 | ] 149 | } 150 | } 151 | ] 152 | }, 153 | { 154 | "mode": "Horse", 155 | "transportOption": [ 156 | { 157 | "name": "Phar Lap", 158 | "forSale": "no" 159 | }, 160 | { 161 | "name": "Greyhound", 162 | "forSale": "yes", 163 | "price": 1000, 164 | "history": { 165 | "historyKnown": "yes", 166 | "previousOwners": [ 167 | { 168 | "ownerName": "Tom" 169 | } 170 | ] 171 | } 172 | } 173 | ] 174 | } 175 | ] 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /examples/data/sink.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": { 3 | "type": "object", 4 | "required": [ 5 | "name", 6 | "shoesizeLeft" 7 | ], 8 | "properties": { 9 | "name": { 10 | "title": "Name", 11 | "description": "Gimme yea name lad", 12 | "type": "string", 13 | "pattern": "^[^/]*$", 14 | "minLength": 2 15 | }, 16 | "invitation": { 17 | "type": "string", 18 | "format": "html", 19 | "title": "Invitation Design", 20 | "description": "Design the invitation in full technicolor HTML" 21 | }, 22 | "favorite": { 23 | "title": "Favorite", 24 | "type": "string", 25 | "enum": [ 26 | "undefined", 27 | "null", 28 | "NaN" 29 | ] 30 | }, 31 | "shoesizeLeft": { 32 | "title": "Shoe size (left)", 33 | "default": 42, 34 | "type": "number" 35 | }, 36 | "shoesizeRight": { 37 | "title": "Shoe size (right)", 38 | "default": 42, 39 | "type": "number" 40 | }, 41 | "attributes": { 42 | "type": "object", 43 | "title": "Attributes", 44 | "required": [ 45 | "eyecolor" 46 | ], 47 | "properties": { 48 | "eyecolor": { 49 | "type": "string", 50 | "format": "color", 51 | "title": "Eye color", 52 | "default": "pink" 53 | }, 54 | "haircolor": { 55 | "type": "string", 56 | "title": "Hair color" 57 | }, 58 | "shoulders": { 59 | "type": "object", 60 | "title": "Shoulders", 61 | "properties": { 62 | "left": { 63 | "type": "string", 64 | "title": "Left" 65 | }, 66 | "right": { 67 | "type": "string", 68 | "title": "Right" 69 | } 70 | } 71 | } 72 | } 73 | }, 74 | "things": { 75 | "type": "array", 76 | "title": "I like...", 77 | "items": { 78 | "type": "string", 79 | "enum": [ 80 | "clowns", 81 | "compiling", 82 | "sleeping" 83 | ] 84 | } 85 | }, 86 | "dislike": { 87 | "type": "array", 88 | "title": "I dislike...", 89 | "items": { 90 | "type": "string", 91 | "title": "I hate" 92 | } 93 | }, 94 | "soul": { 95 | "title": "Terms Of Service", 96 | "description": "I agree to sell my undying soul", 97 | "type": "boolean", 98 | "default": true 99 | }, 100 | "soulserial": { 101 | "title": "Soul Serial No", 102 | "type": "string" 103 | }, 104 | "date": { 105 | "title": "Date of party", 106 | "type": "string", 107 | "format": "date" 108 | }, 109 | "radio": { 110 | "title": "Radio type", 111 | "type": "string", 112 | "enum": [ 113 | "Transistor", 114 | "Tube" 115 | ] 116 | }, 117 | "radio2": { 118 | "title": "My Second Radio", 119 | "type": "string", 120 | "enum": [ 121 | "Transistor", 122 | "Tube" 123 | ] 124 | }, 125 | "radiobuttons": { 126 | "type": "string", 127 | "enum": [ 128 | "Select me!", 129 | "No me!" 130 | ] 131 | } 132 | } 133 | }, 134 | "form": [ 135 | { 136 | "type": "fieldset", 137 | "title": "Stuff", 138 | "items": [ 139 | { 140 | "type": "tabs", 141 | "tabs": [ 142 | { 143 | "title": "Simple stuff", 144 | "items": [ 145 | { 146 | "key": "name", 147 | "placeholder": "Check the console", 148 | "onChange": "log(modelValue)", 149 | "feedback": "{'glyphicon': true, 'glyphicon-ok': hasSuccess(), 'glyphicon-star': !hasSuccess() }" 150 | }, 151 | { 152 | "key": "favorite", 153 | "feedback": false 154 | } 155 | ] 156 | }, 157 | { 158 | "title": "More stuff", 159 | "items": [ 160 | "attributes.eyecolor", 161 | "attributes.haircolor", 162 | { 163 | "key": "attributes.shoulders.left", 164 | "title": "Left shoulder", 165 | "description": "This value is copied to attributes.shoulders.right in the model", 166 | "copyValueTo": ["attributes.shoulders.right"] 167 | }, 168 | { 169 | "key": "shoesizeLeft", 170 | "feedback": false, 171 | "copyValueTo":["shoesizeRight"] 172 | }, 173 | { 174 | "key": "shoesizeRight" 175 | }, 176 | { 177 | "key": "invitation", 178 | "tinymceOptions": { 179 | "toolbar": [ 180 | "undo redo| styleselect | bold italic | link image", 181 | "alignleft aligncenter alignright" 182 | ] 183 | } 184 | }, 185 | "things", 186 | "dislike" 187 | ] 188 | } 189 | ] 190 | } 191 | ] 192 | }, 193 | { 194 | "type": "help", 195 | "helpvalue": "
" 196 | }, 197 | "soul", 198 | { 199 | "type": "conditional", 200 | "condition": "modelData.soul", 201 | "items": [ 202 | { 203 | "key": "soulserial", 204 | "placeholder": "ex. 666" 205 | } 206 | ] 207 | }, 208 | { 209 | "key": "date", 210 | "minDate": "2014-06-20" 211 | }, 212 | { 213 | "key": "radio", 214 | "type": "radios", 215 | "titleMap": [ 216 | { 217 | "value": "Transistor", 218 | "name": "Transistor
Not the tube kind." 219 | }, 220 | { 221 | "value": "Tube", 222 | "name": "Tube
The tube kind." 223 | } 224 | ] 225 | }, 226 | { 227 | "key": "radio2", 228 | "type": "radios-inline", 229 | "titleMap": [ 230 | { 231 | "value": "Transistor", 232 | "name": "Transistor
Not the tube kind." 233 | }, 234 | { 235 | "value": "Tube", 236 | "name": "Tube
The tube kind." 237 | } 238 | ] 239 | }, 240 | { 241 | "key": "radiobuttons", 242 | "style": { 243 | "selected": "btn-success", 244 | "unselected": "btn-default" 245 | }, 246 | "type": "radiobuttons", 247 | "notitle": true 248 | }, 249 | { 250 | "type": "actions", 251 | "items": [ 252 | { 253 | "type": "submit", 254 | "style": "btn-info", 255 | "title": "Do It!" 256 | }, 257 | { 258 | "type": "button", 259 | "style": "btn-danger", 260 | "title": "Noooooooooooo", 261 | "onClick": "sayNo()" 262 | } 263 | ] 264 | } 265 | ] 266 | } 267 | -------------------------------------------------------------------------------- /examples/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bootstrap Schema Form example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 94 | 95 | 96 | 142 | 143 |
144 |

Schema Form Example

145 |
146 |
147 |

The Generated Form

148 | 149 |
150 | 158 |
Form is valid
159 |
Form is not valid
160 | 161 |

Model

162 |
{{pretty()}}
163 |
164 |
165 |

Select Example

166 |
167 | 171 | 172 | By the way, there is also an example of 173 | custom (async) validators example. 174 | 175 |
176 |

Form

177 |
179 |

Schema

180 |
182 |
183 |
184 |
185 |

186 | 187 | JavaScript license information 188 | 189 |

190 |
191 |
192 | 193 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 392 | 393 | 394 | -------------------------------------------------------------------------------- /dist/angular-schema-form-bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * angular-schema-form-bootstrap 3 | * @version 1.0.0-alpha.5 4 | * @date Sat, 29 Apr 2017 14:49:38 GMT 5 | * @link https://github.com/json-schema-form/angular-schema-form-bootstrap 6 | * @license MIT 7 | * Copyright (c) 2014-2017 JSON Schema Form 8 | */ 9 | !function(r){function e(a){if(n[a])return n[a].exports;var l=n[a]={i:a,l:!1,exports:{}};return r[a].call(l.exports,l,l.exports,e),l.l=!0,l.exports}var n={};return e.m=r,e.c=n,e.i=function(r){return r},e.d=function(r,e,n){Object.defineProperty(r,e,{configurable:!1,enumerable:!0,get:n})},e.n=function(r){var n=r&&r.__esModule?function(){return r.default}:function(){return r};return e.d(n,"a",n),n},e.o=function(r,e){return Object.prototype.hasOwnProperty.call(r,e)},e.p="",e(e.s=24)}([function(r,e,n){n(18)},,function(r,e){var n="/bootstrap/actions.html",a='
\r\n \r\n \r\n
\r\n';window.angular.module("ng").run(["$templateCache",function(r){r.put(n,a)}]),r.exports=n},function(r,e){var n="/bootstrap/array.html",a='
\r\n \r\n
    \r\n
  1. \r\n \r\n
    \r\n
  2. \r\n
\r\n
\r\n
\r\n\r\n \r\n
\r\n
\r\n';window.angular.module("ng").run(["$templateCache",function(r){r.put(n,a)}]),r.exports=n},function(r,e){var n="/bootstrap/checkbox.html",a='
\r\n \r\n
\r\n
\r\n';window.angular.module("ng").run(["$templateCache",function(r){r.put(n,a)}]),r.exports=n},function(r,e){var n="/bootstrap/checkboxes.html",a='
\r\n \r\n\r\n
\r\n \r\n\r\n
\r\n
\r\n
\r\n';window.angular.module("ng").run(["$templateCache",function(r){r.put(n,a)}]),r.exports=n},function(r,e){var n="/bootstrap/default.html",a='
\r\n \r\n\r\n \r\n\r\n
\r\n \r\n \r\n\r\n \r\n
\r\n\r\n \r\n\r\n {{ hasSuccess() ? \'(success)\' : \'(error)\' }}\r\n\r\n
\r\n
\r\n';window.angular.module("ng").run(["$templateCache",function(r){r.put(n,a)}]),r.exports=n},function(r,e){var n="/bootstrap/fieldset.html",a='
\r\n {{ form.title }}\r\n
\r\n
\r\n';window.angular.module("ng").run(["$templateCache",function(r){r.put(n,a)}]),r.exports=n},function(r,e){var n="/bootstrap/help.html",a='
\r\n';window.angular.module("ng").run(["$templateCache",function(r){r.put(n,a)}]),r.exports=n},function(r,e){var n="/bootstrap/radio-buttons.html",a='
\r\n
\r\n \r\n
\r\n
\r\n \r\n
\r\n
\r\n
\r\n';window.angular.module("ng").run(["$templateCache",function(r){r.put(n,a)}]),r.exports=n},function(r,e){var n="/bootstrap/radios-inline.html",a='
\r\n \r\n
\r\n \r\n
\r\n
\r\n
\r\n';window.angular.module("ng").run(["$templateCache",function(r){r.put(n,a)}]),r.exports=n},function(r,e){var n="/bootstrap/radios.html",a='
\r\n \r\n
\r\n \r\n
\r\n
\r\n
\r\n';window.angular.module("ng").run(["$templateCache",function(r){r.put(n,a)}]),r.exports=n},function(r,e){var n="/bootstrap/section.html",a='
\r\n';window.angular.module("ng").run(["$templateCache",function(r){r.put(n,a)}]),r.exports=n},function(r,e){var n="/bootstrap/select.html",a='
\r\n \r\n \r\n
\r\n
\r\n';window.angular.module("ng").run(["$templateCache",function(r){r.put(n,a)}]),r.exports=n},function(r,e){var n="/bootstrap/submit.html",a='
\r\n \r\n \r\n
\r\n';window.angular.module("ng").run(["$templateCache",function(r){r.put(n,a)}]),r.exports=n},function(r,e){var n="/bootstrap/tabarray.html",a='
\r\n \r\n\r\n
\r\n
\r\n
\r\n\r\n
\r\n\r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n\r\n \r\n\r\n\r\n';window.angular.module("ng").run(["$templateCache",function(r){r.put(n,a)}]),r.exports=n},function(r,e){var n="/bootstrap/tabs.html",a='
\r\n \r\n\r\n
\r\n
\r\n
\r\n';window.angular.module("ng").run(["$templateCache",function(r){r.put(n,a)}]),r.exports=n},function(r,e){var n="/bootstrap/textarea.html",a='
\r\n \r\n\r\n \r\n\r\n
\r\n \r\n \r\n \r\n
\r\n\r\n \r\n
\r\n';window.angular.module("ng").run(["$templateCache",function(r){r.put(n,a)}]),r.exports=n},function(r,e,n){"use strict";function a(r,e,n,a,l){var s=n.builders.simpleTransclusion,i=n.builders.ngModelOptions,m=n.builders.ngModel,c=n.builders.sfField,p=n.builders.condition,h=n.builders.array,v=n.builders.numeric,$=function(r){if(r.form.tabs&&r.form.tabs.length>0){var e=r.fieldFrag.querySelector(".tab-content");r.form.tabs.forEach(function(n,a){var l="(evalExpr("+r.path+".tabs["+a+'].condition, { model: model, "arrayIndex": $index}))',t=document.createElement("div");t.className="tab-pane",t.setAttribute("ng-disabled","form.readonly"),t.setAttribute("ng-show","selected.tab === "+a),t.setAttribute("ng-class","{active: selected.tab === "+a+"}"),n.condition&&t.setAttribute("ng-if",l);var s=r.build(n.items,r.path+".tabs["+a+"].items",r.state);t.appendChild(s),e.appendChild(t)})}},C=function(r){if(r.form.placeholder){var e=r.fieldFrag.querySelector("select"),n=document.createElement("option");n.setAttribute("value",""),n.setAttribute("sf-field-model","replaceAll"),1===angular.version.major&&angular.version.minor<4?n.setAttribute("ng-if","$$value$$ === undefined"):n.setAttribute("ng-show","$$value$$ === undefined"),n.textContent=r.form.placeholder,e.appendChild(n)}},w=[c,m,i,p];e.defineDecorator("bootstrapDecorator",{actions:{template:t.a,builder:w},array:{template:o.a,builder:[c,i,m,h,p]},button:{template:H.a,builder:w},checkbox:{template:d.a,builder:w},checkboxes:{template:f.a,builder:[c,i,m,h,p]},conditional:{template:A.a,builder:[c,s,p]},default:{template:u.a,builder:w},fieldset:{template:b.a,builder:[c,s,p]},help:{template:g.a,builder:w},number:{template:u.a,builder:w.concat(v)},password:{template:u.a,builder:w},radios:{template:x.a,builder:w},"radios-inline":{template:k.a,builder:w},radiobuttons:{template:y.a,builder:w},section:{template:A.a,builder:[c,s,p]},select:{template:E.a,builder:[C].concat(w)},submit:{template:H.a,builder:w},tabarray:{template:q.a,builder:[c,i,m,h,p]},tabs:{template:R.a,builder:[c,i,$,p]},textarea:{template:j.a,builder:w}},[])}var l=n(2),t=n.n(l),s=n(3),o=n.n(s),i=n(4),d=n.n(i),m=n(5),f=n.n(m),c=n(6),u=n.n(c),p=n(7),b=n.n(p),h=n(8),g=n.n(h),v=n(9),y=n.n(v),$=n(11),x=n.n($),C=n(10),k=n.n(C),w=n(12),A=n.n(w),S=n(13),E=n.n(S),I=n(14),H=n.n(I),T=n(15),q=n.n(T),M=n(16),R=n.n(M),L=n(17),j=n.n(L);angular.module("schemaForm").config(a),a.$inject=["schemaFormProvider","schemaFormDecoratorsProvider","sfBuilderProvider","sfPathProvider","$injector"]},,,,,,function(r,e,n){r.exports=n(0)}]); -------------------------------------------------------------------------------- /dist/angular-schema-form-bootstrap.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * angular-schema-form-bootstrap 3 | * @version 1.0.0-alpha.5 4 | * @date Sat, 29 Apr 2017 14:49:38 GMT 5 | * @link https://github.com/json-schema-form/angular-schema-form-bootstrap 6 | * @license MIT 7 | * Copyright (c) 2014-2017 JSON Schema Form 8 | */ 9 | /******/ (function(modules) { // webpackBootstrap 10 | /******/ // The module cache 11 | /******/ var installedModules = {}; 12 | 13 | /******/ // The require function 14 | /******/ function __webpack_require__(moduleId) { 15 | 16 | /******/ // Check if module is in cache 17 | /******/ if(installedModules[moduleId]) 18 | /******/ return installedModules[moduleId].exports; 19 | 20 | /******/ // Create a new module (and put it into the cache) 21 | /******/ var module = installedModules[moduleId] = { 22 | /******/ i: moduleId, 23 | /******/ l: false, 24 | /******/ exports: {} 25 | /******/ }; 26 | 27 | /******/ // Execute the module function 28 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 29 | 30 | /******/ // Flag the module as loaded 31 | /******/ module.l = true; 32 | 33 | /******/ // Return the exports of the module 34 | /******/ return module.exports; 35 | /******/ } 36 | 37 | 38 | /******/ // expose the modules object (__webpack_modules__) 39 | /******/ __webpack_require__.m = modules; 40 | 41 | /******/ // expose the module cache 42 | /******/ __webpack_require__.c = installedModules; 43 | 44 | /******/ // identity function for calling harmory imports with the correct context 45 | /******/ __webpack_require__.i = function(value) { return value; }; 46 | 47 | /******/ // define getter function for harmory exports 48 | /******/ __webpack_require__.d = function(exports, name, getter) { 49 | /******/ Object.defineProperty(exports, name, { 50 | /******/ configurable: false, 51 | /******/ enumerable: true, 52 | /******/ get: getter 53 | /******/ }); 54 | /******/ }; 55 | 56 | /******/ // getDefaultExport function for compatibility with non-harmony modules 57 | /******/ __webpack_require__.n = function(module) { 58 | /******/ var getter = module && module.__esModule ? 59 | /******/ function getDefault() { return module['default']; } : 60 | /******/ function getModuleExports() { return module; }; 61 | /******/ __webpack_require__.d(getter, 'a', getter); 62 | /******/ return getter; 63 | /******/ }; 64 | 65 | /******/ // Object.prototype.hasOwnProperty.call 66 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 67 | 68 | /******/ // __webpack_public_path__ 69 | /******/ __webpack_require__.p = ""; 70 | 71 | /******/ // Load entry module and return exports 72 | /******/ return __webpack_require__(__webpack_require__.s = 21); 73 | /******/ }) 74 | /************************************************************************/ 75 | /******/ ([ 76 | /* 0 */ 77 | /***/ function(module, exports, __webpack_require__) { 78 | 79 | __webpack_require__(18); 80 | 81 | 82 | /***/ }, 83 | /* 1 */, 84 | /* 2 */ 85 | /***/ function(module, exports) { 86 | 87 | var path = '/bootstrap/actions.html'; 88 | var html = "
\r\n \r\n \r\n
\r\n"; 89 | window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]); 90 | module.exports = path; 91 | 92 | /***/ }, 93 | /* 3 */ 94 | /***/ function(module, exports) { 95 | 96 | var path = '/bootstrap/array.html'; 97 | var html = "
\r\n \r\n
    \r\n
  1. \r\n \r\n
    \r\n
  2. \r\n
\r\n
\r\n
\r\n\r\n \r\n
\r\n
\r\n"; 98 | window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]); 99 | module.exports = path; 100 | 101 | /***/ }, 102 | /* 4 */ 103 | /***/ function(module, exports) { 104 | 105 | var path = '/bootstrap/checkbox.html'; 106 | var html = "
\r\n \r\n
\r\n
\r\n"; 107 | window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]); 108 | module.exports = path; 109 | 110 | /***/ }, 111 | /* 5 */ 112 | /***/ function(module, exports) { 113 | 114 | var path = '/bootstrap/checkboxes.html'; 115 | var html = "
\r\n \r\n\r\n
\r\n \r\n\r\n
\r\n
\r\n
\r\n"; 116 | window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]); 117 | module.exports = path; 118 | 119 | /***/ }, 120 | /* 6 */ 121 | /***/ function(module, exports) { 122 | 123 | var path = '/bootstrap/default.html'; 124 | var html = "
\r\n \r\n\r\n \r\n\r\n
\r\n \r\n \r\n\r\n \r\n
\r\n\r\n \r\n\r\n {{ hasSuccess() ? '(success)' : '(error)' }}\r\n\r\n
\r\n
\r\n"; 125 | window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]); 126 | module.exports = path; 127 | 128 | /***/ }, 129 | /* 7 */ 130 | /***/ function(module, exports) { 131 | 132 | var path = '/bootstrap/fieldset.html'; 133 | var html = "
\r\n {{ form.title }}\r\n
\r\n
\r\n"; 134 | window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]); 135 | module.exports = path; 136 | 137 | /***/ }, 138 | /* 8 */ 139 | /***/ function(module, exports) { 140 | 141 | var path = '/bootstrap/help.html'; 142 | var html = "
\r\n"; 143 | window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]); 144 | module.exports = path; 145 | 146 | /***/ }, 147 | /* 9 */ 148 | /***/ function(module, exports) { 149 | 150 | var path = '/bootstrap/radio-buttons.html'; 151 | var html = "
\r\n
\r\n \r\n
\r\n
\r\n \r\n
\r\n
\r\n
\r\n"; 152 | window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]); 153 | module.exports = path; 154 | 155 | /***/ }, 156 | /* 10 */ 157 | /***/ function(module, exports) { 158 | 159 | var path = '/bootstrap/radios-inline.html'; 160 | var html = "
\r\n \r\n
\r\n \r\n
\r\n
\r\n
\r\n"; 161 | window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]); 162 | module.exports = path; 163 | 164 | /***/ }, 165 | /* 11 */ 166 | /***/ function(module, exports) { 167 | 168 | var path = '/bootstrap/radios.html'; 169 | var html = "
\r\n \r\n
\r\n \r\n
\r\n
\r\n
\r\n"; 170 | window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]); 171 | module.exports = path; 172 | 173 | /***/ }, 174 | /* 12 */ 175 | /***/ function(module, exports) { 176 | 177 | var path = '/bootstrap/section.html'; 178 | var html = "
\r\n"; 179 | window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]); 180 | module.exports = path; 181 | 182 | /***/ }, 183 | /* 13 */ 184 | /***/ function(module, exports) { 185 | 186 | var path = '/bootstrap/select.html'; 187 | var html = "
\r\n \r\n \r\n
\r\n
\r\n"; 188 | window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]); 189 | module.exports = path; 190 | 191 | /***/ }, 192 | /* 14 */ 193 | /***/ function(module, exports) { 194 | 195 | var path = '/bootstrap/submit.html'; 196 | var html = "
\r\n \r\n \r\n
\r\n"; 197 | window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]); 198 | module.exports = path; 199 | 200 | /***/ }, 201 | /* 15 */ 202 | /***/ function(module, exports) { 203 | 204 | var path = '/bootstrap/tabarray.html'; 205 | var html = "
\r\n \r\n\r\n
\r\n
\r\n
\r\n\r\n
\r\n\r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n\r\n \r\n\r\n\r\n"; 206 | window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]); 207 | module.exports = path; 208 | 209 | /***/ }, 210 | /* 16 */ 211 | /***/ function(module, exports) { 212 | 213 | var path = '/bootstrap/tabs.html'; 214 | var html = "
\r\n \r\n\r\n
\r\n
\r\n
\r\n"; 215 | window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]); 216 | module.exports = path; 217 | 218 | /***/ }, 219 | /* 17 */ 220 | /***/ function(module, exports) { 221 | 222 | var path = '/bootstrap/textarea.html'; 223 | var html = "
\r\n \r\n\r\n \r\n\r\n
\r\n \r\n \r\n \r\n
\r\n\r\n \r\n
\r\n"; 224 | window.angular.module('ng').run(['$templateCache', function(c) { c.put(path, html) }]); 225 | module.exports = path; 226 | 227 | /***/ }, 228 | /* 18 */ 229 | /***/ function(module, exports, __webpack_require__) { 230 | 231 | "use strict"; 232 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__bootstrap_actions_html__ = __webpack_require__(2); 233 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__bootstrap_actions_html___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__bootstrap_actions_html__); 234 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__bootstrap_array_html__ = __webpack_require__(3); 235 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__bootstrap_array_html___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__bootstrap_array_html__); 236 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__bootstrap_checkbox_html__ = __webpack_require__(4); 237 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__bootstrap_checkbox_html___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2__bootstrap_checkbox_html__); 238 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__bootstrap_checkboxes_html__ = __webpack_require__(5); 239 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__bootstrap_checkboxes_html___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_3__bootstrap_checkboxes_html__); 240 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__bootstrap_default_html__ = __webpack_require__(6); 241 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__bootstrap_default_html___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_4__bootstrap_default_html__); 242 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__bootstrap_fieldset_html__ = __webpack_require__(7); 243 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__bootstrap_fieldset_html___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_5__bootstrap_fieldset_html__); 244 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__bootstrap_help_html__ = __webpack_require__(8); 245 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__bootstrap_help_html___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_6__bootstrap_help_html__); 246 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__bootstrap_radio_buttons_html__ = __webpack_require__(9); 247 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__bootstrap_radio_buttons_html___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_7__bootstrap_radio_buttons_html__); 248 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_8__bootstrap_radios_html__ = __webpack_require__(11); 249 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_8__bootstrap_radios_html___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_8__bootstrap_radios_html__); 250 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_9__bootstrap_radios_inline_html__ = __webpack_require__(10); 251 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_9__bootstrap_radios_inline_html___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_9__bootstrap_radios_inline_html__); 252 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_10__bootstrap_section_html__ = __webpack_require__(12); 253 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_10__bootstrap_section_html___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_10__bootstrap_section_html__); 254 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_11__bootstrap_select_html__ = __webpack_require__(13); 255 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_11__bootstrap_select_html___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_11__bootstrap_select_html__); 256 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_12__bootstrap_submit_html__ = __webpack_require__(14); 257 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_12__bootstrap_submit_html___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_12__bootstrap_submit_html__); 258 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_13__bootstrap_tabarray_html__ = __webpack_require__(15); 259 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_13__bootstrap_tabarray_html___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_13__bootstrap_tabarray_html__); 260 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_14__bootstrap_tabs_html__ = __webpack_require__(16); 261 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_14__bootstrap_tabs_html___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_14__bootstrap_tabs_html__); 262 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_15__bootstrap_textarea_html__ = __webpack_require__(17); 263 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_15__bootstrap_textarea_html___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_15__bootstrap_textarea_html__); 264 | // ngtemplate-loader embeds the html on build 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | angular 283 | .module('schemaForm') 284 | .config(bootstrapDecoratorConfig); 285 | 286 | bootstrapDecoratorConfig.$inject = [ 287 | 'schemaFormProvider', 'schemaFormDecoratorsProvider', 'sfBuilderProvider', 'sfPathProvider', '$injector' 288 | ]; 289 | 290 | function bootstrapDecoratorConfig( 291 | schemaFormProvider, decoratorsProvider, sfBuilderProvider, sfPathProvider, $injector) { 292 | var base = 'decorators/bootstrap/'; 293 | 294 | var simpleTransclusion = sfBuilderProvider.builders.simpleTransclusion; 295 | var ngModelOptions = sfBuilderProvider.builders.ngModelOptions; 296 | var ngModel = sfBuilderProvider.builders.ngModel; 297 | var sfField = sfBuilderProvider.builders.sfField; 298 | var condition = sfBuilderProvider.builders.condition; 299 | var array = sfBuilderProvider.builders.array; 300 | var numeric = sfBuilderProvider.builders.numeric; 301 | 302 | // Tabs is so bootstrap specific that it stays here. 303 | var tabs = function(args) { 304 | if (args.form.tabs && args.form.tabs.length > 0) { 305 | var tabContent = args.fieldFrag.querySelector('.tab-content'); 306 | 307 | args.form.tabs.forEach(function(tab, index) { 308 | var evalExpr = '(evalExpr(' + args.path + '.tabs[' + index + ']' + 309 | '.condition, { model: model, "arrayIndex": $index}))'; 310 | var div = document.createElement('div'); 311 | div.className = 'tab-pane'; 312 | div.setAttribute('ng-disabled', 'form.readonly'); 313 | div.setAttribute('ng-show', 'selected.tab === ' + index); 314 | div.setAttribute('ng-class', '{active: selected.tab === ' + index + '}'); 315 | if(!!tab.condition) { 316 | div.setAttribute('ng-if', evalExpr); 317 | }; 318 | 319 | var childFrag = args.build(tab.items, args.path + '.tabs[' + index + '].items', args.state); 320 | div.appendChild(childFrag); 321 | tabContent.appendChild(div); 322 | }); 323 | } 324 | }; 325 | 326 | var selectPlaceholder = function(args) { 327 | if (args.form.placeholder) { 328 | var selectBox = args.fieldFrag.querySelector('select'); 329 | var option = document.createElement('option'); 330 | option.setAttribute('value', ''); 331 | 332 | /* We only want the placeholder to show when we do not have a value on the model. 333 | * We make ngModel builder replace all so we can use $$value$$. 334 | */ 335 | option.setAttribute('sf-field-model', 'replaceAll'); 336 | 337 | /* https://github.com/angular/angular.js/issues/12190#issuecomment-115277040 338 | * angular > 1.4 does a emptyOption.attr('selected', true) 339 | * which does not like the ng-if comment. 340 | */ 341 | if (angular.version.major === 1 && angular.version.minor < 4) { 342 | option.setAttribute('ng-if', '$$value$$ === undefined'); 343 | } else { 344 | option.setAttribute('ng-show', '$$value$$ === undefined'); 345 | } 346 | 347 | option.textContent = args.form.placeholder; 348 | 349 | selectBox.appendChild(option); 350 | } 351 | }; 352 | 353 | var defaults = [sfField, ngModel, ngModelOptions, condition]; 354 | decoratorsProvider.defineDecorator('bootstrapDecorator', { 355 | actions: {template: __WEBPACK_IMPORTED_MODULE_0__bootstrap_actions_html___default.a, builder: defaults}, 356 | array: {template: __WEBPACK_IMPORTED_MODULE_1__bootstrap_array_html___default.a, builder: [ sfField, ngModelOptions, ngModel, array, condition ]}, 357 | button: {template: __WEBPACK_IMPORTED_MODULE_12__bootstrap_submit_html___default.a, builder: defaults}, 358 | checkbox: {template: __WEBPACK_IMPORTED_MODULE_2__bootstrap_checkbox_html___default.a, builder: defaults}, 359 | checkboxes: {template: __WEBPACK_IMPORTED_MODULE_3__bootstrap_checkboxes_html___default.a, builder: [ sfField, ngModelOptions, ngModel, array, condition ]}, 360 | conditional: {template: __WEBPACK_IMPORTED_MODULE_10__bootstrap_section_html___default.a, builder: [ sfField, simpleTransclusion, condition ]}, 361 | 'default': {template: __WEBPACK_IMPORTED_MODULE_4__bootstrap_default_html___default.a, builder: defaults}, 362 | fieldset: {template: __WEBPACK_IMPORTED_MODULE_5__bootstrap_fieldset_html___default.a, builder: [ sfField, simpleTransclusion, condition ]}, 363 | help: {template: __WEBPACK_IMPORTED_MODULE_6__bootstrap_help_html___default.a, builder: defaults}, 364 | number: {template: __WEBPACK_IMPORTED_MODULE_4__bootstrap_default_html___default.a, builder: defaults.concat(numeric)}, 365 | password: {template: __WEBPACK_IMPORTED_MODULE_4__bootstrap_default_html___default.a, builder: defaults}, 366 | radios: {template: __WEBPACK_IMPORTED_MODULE_8__bootstrap_radios_html___default.a, builder: defaults}, 367 | 'radios-inline': {template: __WEBPACK_IMPORTED_MODULE_9__bootstrap_radios_inline_html___default.a, builder: defaults}, 368 | radiobuttons: {template: __WEBPACK_IMPORTED_MODULE_7__bootstrap_radio_buttons_html___default.a, builder: defaults}, 369 | section: {template: __WEBPACK_IMPORTED_MODULE_10__bootstrap_section_html___default.a, builder: [ sfField, simpleTransclusion, condition ]}, 370 | select: {template: __WEBPACK_IMPORTED_MODULE_11__bootstrap_select_html___default.a, builder: [ selectPlaceholder ].concat(defaults)}, 371 | submit: {template: __WEBPACK_IMPORTED_MODULE_12__bootstrap_submit_html___default.a, builder: defaults}, 372 | tabarray: {template: __WEBPACK_IMPORTED_MODULE_13__bootstrap_tabarray_html___default.a, builder: [ sfField, ngModelOptions, ngModel, array, condition ]}, 373 | tabs: {template: __WEBPACK_IMPORTED_MODULE_14__bootstrap_tabs_html___default.a, builder: [ sfField, ngModelOptions, tabs, condition ]}, 374 | textarea: {template: __WEBPACK_IMPORTED_MODULE_15__bootstrap_textarea_html___default.a, builder: defaults}, 375 | }, []); 376 | }; 377 | 378 | 379 | /***/ }, 380 | /* 19 */, 381 | /* 20 */, 382 | /* 21 */ 383 | /***/ function(module, exports, __webpack_require__) { 384 | 385 | module.exports = __webpack_require__(0); 386 | 387 | 388 | /***/ } 389 | /******/ ]); --------------------------------------------------------------------------------