├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bower.json ├── circle.yml ├── definitions ├── angular.d.ts └── jquery.d.ts ├── dist ├── form-for.bootstrap-templates.js ├── form-for.css ├── form-for.default-templates.js ├── form-for.js ├── form-for.material-templates.js ├── form-for.material.css ├── form-for.min.js └── form-for.min.js.map ├── examples ├── checkbox-field.html ├── init-test.js ├── radio-field.html └── select-field.html ├── gulpfile.js ├── karma.conf.js ├── package.json ├── protractor.conf.js ├── source ├── directives │ ├── aria-manager.ts │ ├── checkbox-field.ts │ ├── collection-label.ts │ ├── field-error.ts │ ├── field-label.ts │ ├── form-for-builder.ts │ ├── form-for-debounce.ts │ ├── form-for.ts │ ├── radio-field.ts │ ├── select-field.ts │ ├── submit-button.ts │ ├── text-field.ts │ └── type-ahead-field.ts ├── enums │ ├── builder-field-type.ts │ ├── validation-failure-type.ts │ └── validation-field-type.ts ├── interfaces │ ├── bindable-collection-wrapper.ts │ ├── bindable-field-wrapper.ts │ ├── builder │ │ ├── view-field.ts │ │ └── view-schema.ts │ ├── custom-validation-function.ts │ ├── field-datum.ts │ ├── form-for-scope.ts │ ├── submit-button-wrapper.ts │ └── validation │ │ ├── validation-error-map.ts │ │ ├── validation-rule-boolean.ts │ │ ├── validation-rule-collection.ts │ │ ├── validation-rule-custom.ts │ │ ├── validation-rule-field-type.ts │ │ ├── validation-rule-number.ts │ │ ├── validation-rule-regexp.ts │ │ ├── validation-rule.ts │ │ └── validation-ruleset.ts ├── module.ts ├── services │ ├── field-helper.ts │ ├── form-for-configuration.ts │ └── model-validator.ts └── utils │ ├── form-for-controller.ts │ ├── form-for-guid.ts │ ├── form-for-state-helper.ts │ ├── nested-object-helper.ts │ ├── promise-utils.ts │ └── string-util.ts ├── styles ├── default │ ├── _mixins.styl │ ├── _variables.styl │ ├── checkbox-field.styl │ ├── collection-label.styl │ ├── field-error.styl │ ├── field-label.styl │ ├── form-for-field.styl │ ├── form-for.styl │ ├── radio-field.styl │ ├── select-field.styl │ ├── submit-button.styl │ ├── text-field.styl │ ├── tooltip.styl │ └── type-ahead-field.styl └── material │ ├── field-error.styl │ ├── md-checkbox.styl │ ├── md-input-container.styl │ └── select-field.styl ├── templates ├── bootstrap │ ├── checkbox-field.html │ ├── collection-label.html │ ├── field-error.html │ ├── field-label.html │ ├── radio-field.html │ ├── select-field.html │ ├── select-field │ │ ├── _multi-select.html │ │ └── _select.html │ ├── submit-button.html │ ├── text-field.html │ ├── text-field │ │ ├── _input.html │ │ └── _textarea.html │ └── type-ahead-field.html ├── default │ ├── checkbox-field.html │ ├── collection-label.html │ ├── field-error.html │ ├── field-label.html │ ├── radio-field.html │ ├── select-field.html │ ├── select-field │ │ ├── _multi-select.html │ │ └── _select.html │ ├── submit-button.html │ ├── text-field.html │ ├── text-field │ │ ├── _input.html │ │ └── _textarea.html │ └── type-ahead-field.html └── material │ ├── checkbox-field.html │ ├── collection-label.html │ ├── field-error.html │ ├── field-label.html │ ├── radio-field.html │ ├── select-field.html │ ├── submit-button.html │ ├── text-field.html │ └── type-ahead-field.html └── tests ├── integration ├── directives │ ├── checkbox-field.js │ ├── radio-field.js │ └── select-field.js └── test-helper.js └── unit └── services ├── form-for-state-helper.js ├── model-validator.js ├── nested-object-helper.js ├── parse.js ├── promise-utils.js └── string-util.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.bower 3 | /.idea 4 | /bower_components 5 | /node_modules 6 | /npm-debug.log 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 'iojs' 4 | - 'node' 5 | install: 6 | - npm install 7 | script: 8 | - gulp travis -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to this project 2 | 3 | Please take a moment to review this document in order to make the contribution process easy and effective for everyone involved. 4 | 5 | Following these guidelines helps to communicate that you respect the time of the developers managing and developing this open source project. 6 | In return, they should reciprocate that respect in addressing your issue or assessing patches and features. 7 | 8 | 9 | ## Using the issue tracker 10 | 11 | The issue tracker is the preferred channel for [bug reports](#bugs), [features requests](#features), [submitting pull requests](#pull-requests), and [running tests](#running-tests) but please respect the following restrictions: 12 | 13 | * Please **do not** use the issue tracker for personal support requests (use [Gitter](https://gitter.im/bvaughn/angular-form-for)). 14 | * Please **do not** derail or troll issues. Keep the discussion on topic and respect the opinions of others. 15 | 16 | 17 | ## Bug reports 18 | 19 | A bug is a _demonstrable problem_ that is caused by the code in the repository. 20 | Good bug reports are extremely helpful - thank you! 21 | 22 | Guidelines for bug reports: 23 | 24 | 1. **Use the GitHub issue search** — check if the issue has already been reported. 25 | 2. **Check if the issue has been fixed** — try to reproduce it using the latest `master` or development branch in the repository. 26 | 3. **Isolate the problem** — create a [reduced test case](http://css-tricks.com/reduced-test-cases/) and a live example (using a site like [Plunker](http://plnkr.co/)). 27 | 28 | A good bug report shouldn't leave others needing to chase you up for more information. 29 | Please try to be as detailed as possible in your report. 30 | Which versions of formFor and Angular are you using? 31 | What steps will reproduce the issue? What browser(s) and OS experience the problem? 32 | What would you expect to be the outcome? 33 | All these details will help people to fix any potential bugs. 34 | 35 | Example: 36 | 37 | > Short and descriptive example bug report title 38 | > 39 | > A summary of the issue and the browser/OS environment in which it occurs. 40 | > If suitable, include the steps required to reproduce the bug. 41 | > 42 | > 1. This is the first step 43 | > 2. This is the second step 44 | > 3. Further steps, etc. 45 | > 46 | > `` - a link to the reduced test case 47 | > 48 | > Any other information you want to share that is relevant to the issue being reported. 49 | > This might include the lines of code that you have identified as causing the bug, 50 | > and potential solutions (and your opinions on their merits). 51 | 52 | 53 | 54 | ## Feature requests 55 | 56 | Feature requests are welcome. 57 | But take a moment to find out whether your idea fits with the scope and aims of the project. 58 | It's up to *you* to make a strong case to convince the project's developers of the merits of this feature. 59 | Please provide as much detail and context as possible. 60 | 61 | 62 | 63 | ## Pull requests 64 | 65 | Good pull requests - patches, improvements, new features - are a fantastic help. 66 | They should remain focused in scope and avoid containing unrelated commits. 67 | 68 | **Please ask first** before embarking on any significant pull request (e.g. implementing features, refactoring code, porting to a different language), 69 | otherwise you risk spending a lot of time working on something that the project's developers might not want to merge into the project. 70 | 71 | Please adhere to the coding conventions used throughout a project (indentation, accurate comments, etc.) and any other requirements (such as test coverage). 72 | 73 | Follow this process if you'd like your work considered for inclusion in the project: 74 | 75 | 1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, and configure the remotes: 76 | 77 | ```bash 78 | # Clone your fork of the repo into the current directory 79 | git clone https://github.com//angular-form-for 80 | # Navigate to the newly cloned directory 81 | cd angular-form-for 82 | # Assign the original repo to a remote called "upstream" 83 | git remote add upstream https://github.com/bvaughn/angular-form-for 84 | ``` 85 | 86 | 2. If you cloned a while ago, get the latest changes from upstream: 87 | 88 | ```bash 89 | git checkout master 90 | git pull upstream master 91 | ``` 92 | 93 | 3. Create a new topic branch (off the main project development branch) to 94 | contain your feature, change, or fix: 95 | 96 | ```bash 97 | git checkout -b 98 | ``` 99 | 100 | 4. Commit your changes in logical chunks. 101 | Please adhere to these [git commit message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) 102 | or your code is unlikely be merged into the main project. 103 | Use Git's [interactive rebase](https://help.github.com/articles/interactive-rebase) 104 | feature to tidy up your commits before making them public. 105 | 106 | 5. Locally merge (or rebase) the upstream development branch into your topic branch: 107 | 108 | ```bash 109 | git pull [--rebase] upstream master 110 | ``` 111 | 112 | 6. Push your topic branch up to your fork: 113 | 114 | ```bash 115 | git push origin 116 | ``` 117 | 118 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) 119 | with a clear title and description. 120 | 121 | **IMPORTANT**: By submitting a patch, you agree to allow the project owner to license your work under the same license as that used by this project (MIT). 122 | 123 | 124 | ## Running Tests 125 | 126 | ### Unit Tests 127 | 128 | All unit tests must pass before a pull request will be approved. You can run unit tests with Gulp like so: 129 | 130 | ```bash 131 | cd 132 | gulp test:unit 133 | ``` 134 | 135 | ### Integration Tests 136 | 137 | Integration tests are a bit more complicated to run. 138 | They require web-driver to be running as well as a local HTTP server. 139 | 140 | To start web-driver: 141 | 142 | ```bash 143 | cd 144 | webdriver-manager start 145 | ``` 146 | 147 | To start the HTTP server: 148 | 149 | ```bash 150 | cd 151 | http-server -p 8000 152 | ``` 153 | 154 | Then run the integration tests with Gulp like so: 155 | 156 | ```bash 157 | cd 158 | gulp test:unit 159 | ``` -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Brian Vaughn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This repository is no longer actively maintained. 2 | 3 | Issue reports and pull requests will probably not be attended. 4 | 5 | I am no longer working with Angular on a regular basis. My day job has switched to React and it's too much effort to context switch between the frameworks. Given the upcoming Angular 2.0 release it seems like a reasonable time to end support for this library. 6 | 7 | Thanks to all of you for using, supporting, and contributing to this project. 8 | 9 | # angular-form-for [![Build Status](https://travis-ci.org/bvaughn/angular-form-for.svg)](https://travis-ci.org/bvaughn/angular-form-for) [![Join the chat at https://gitter.im/bvaughn/angular-form-for](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/bvaughn/angular-form-for?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 10 | 11 | *formFor* is a quick and easy way to declare complex HTML forms with client and server-side validations. 12 | You can generate a complete form with as little markup as this: 13 | 14 | ```html 15 |
16 | ``` 17 | 18 | But that's not all! *formFor* is incredibly flexible, offering a wide range of configuration options. 19 | Check out the official website to learn more: 20 | [http://bvaughn.github.io/angular-form-for/](http://bvaughn.github.io/angular-form-for/) 21 | 22 | ## Compatibility and Dependencies 23 | 24 | *formFor* is compatible with Angular Angular 1.2 and newer. It does not require any third party libraries such as jQuery, lodash, or underscore. 25 | 26 | ## Installation 27 | 28 | You can install this plugin with either [Bower](http://bower.io/) or [NPM](https://www.npmjs.org/): 29 | 30 | ```shell 31 | bower install angular-form-for --save-dev 32 | npm install angular-form-for --save-dev 33 | ``` 34 | 35 | This will download an `angular-form-for` folder into your bower/node components directory. Inside of that folder there will be a `dist` folder with the *formFor* JavaScript and CSS files. By default *formFor* is compatible with [Bootstrap](getbootstrap.com) 3.2.x styles. A separate, *formFor* only CSS stylehseet is included for those not using Bootstrap. 36 | 37 | Lastly just include the *formFor* module in your Angular application like so: 38 | 39 | ```js 40 | angular.module('myAngularApp', ['formFor']); 41 | ``` 42 | 43 | ## Contributions 44 | 45 | Interested in contributing? Check out the [contribution guidelines](CONTRIBUTING.md)! 46 | 47 | ## License 48 | 49 | Copyright (c) 2014 Brian Vaughn. Licensed under the MIT license. 50 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-form-for", 3 | "version": "4.1.10", 4 | "main": [ 5 | "./dist/form-for.css", 6 | "./dist/form-for.js" 7 | ], 8 | "homepage": "https://github.com/bvaughn/angular-form-for", 9 | "authors": [ 10 | "Brian Vaughn " 11 | ], 12 | "description": "Set of Angular directives to simplify creating and validating HTML forms.", 13 | "keywords": [ 14 | "angular", 15 | "form", 16 | "forms", 17 | "crud", 18 | "validation" 19 | ], 20 | "license": "MIT", 21 | "ignore": [ 22 | "**/.*", 23 | "node_modules", 24 | "bower_components", 25 | "test", 26 | "tests" 27 | ], 28 | "dependencies": {}, 29 | "devDependencies": {} 30 | } 31 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | node: 3 | version: 4 | 0.10.28 5 | test: 6 | pre: 7 | - npm start: 8 | background: true 9 | - sleep 5 10 | override: 11 | - npm run unit 12 | - npm run protractor -------------------------------------------------------------------------------- /dist/form-for.bootstrap-templates.js: -------------------------------------------------------------------------------- 1 | angular.module("formFor.bootstrapTemplates", []).run(["$templateCache", function($templateCache) {$templateCache.put("form-for/templates/checkbox-field.html","
\n\n \n \n\n \n\n \n \n
\n"); 2 | $templateCache.put("form-for/templates/collection-label.html","
\n \n \n\n \n
\n"); 3 | $templateCache.put("form-for/templates/field-error.html","

\n

\n"); 4 | $templateCache.put("form-for/templates/field-label.html","\n"); 5 | $templateCache.put("form-for/templates/radio-field.html","\n\n \n \n\n \n \n\n
\n \n
\n
\n"); 6 | $templateCache.put("form-for/templates/select-field.html","
\n\n \n \n\n \n \n\n \n\n \n\n \n
\n"); 7 | $templateCache.put("form-for/templates/submit-button.html","\n"); 8 | $templateCache.put("form-for/templates/text-field.html","
\n\n \n \n\n \n \n\n
\n \n \n \n\n \n\n \n\n \n \n \n
\n
\n"); 9 | $templateCache.put("form-for/templates/type-ahead-field.html","
\n\n \n \n\n \n \n\n \n
\n \n\n
    \n
  • \n\n \n \n
  • \n
\n\n \n \n \n
\n
\n"); 10 | $templateCache.put("form-for/templates/select-field/_multi-select.html",""); 11 | $templateCache.put("form-for/templates/select-field/_select.html",""); 12 | $templateCache.put("form-for/templates/text-field/_input.html","\n"); 13 | $templateCache.put("form-for/templates/text-field/_textarea.html","\n");}]); -------------------------------------------------------------------------------- /dist/form-for.default-templates.js: -------------------------------------------------------------------------------- 1 | angular.module("formFor.defaultTemplates", []).run(["$templateCache", function($templateCache) {$templateCache.put("form-for/templates/checkbox-field.html","
\n \n \n\n \n\n \n \n
\n"); 2 | $templateCache.put("form-for/templates/collection-label.html","
\n \n \n\n \n
\n"); 3 | $templateCache.put("form-for/templates/field-error.html","

\n

\n"); 4 | $templateCache.put("form-for/templates/field-label.html","\n"); 5 | $templateCache.put("form-for/templates/radio-field.html","
\n \n \n\n \n \n\n \n
"); 6 | $templateCache.put("form-for/templates/select-field.html","
\n \n \n\n \n\n \n\n \n\n \n
\n"); 7 | $templateCache.put("form-for/templates/submit-button.html","\n"); 8 | $templateCache.put("form-for/templates/text-field.html","
\n \n \n\n \n\n \n \n \n\n \n\n \n
\n"); 9 | $templateCache.put("form-for/templates/type-ahead-field.html","
\n \n \n\n \n\n \n \n\n
    \n
  • \n
  • \n
\n\n \n
\n"); 10 | $templateCache.put("form-for/templates/select-field/_multi-select.html","\n\n"); 11 | $templateCache.put("form-for/templates/select-field/_select.html","\n\n"); 12 | $templateCache.put("form-for/templates/text-field/_input.html","\n"); 13 | $templateCache.put("form-for/templates/text-field/_textarea.html","\n");}]); -------------------------------------------------------------------------------- /dist/form-for.material-templates.js: -------------------------------------------------------------------------------- 1 | angular.module("formFor.materialTemplates", []).run(["$templateCache", function($templateCache) {$templateCache.put("form-for/templates/checkbox-field.html","\n \n\n {{label}}\n \n\n \n \n"); 2 | $templateCache.put("form-for/templates/collection-label.html","
\n \n \n\n \n
\n"); 3 | $templateCache.put("form-for/templates/field-error.html","
\n
\n"); 4 | $templateCache.put("form-for/templates/field-label.html",""); 5 | $templateCache.put("form-for/templates/radio-field.html","\n \n \n\n \n \n \n\n \n \n"); 6 | $templateCache.put("form-for/templates/select-field.html","
\n \n\n \n \n\n \n {{option[labelAttribute]}}\n \n \n\n \n\n \n {{option[labelAttribute]}}\n \n \n\n \n \n \n
"); 7 | $templateCache.put("form-for/templates/submit-button.html","\n"); 8 | $templateCache.put("form-for/templates/text-field.html","\n \n\n \n\n \n\n \n"); 9 | $templateCache.put("form-for/templates/type-ahead-field.html","\n \n \n\n \n\n \n \n {{option[labelAttribute]}}\n \n \n\n \n No matches found for \"{{scopeBuster.filter}}\".\n \n \n\n \n \n");}]); -------------------------------------------------------------------------------- /dist/form-for.material.css: -------------------------------------------------------------------------------- 1 | field-error { 2 | display: block; 3 | } 4 | field-error .text-danger { 5 | font-size: 12px; 6 | line-height: 24px; 7 | color: #f44336; 8 | } 9 | 10 | md-checkbox { 11 | display: inline-block !important; 12 | } 13 | 14 | 15 | select-field md-select { 16 | padding: 0; 17 | } 18 | select-field md-input-container label { 19 | display: none; 20 | } 21 | -------------------------------------------------------------------------------- /examples/checkbox-field.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 22 | 23 |
24 | 26 | 27 | 28 | 30 | 31 | 32 | 35 | 36 | 37 | 40 | 41 | 42 | 44 | 45 |
46 | 47 | -------------------------------------------------------------------------------- /examples/init-test.js: -------------------------------------------------------------------------------- 1 | function init(callback) { 2 | 3 | // http://stackoverflow.com/questions/19491336/get-url-parameter-jquery 4 | var getUrlParameter = function(sParam) { 5 | var sPageURL = window.location.search.substring(1); 6 | var sURLVariables = sPageURL.split('&'); 7 | for (var i = 0; i < sURLVariables.length; i++) { 8 | var sParameterName = sURLVariables[i].split('='); 9 | if (sParameterName[0] == sParam) { 10 | return sParameterName[1]; 11 | } 12 | } 13 | }; 14 | 15 | var loadingResources = []; 16 | 17 | var resourceLoaded = function(resource) { 18 | console.log('Resource loaded: ' + resource); 19 | 20 | loadingResources.splice(loadingResources.indexOf(resource), 1); 21 | 22 | // Once all resources have loaded, proceed to bootstrap the application 23 | if (loadingResources.length == 0) { 24 | console.log('Initiliazing module'); 25 | 26 | var module = angular.module('ExampleApp', dependencies); 27 | module.controller('ExampleController', function($scope, $timeout) { 28 | $scope.controller = {}; 29 | $scope.formData = {}; 30 | $scope.validation = {}; 31 | 32 | callback($scope); 33 | 34 | $scope.$watch('controller', function(controller) { 35 | $timeout(function() { 36 | controller.validateForm(true); 37 | }, 1); 38 | }); 39 | }); 40 | 41 | var body = $('body'); 42 | body.attr('ng-controller', 'ExampleController'); 43 | 44 | angular.bootstrap(body, ['ExampleApp']); 45 | } 46 | }; 47 | 48 | // Load CSS stylesheets 49 | var loadStylesheet = function(source) { 50 | console.log('Loading style: ' + source); 51 | 52 | loadingResources.push(source); 53 | 54 | $.get(source) 55 | .done(function(response) { 56 | $('head').append(``); 57 | 58 | resourceLoaded(source); 59 | }) 60 | .fail(function() { 61 | console.warn('Resource failed: ' + source); 62 | }); 63 | }; 64 | 65 | // Load JS script 66 | var loadScript = function(source) { 67 | console.log('Loading script: ' + source); 68 | 69 | loadingResources.push(source); 70 | 71 | $.getScript(source) 72 | .done(function () { 73 | resourceLoaded(source); 74 | }) 75 | .fail(function() { 76 | console.warn('Resource failed: ' + source); 77 | }); 78 | }; 79 | 80 | // Load scripts needed by all tests 81 | loadScript('/dist/form-for.js'); 82 | loadScript('/node_modules/angular-aria/angular-aria.js'); 83 | loadScript('/node_modules/angular-route/angular-route.js'); 84 | 85 | var dependencies = [ 86 | 'formFor', 87 | 'ngRoute' 88 | ]; 89 | 90 | // Load the additional dependencies needed by the targed template. 91 | switch (getUrlParameter('template')) { 92 | case 'bootstrap': 93 | loadScript('/dist/form-for.bootstrap-templates.js'); 94 | loadScript('/node_modules/angular-bootstrap-npm/dist/angular-bootstrap.js'); 95 | loadStylesheet('/node_modules/bootstrap/dist/css/bootstrap.css'); 96 | loadStylesheet('/node_modules/font-awesome/css/font-awesome.css'); 97 | 98 | dependencies.push('formFor.bootstrapTemplates'); 99 | dependencies.push('ui.bootstrap'); 100 | break; 101 | case 'material': 102 | loadScript('/dist/form-for.material-templates.js'); 103 | loadScript('/node_modules/angular-animate/angular-animate.js'); 104 | loadScript('/node_modules/angular-material/angular-material.js'); 105 | loadStylesheet('/node_modules/angular-material/angular-material.css'); 106 | 107 | dependencies.push('ngMaterial'); 108 | dependencies.push('formFor.materialTemplates'); 109 | break; 110 | case 'default': 111 | default: 112 | loadScript('/dist/form-for.default-templates.js'); 113 | loadStylesheet('/dist/form-for.css'); 114 | 115 | dependencies.push('formFor.defaultTemplates'); 116 | break; 117 | } 118 | } -------------------------------------------------------------------------------- /examples/radio-field.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 27 | 28 |
29 | 32 | 33 | 34 | 38 | 39 | 40 | 44 | 45 | 46 | 49 | 50 |
51 | 52 | -------------------------------------------------------------------------------- /examples/select-field.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 29 | 30 |
31 | 35 | 36 | 37 | 41 | 42 | 43 | 47 | 48 | 49 | 53 | 54 | 55 | 59 | 60 |
61 | 62 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var karma = require('gulp-karma'); 3 | var runSequence = require('run-sequence'); 4 | 5 | var sources = [ 6 | 'source/**/*.ts' 7 | ]; 8 | var stylesDirectory = "styles"; 9 | var testFiles = []; // Declared in the karma.conf.js 10 | var distDirectory = 'dist'; 11 | 12 | /** 13 | * Main task: cleans, builds, run tests, and bundles up for distribution. 14 | */ 15 | gulp.task('all', function(callback) { 16 | runSequence( 17 | 'clean', 18 | 'build', 19 | 'test', 20 | callback); 21 | }); 22 | 23 | gulp.task('build', function(callback) { 24 | runSequence( 25 | ['compile', 'compileBootstrapTemplates', 'compileDefaultTemplates', 'compileMaterialTemplates', 'compileStylesheets'], 26 | 'uglify', 27 | 'map', 28 | callback); 29 | }); 30 | 31 | gulp.task('compile', function() { 32 | return buildHelper(sources, distDirectory , 'form-for.js'); 33 | }); 34 | 35 | gulp.task('compileBootstrapTemplates', function() { 36 | return buildTemplatesHelper('templates/bootstrap/**/*.html', 'formFor.bootstrapTemplates', 'form-for.bootstrap-templates.js'); 37 | }); 38 | 39 | gulp.task('compileDefaultTemplates', function() { 40 | return buildTemplatesHelper('templates/default/**/*.html', 'formFor.defaultTemplates', 'form-for.default-templates.js'); 41 | }); 42 | 43 | gulp.task('compileMaterialTemplates', function() { 44 | return buildTemplatesHelper('templates/material/**/*.html', 'formFor.materialTemplates', 'form-for.material-templates.js'); 45 | }); 46 | 47 | gulp.task('compileStylesheets', function(callback) { 48 | runSequence( 49 | ['compileDefaultStylesheets', 'compileMaterialStylesheets'], 50 | callback); 51 | }); 52 | 53 | gulp.task('compileDefaultStylesheets', function() { 54 | buildStyles(stylesDirectory + '/default/**/*.styl', 'form-for.css'); 55 | }); 56 | 57 | gulp.task('compileMaterialStylesheets', function() { 58 | buildStyles(stylesDirectory + '/material/**/*.styl', 'form-for.material.css'); 59 | }); 60 | 61 | gulp.task('clean', function() { 62 | var clean = require('gulp-clean'); 63 | 64 | return gulp.src(distDirectory ).pipe(clean()); 65 | }); 66 | 67 | gulp.task('map', function() { 68 | var shell = require('gulp-shell'); 69 | 70 | console.log('CWD: ' + process.cwd() + '/dist'); 71 | 72 | return shell.task( 73 | 'uglifyjs --compress --mangle --source-map form-for.min.js.map --source-map-root . -o form-for.min.js -- form-for.js', 74 | {cwd: process.cwd() + '/dist'} 75 | )(); 76 | }); 77 | 78 | gulp.task('test', function(callback) { 79 | runSequence( 80 | ['test:integration', 'test:unit'], 81 | callback); 82 | }); 83 | gulp.task('test:integration', function() { 84 | var shell = require('gulp-shell'); 85 | 86 | return shell.task( 87 | 'protractor protractor.conf.js', 88 | {cwd: process.cwd()} 89 | )(); 90 | }); 91 | gulp.task('test:unit', function() { 92 | // Be sure to return the stream 93 | return gulp.src(testFiles) 94 | .pipe(karma({ 95 | configFile: 'karma.conf.js', 96 | action: 'run' 97 | })) 98 | .on('error', function(error) { 99 | // Make sure failed tests cause gulp to exit non-zero 100 | throw error; 101 | }); 102 | }); 103 | 104 | gulp.task('travis', function(callback) { 105 | runSequence( 106 | 'clean', 107 | 'build', 108 | 'test:unit', 109 | callback); 110 | }); 111 | 112 | // TODO Compile and bundle Stylus styles 113 | 114 | gulp.task('uglify', function() { 115 | var fs = require('fs'); 116 | var uglifyJs = require('uglify-js2'); 117 | 118 | var code = fs.readFileSync('dist/form-for.js', 'utf8'); 119 | 120 | var parsed = uglifyJs.parse(code); 121 | parsed.figure_out_scope(); 122 | 123 | var compressed = parsed.transform(uglifyJs.Compressor()); 124 | compressed.figure_out_scope(); 125 | compressed.compute_char_frequency(); 126 | compressed.mangle_names(); 127 | 128 | var finalCode = compressed.print_to_string(); 129 | 130 | fs.writeFileSync('dist/form-for.min.js', finalCode); 131 | }); 132 | 133 | var buildHelper = function(sources, directory, outputFile) { 134 | var typeScriptCompiler = require('gulp-tsc'); 135 | var ngAnnotate = require('gulp-ng-annotate'); 136 | var replace = require('gulp-replace'); 137 | 138 | // TypeScript creates a lot of empty, IIAFs for some reason. 139 | // This pattern strips them out. 140 | var pattern = "\nvar formFor;\n(function (formFor) {\n ;\n})(formFor || (formFor = {}));\n;"; 141 | 142 | return gulp 143 | .src(sources) 144 | .pipe(typeScriptCompiler({ 145 | //declaration: true, // Generate *.d.ts declarations file as well 146 | module: "CommonJS", 147 | emitError: false, 148 | out: outputFile, 149 | target: 'ES5' 150 | })) 151 | .pipe(ngAnnotate()) 152 | .pipe(replace(pattern, '')) 153 | .pipe(gulp.dest(directory)); 154 | }; 155 | 156 | var buildStyles = function(sources, outputFileName) { 157 | var concat = require('gulp-concat'); 158 | var stylus = require('gulp-stylus'); 159 | var autoprefixer = require('autoprefixer-stylus'); 160 | var nib = require('nib'); 161 | 162 | return gulp.src(sources) 163 | .pipe(stylus({use: [nib(), autoprefixer()]})) 164 | .pipe(concat(outputFileName)) 165 | .pipe(gulp.dest(distDirectory)); 166 | }; 167 | 168 | var buildTemplatesHelper = function(templatesDirectory, moduleName, outputFile) { 169 | var concat = require('gulp-concat'); 170 | var ngAnnotate = require('gulp-ng-annotate'); 171 | var templateCache = require('gulp-angular-templatecache'); 172 | 173 | return gulp.src(templatesDirectory) 174 | .pipe( 175 | templateCache('templates.js', 176 | { 177 | module: moduleName, 178 | standalone: true, 179 | root: 'form-for/templates/' // Relative path for Directive :templateUrls 180 | })) 181 | .pipe(ngAnnotate()) 182 | .pipe(concat(outputFile)) 183 | .pipe(gulp.dest(distDirectory)) 184 | }; -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(config) { 4 | config.set({ 5 | autoWatch: true, 6 | basePath: '', 7 | frameworks: ['jasmine'], 8 | preprocessors: { 9 | 'templates/**/*.html': 'ng-html2js' 10 | }, 11 | // See https://github.com/karma-runner/karma-ng-html2js-preprocessor 12 | ngHtml2JsPreprocessor: { 13 | }, 14 | files: [ 15 | 'node_modules/jquery/dist/jquery.js', 16 | 'node_modules/angular/angular.js', 17 | 'node_modules/angular-mocks/angular-mocks.js', 18 | 'node_modules/jasmine-object-matchers/dist/jasmine-object-matchers.js', 19 | 'node_modules/jasmine-promise-matchers/dist/jasmine-promise-matchers.js', 20 | 'dist/form-for.js', 21 | 'tests/unit/**/*.js', 22 | 'templates/**/*.html' 23 | ], 24 | exclude: [], 25 | port: 9999, 26 | browsers: [ 27 | 'PhantomJS' 28 | ], 29 | plugins: [ 30 | 'karma-phantomjs-launcher', 31 | 'karma-jasmine', 32 | 'karma-ng-html2js-preprocessor' 33 | ], 34 | singleRun: false, 35 | colors: true, 36 | logLevel: config.LOG_INFO 37 | }); 38 | }; 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-form-for", 3 | "version": "4.1.12", 4 | "description": "Set of Angular directives to simplify creating and validating HTML forms.", 5 | "keywords": [ 6 | "angular", 7 | "form", 8 | "forms", 9 | "crud", 10 | "validation" 11 | ], 12 | "main": "gulpfile.js", 13 | "directories": { 14 | "test": "tests" 15 | }, 16 | "dependencies": {}, 17 | "devDependencies": { 18 | "angular": "~1.3.15", 19 | "angular-animate": "~1.3.15", 20 | "angular-aria": "~1.3.15", 21 | "angular-bootstrap-npm": "~0.12.3", 22 | "angular-material": "0.10.0", 23 | "angular-mocks": "~1.3.15", 24 | "angular-route": "^1.3.15", 25 | "angular-ui-router": "^0.2.13", 26 | "autoprefixer-stylus": "~0.5.0", 27 | "bootstrap": "^3.3.4", 28 | "doxx": "~1.2.5", 29 | "font-awesome": "^4.3.0", 30 | "gulp": "^3.9.0", 31 | "gulp-angular-protractor": "0.0.2", 32 | "gulp-angular-templatecache": "~1.5.0", 33 | "gulp-clean": "~0.3.1", 34 | "gulp-closure-deps": "~0.4.0", 35 | "gulp-concat": "~2.5.2", 36 | "gulp-doxx": "0.0.4", 37 | "gulp-karma": "0.0.4", 38 | "gulp-ng-annotate": "~0.5.2", 39 | "gulp-rename": "~1.2.0", 40 | "gulp-replace": "^0.5.3", 41 | "gulp-shell": "~0.3.0", 42 | "gulp-stylus": "~2.0.1", 43 | "gulp-tsc": "~0.9.2", 44 | "gulp-uglify": "~1.1.0", 45 | "gulp-umd": "~0.1.3", 46 | "html2js": "~0.1.1", 47 | "jasmine": "2.1.1", 48 | "jasmine-object-matchers": "0.0.3", 49 | "jasmine-promise-matchers": "0.0.5", 50 | "jquery": "~2.1.3", 51 | "karma": "~0.12.31", 52 | "karma-jasmine": "~0.3.5", 53 | "karma-jasmine-ajax": "~0.1.11", 54 | "karma-ng-html2js-preprocessor": "~0.1.2", 55 | "karma-phantomjs-launcher": "~0.1.4", 56 | "nib": "~1.1.0", 57 | "phantomjs": "~1.9.13", 58 | "protractor": "^2.0.0", 59 | "replace": "^0.3.0", 60 | "run-sequence": "~1.0.2", 61 | "uglify-js": "~2.4.17", 62 | "uglify-js2": "~2.1.11" 63 | }, 64 | "scripts": { 65 | "prestart": "npm install", 66 | "start": "http-server -p 8000", 67 | "unit": "gulp test:unit", 68 | "update-webdriver": "webdriver-manager update", 69 | "preprotractor": "npm run update-webdriver", 70 | "protractor": "protractor protractor.conf.js" 71 | }, 72 | "repository": { 73 | "type": "git", 74 | "url": "https://github.com/bvaughn/angular-form-for.git" 75 | }, 76 | "author": "Brian Vaughn ", 77 | "license": "MIT", 78 | "bugs": { 79 | "url": "https://github.com/bvaughn/angular-form-for/issues" 80 | }, 81 | "homepage": "https://github.com/bvaughn/angular-form-for" 82 | } 83 | -------------------------------------------------------------------------------- /protractor.conf.js: -------------------------------------------------------------------------------- 1 | var config = { 2 | allScriptsTimeout: 11000, 3 | 4 | capabilities: { 5 | browserName: 'chrome', 6 | shardTestFiles: true, 7 | maxInstances: 2 8 | }, 9 | 10 | specs: [ 11 | 'tests/integration/**/*.js' 12 | ], 13 | 14 | seleniumAddress: 'http://localhost:4444/wd/hub', 15 | baseUrl: 'http://localhost:8000/examples/', 16 | 17 | framework: 'jasmine2', 18 | rootElement: 'body', 19 | 20 | jasmineNodeOpts: { 21 | showColors: true, // Use colors in the command line report. 22 | defaultTimeoutInterval: 30000 23 | } 24 | }; 25 | 26 | if (process.env.TRAVIS_BUILD_NUMBER) { 27 | config.sauceUser = process.env.SAUCE_USERNAME; 28 | config.sauceKey = process.env.SAUCE_ACCESS_KEY; 29 | config.capabilities['tunnel-identifier'] = process.env.TRAVIS_JOB_NUMBER; 30 | config.capabilities.build = process.env.TRAVIS_BUILD_NUMBER; 31 | } 32 | 33 | exports.config = config; -------------------------------------------------------------------------------- /source/directives/aria-manager.ts: -------------------------------------------------------------------------------- 1 | module formFor { 2 | 3 | /** 4 | * Helper directive for input elements. 5 | * Observes the $scope :model attribute and updates aria-* elements accordingly. 6 | */ 7 | export class AriaManager implements ng.IDirective { 8 | 9 | restrict:string = 'A'; 10 | 11 | /* @ngInject */ 12 | link($scope:ng.IScope, $element:ng.IAugmentedJQuery, $attributes:ng.IAttributes):void { 13 | $scope.$watch('model.uid', function(uid) { 14 | $attributes.$set('ariaDescribedby', uid + '-error'); 15 | $attributes.$set('ariaLabelledby', uid + '-label'); 16 | }); 17 | 18 | $scope.$watch('model.error', function(error) { 19 | $attributes.$set('ariaInvalid', !!error); 20 | }); 21 | } 22 | } 23 | 24 | angular.module('formFor').directive('ariaManager', () => { 25 | return new AriaManager(); 26 | }); 27 | } -------------------------------------------------------------------------------- /source/directives/checkbox-field.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | module formFor { 5 | 6 | /** 7 | * CheckboxField $scope. 8 | */ 9 | interface CheckboxFieldScope extends ng.IScope { 10 | 11 | /** 12 | * Name of the attribute within the parent form-for directive's model object. 13 | * This attributes specifies the data-binding target for the input. 14 | * Dot notation (ex "address.street") is supported. 15 | */ 16 | attribute:string; 17 | 18 | /** 19 | * HTML field attributes; passed along so that custom view implementations can access custom parameters. 20 | */ 21 | attributes: any; 22 | 23 | /** 24 | * Optional function to be invoked on checkbox change. 25 | */ 26 | changed?:Function; 27 | 28 | /** 29 | * Disable input element. 30 | * Note the name is disable and not disabled to avoid collisions with the HTML5 disabled attribute. 31 | */ 32 | disable:boolean; 33 | 34 | /** 35 | * Optional help tooltip to display on hover. 36 | * By default this makes use of the Angular Bootstrap tooltip directive and the Font Awesome icon set. 37 | */ 38 | help?:string; 39 | 40 | /** 41 | * Optional field label displayed after the checkbox input. 42 | * Although not required, it is strongly suggested that you specify a value for this attribute. 43 | * HTML is allowed for this attribute. 44 | */ 45 | label?:string; 46 | 47 | /** 48 | * Optional class-name for field