├── .coveralls.yml ├── .eslintignore ├── .eslintrc ├── .travis.yml ├── CHANGELOG.md ├── README.md ├── docs ├── auto-formly-parsers.md └── auto-formly.md ├── lib ├── client │ ├── auto-formly-component.js │ ├── auto-formly-component.ng.html │ ├── auto-formly-helpers.js │ ├── auto-formly-parsers.js │ ├── auto-formly.js │ ├── formly-validator │ │ └── unique.js │ ├── main.js │ └── parsers │ │ ├── defaultvalue.js │ │ ├── key.js │ │ ├── template-options │ │ ├── label.js │ │ ├── max-date.js │ │ ├── min-date.js │ │ └── options.js │ │ ├── type.js │ │ ├── validation │ │ └── messages.js │ │ └── validators │ │ ├── allowed.js │ │ ├── maxlength.js │ │ ├── maxnumber.js │ │ ├── minlength.js │ │ ├── minnumber.js │ │ ├── pattern.js │ │ ├── required.js │ │ └── unique.js └── schema.js ├── package.js └── tests └── client ├── auto-formly-parsers-spec.js ├── auto-formly-spec.js ├── collection.js ├── parsers ├── defaultvalue-spec.js ├── key-spec.js ├── template-options │ ├── label-spec.js │ ├── max-date-spec.js │ └── min-date-spec.js ├── type-spec.js ├── validation │ └── messages-spec.js └── validators │ ├── allowed-spec.js │ ├── maxlength-spec.js │ ├── maxnumber-spec.js │ ├── minlength-spec.js │ ├── minnumber-spec.js │ ├── pattern-spec.js │ ├── required-spec.js │ └── unique-spec.js └── schema.js /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .meteor 2 | tests 3 | **/tests 4 | 5 | **/*.md 6 | **/*.html 7 | **/*.css 8 | **/*.json 9 | **/*.yaml 10 | **/*.yml -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": "eslint:recommended", 4 | "env": { 5 | "browser": true, 6 | "node": true, 7 | "meteor": true, 8 | "mongo": true, 9 | "jasmine": true, 10 | "es6": true 11 | }, 12 | "globals": { 13 | // app globals 14 | "autoFormlyHelpers": true, 15 | // thrid-party packages globals 16 | "SimpleSchema": false, 17 | "angular2now": false, 18 | "angular": false 19 | }, 20 | "rules": { 21 | "no-unused-vars": [ 22 | 1, 23 | { 24 | "vars": "all", 25 | "args": "after-used" 26 | } 27 | ], 28 | "curly": 2, 29 | // Require Following Curly Brace Conventions 30 | "eqeqeq": 2, 31 | // Require === and !== 32 | "no-alert": 2, 33 | // Disallow Use of Alert 34 | "no-caller": 2, 35 | // Disallow Use of caller/callee 36 | "no-else-return": 2, 37 | // Disallow return in else 38 | "no-eval": 2, 39 | // Disallow eval() 40 | "no-implied-eval": 2, 41 | // Disallow Implied eval() 42 | "no-labels": 2, 43 | // Disallow use of labeled statements 44 | "no-lone-blocks": 2, 45 | // Disallow Unnecessary Nested Blocks 46 | "no-loop-func": 2, 47 | // Disallow Functions in Loops 48 | "no-magic-numbers": 1, 49 | // Disallow Magic Numbers 50 | "no-multi-spaces": 2, 51 | // Disallow multiple spaces 52 | "no-native-reassign": 2, 53 | // Disallow Reassignment of Native Objects 54 | "no-new-func": 2, 55 | // Disallow Function Constructor 56 | "no-new-wrappers": 2, 57 | // Disallow Primitive Wrapper Instances 58 | "no-new": 2, 59 | // Disallow new For Side Effects 60 | "no-param-reassign": 2, 61 | // Disallow Reassignment of Function Parameters 62 | "no-proto": 2, 63 | // Disallow Use of __proto__ 64 | "no-return-assign": 2, 65 | // Disallow Assignment in return Statement 66 | "no-script-url": 2, 67 | // Disallow use of javascript: urls 68 | "no-self-compare": 2, 69 | // Disallow comparisons where both sides are exactly the same 70 | "no-throw-literal": 2, 71 | // Restrict what can be thrown as an exception 72 | "no-unused-expressions": 2, 73 | // Disallow usage of expressions in statement position 74 | "no-useless-call": 2, 75 | // Disallow unnecessary .call() and .apply() 76 | "no-useless-concat": 2, 77 | // Disallow unnecessary concatenation of literals or template literals 78 | "no-void": 2, 79 | // Disallow use of the void operator 80 | "no-with": 2, 81 | // No with statements 82 | "vars-on-top": 2, 83 | // Require Variable Declarations to be at the top of their scope 84 | "wrap-iife": 2, 85 | // Require immediate function invocation to be wrapped in parentheses 86 | "array-bracket-spacing": [ 87 | 2, 88 | "never" 89 | ], 90 | // Disallow or enforce spaces inside of brackets 91 | "block-spacing": [ 92 | 2, 93 | "always" 94 | ], 95 | // Disallow or enforce spaces inside of single line blocks 96 | "camelcase": [ 97 | 2, 98 | { 99 | "properties": "always" 100 | } 101 | ], 102 | // Require Camelcase 103 | "comma-spacing": [ 104 | 2, 105 | { 106 | "before": false, 107 | "after": true 108 | } 109 | ], 110 | // Enforces spacing around commas 111 | "computed-property-spacing": [ 112 | 2, 113 | "never" 114 | ], 115 | // Disallow or enforce spaces inside of computed properties 116 | "consistent-this": [ 117 | 2, 118 | "self" 119 | ], 120 | // Require Consistent This 121 | "max-nested-callbacks": [ 122 | 2, 123 | 3 124 | ], 125 | // Set Maximum Depth of Nested Callbacks 126 | "new-parens": 2, 127 | // Require Parens for Constructors 128 | "newline-after-var": 2, 129 | // Require or disallow an empty newline after variable declarations 130 | "no-array-constructor": 2, 131 | // Disallow creation of dense arrays using the Array constructor 132 | "no-inline-comments": 2, 133 | // Disallows comments after code. Comments must come on their own lines 134 | "no-multiple-empty-lines": [ 135 | 2, 136 | { 137 | "max": 2 138 | } 139 | ], 140 | // Disallows multiple blank lines 141 | "no-nested-ternary": 2, 142 | // Disallow Nested Ternaries 143 | "no-new-object": 2, 144 | // Disallow the use of the Object constructor 145 | "no-unneeded-ternary": 2, 146 | // Disallow conditional expressions that can be expressed with simpler constructs 147 | "object-curly-spacing": [ 148 | 2, 149 | "never" 150 | ], 151 | // Disallow or enforce spaces inside of curly braces in objects. 152 | "one-var": [ 153 | 2, 154 | "never" 155 | ], 156 | // Require or Disallow One Variable Declaration per Scope 157 | "wrap-regex": 2, 158 | // Require Regex Literals to be Wrapped 159 | "arrow-parens": [ 160 | 2, 161 | "always" 162 | ], 163 | // Require parens in arrow function arguments 164 | "constructor-super": 2, 165 | // Verify calls of super() in constructors 166 | "no-class-assign": 2, 167 | // Disallow modifying variables of class declarations 168 | "no-const-assign": 2, 169 | // Disallow modifying variables that are declared using const 170 | "no-dupe-class-members": 2, 171 | // Disallow duplicate name in class members 172 | "no-this-before-super": 2, 173 | // Disallow use of this/super before calling super() in constructors 174 | } 175 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.12" 4 | before_install: 5 | - "curl -L https://raw.githubusercontent.com/arunoda/travis-ci-meteor-packages/master/configure.sh | /bin/sh" 6 | - "npm install -g velocity-cli" 7 | before_script: 8 | - "export PATH=$HOME/.meteor:$PATH" 9 | script: 10 | - "velocity test-package ./ --ci" 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). 5 | 6 | [0.8.0](https://github.com/wieldo/meteor-autoformly/compare/v0.7.0...v0.8.0) - 2016-01-18 7 | 8 | ### Changed 9 | 10 | - at least wieldo:angular-formly-validator@1.5.0 11 | 12 | [0.7.0](https://github.com/wieldo/meteor-autoformly/compare/v0.6.0...v0.7.0) - 2015-12-30 13 | 14 | ### Added 15 | 16 | - Support for **Date** fields with **min** and **max** 17 | 18 | [0.6.0](https://github.com/wieldo/meteor-autoformly/compare/v0.5.1...v0.6.0) 19 | ---------------------------------------------------------------------------- 20 | 21 | ### Added 22 | 23 | - validation for `schema.allowedValues` 24 | 25 | ### Changed 26 | 27 | - BREAKING CHANGE: uses now **formly:angular-formly** instead of wieldo:angular-formly 28 | 29 | [0.5.1](https://github.com/wieldo/meteor-autoformly/compare/v0.5.0...v0.5.1) - 2015-11-25 30 | ----------------------------------------------------------------------------------------- 31 | 32 | ### Added 33 | 34 | - add info about options to autoFormly component's documentation 35 | 36 | [0.5.0](https://github.com/wieldo/meteor-autoformly/compare/v0.4.0...v0.5.0) - 2015-11-25 37 | ----------------------------------------------------------------------------------------- 38 | 39 | ### Added 40 | 41 | - `auto-formly` component to automate process of insertion or collection update 42 | 43 | [0.4.0](https://github.com/wieldo/meteor-autoformly/compare/v0.3.1...v0.4.0) - 2015-11-25 44 | ----------------------------------------------------------------------------------------- 45 | 46 | ### Added 47 | 48 | - `autoFormly.errors()` method to handle validation errors while inserting or updating collection object 49 | 50 | [0.3.1](https://github.com/wieldo/meteor-autoformly/compare/v0.3.0...v0.3.1) - 2015-11-23 51 | ----------------------------------------------------------------------------------------- 52 | 53 | ### Fixed 54 | 55 | - Missing support for **$meteor.collection()** 56 | 57 | [0.3.0](https://github.com/wieldo/meteor-autoformly/compare/v0.2.1...v0.3.0) - 2015-11-23 58 | ----------------------------------------------------------------------------------------- 59 | 60 | ### Added 61 | 62 | - Support autoformly.validation.messages in SimpleSchema 63 | - Add optional manual formly configuration for each field in autoFormly service 64 | - Implement more advanced filtering 65 | - `schema.autoformly.templateOptions.rows` to be displayed as textarea 66 | 67 | ### Deprecated 68 | 69 | - Filtering using schema keys as array 70 | 71 | ### Fixed 72 | 73 | - Prevent templatesObject.label overwriting 74 | 75 | [0.2.1](https://github.com/wieldo/meteor-autoformly/compare/v0.2.0...v0.2.1) - 2015-11-18 76 | ----------------------------------------------------------------------------------------- 77 | 78 | ### Changed 79 | 80 | - Refactor all files to use Strict Dependency Injection 81 | 82 | [0.2.0](https://github.com/wieldo/meteor-autoformly/compare/v0.1.0...v0.2.0) - 2015-11-18 83 | ----------------------------------------------------------------------------------------- 84 | 85 | ### Added 86 | 87 | - Support for ***Boolean*** type (as checkbox) 88 | - Support for allowedValues with ***String*** type (as select) 89 | 90 | [0.1.0](https://github.com/wieldo/meteor-autoformly/compare/v0.0.1...v0.1.0) - 2015-11-15 91 | ----------------------------------------------------------------------------------------- 92 | 93 | ### Added 94 | 95 | - Support for default values 96 | - Support for schema.autoformly.type 97 | - Support for schema.regEx 98 | - Tests for all parsers 99 | 100 | 0.0.1 - 2015-11-14 101 | ------------------ 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | AutoFormly 2 | ========== 3 | 4 | [![GitHub version](https://badge.fury.io/gh/wieldo%2Fmeteor-autoformly.svg)](https://badge.fury.io/gh/wieldo%2Fmeteor-autoformly) 5 | 6 | [![Build Status](https://travis-ci.org/wieldo/meteor-autoformly.svg)](https://travis-ci.org/wieldo/meteor-autoformly) 7 | 8 | [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/wieldo/meteor-autoformly?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 9 | 10 | [![Coverage Status](https://coveralls.io/repos/wieldo/meteor-autoformly/badge.svg?branch=master&service=github)](https://coveralls.io/github/wieldo/meteor-autoformly?branch=master) 11 | 12 | [![Codacy Badge](https://api.codacy.com/project/badge/grade/b55750daf3c6417caf63154da85a9eae)](https://www.codacy.com/app/mys-sterowiec/meteor-autoformly) 13 | 14 | Create [Angular-Formly](http://angular-formly.com) forms with automatic insert and update, and automatic reactive validation. Requires [SimpleSchema](http://github.com/aldeed/meteor-simple-schema) or [Collection2](http://github.com/aldeed/meteor-collection2). 15 | 16 | Installation 17 | ------------ 18 | 19 | ``` 20 | meteor add wieldo:autoformly 21 | ``` 22 | 23 | How to use it 24 | ------------- 25 | 26 | Add autoFormly and angular-formly templates to your angular module. 27 | 28 | ```javascript 29 | angular.module('app', [ 30 | 'autoFormly', 31 | 'formlyMaterial' // or other angular-formly templates 32 | ]); 33 | ``` 34 | 35 | ### Templates 36 | 37 | As you can see above, I'm using [angular-formly-templates-material](http://github.com/formly-js/angular-formly-templates-material) which I also maintain. You can add it using `meteor add formly:angular-formly-templates-material`. 38 | 39 | ### Be aware 40 | 41 | ``` 42 | Be aware that not all angular-formly templates have same API 43 | 44 | It is not yet a stable version 45 | 46 | If you found some mismatch with other templates, post an issue 47 | ``` 48 | 49 | ### auto-formly component 50 | 51 | To insert or update collection 52 | ```html 53 | 59 | 60 | 61 | ``` 62 | 63 | - **collection** Mongo.Collection object 64 | - **doc** document from collection (optional) 65 | - **options** same object as in autoFormly.collection (to custom configuration and filtering) (optional) 66 | - **onSuccess** callback with result of action as argument (optional) 67 | - **onError** callback with error as argument (false if form contains errors on client-side) (optional) 68 | 69 | ### Convert all schema fields 70 | 71 | ```javascript 72 | const fields = autoFormly.collection(BooksCollection); 73 | // or 74 | const fields = autoFormly.collection($meteor.collection(BooksCollection); 75 | // or 76 | const fields = autoFormly.schema(BooksSchema); 77 | ``` 78 | 79 | ### Save object to collection with validation error handling 80 | 81 | ```javascript 82 | const fields = autoFormly.collection(BooksCollection); 83 | 84 | function submit(book) { 85 | $meteor.collection(BooksCollection) 86 | .save(book) 87 | .then(() => { 88 | // success 89 | }) 90 | .catch(() => autoFormly.errors(BooksCollection, fields); 91 | } 92 | ``` 93 | 94 | ### Convert all schema fields excluding one 95 | 96 | ```javascript 97 | const fields = autoFormly.schema(BooksSchema, { 98 | fields: { 99 | published: false 100 | } 101 | }); 102 | ``` 103 | 104 | ### Convert selected collection fields 105 | 106 | ```javascript 107 | const fields = autoFormly.collection(BooksCollection, { 108 | all: false, 109 | fields: { 110 | published: true, 111 | author: true, 112 | title: true 113 | } 114 | }); 115 | ``` 116 | 117 | ### Extend formly configuration for selected field 118 | 119 | ```javascript 120 | const fields = autoFormly.schema(BooksSchema, { 121 | all: false, 122 | fields: { 123 | published: true, 124 | author: { 125 | templateOptions: { 126 | label: "Written by" 127 | } 128 | } 129 | } 130 | }); 131 | ``` 132 | 133 | #### Helpers 134 | 135 | We're currently working on three other packages that are very useful in autoFormly. 136 | 137 | - [formlyTransformer](http://github.com/wieldo/angular-formly-transformer) to simplify process of formly field transformation. 138 | - [formlyValidator](http://github.com/wieldo/angular-formly-validator) to make validation easier (with built-in validators) 139 | - [formlyMaterial](http://github.com/formly-js/angular-formly-templates-material) is a AngularJS module with Angular Material templates to use in angular-formly. 140 | 141 | What is ready? 142 | -------------- 143 | 144 | See examples above. 145 | 146 | - creating formly fields with validators using collection or schema (autoFormly.collection(), autoFormly.schema()) 147 | - handling validation errors (autoFormly.errors() sets validation on form fields) 148 | 149 | Take a look at this docs: 150 | 151 | - [autoFormly](docs/auto-formly.md) 152 | - [autoFormlyParsers](docs/auto-formly-parsers.md) 153 | 154 | Contributing 155 | ------------ 156 | 157 | It is a new project, at the beginning of development process. 158 | 159 | Feel free to ask me anything. 160 | 161 | ### You can help 162 | 163 | If you would like to add functionality, just fork this repo and create pull request. 164 | 165 | ### How autoFormly works 166 | 167 | Basically it parses simpleSchema structure and creates formly configuration for each field. 168 | 169 | For example, to mark field as required we can create the parser function to check if `optional` property is being used. If *opional* is not set to *true* then we're adding `required` validator from `wieldo:angular-formly-validator` package (see [formlyValidator](http://github.com/wieldo/angular-formly-validator) and source code in [required.js](lib/client/parsers/validators/required.js). 170 | 171 | #### What is parser? 172 | 173 | Parser is a function that receives simpleSchema key with configuration and reference of formly field configuration object. 174 | 175 | So basically, you can add properties to formly configuration by checking field's schema. 176 | 177 | ### Roadmap 1.0 178 | 179 | - [ ] Component to automate process of insertion or collection update 180 | - [ ] `schema.minCount` and `schema.maxCount` support 181 | - [ ] Support for ***Object*** type fields 182 | - [ ] Support for ***array of objects*** 183 | - [ ] Interactive **demo** 184 | - [x] Extend SimpleSchema to use `autoformly` property 185 | - [x] Add optional manual formly configuration for each field 186 | - [x] More advanced field filtering (show all / hide all / add excluding) 187 | - [x] `schema.key` as `formly.key` 188 | - [x] `schema.label` as `formly.templateOptions.label` 189 | - [x] `schema.optional` and required validator 190 | - [x] `schema.max` for ***String*** and ***Number*** types as **maxlength** and **maxnumber** validator 191 | - [x] `schema.min` for ***String*** and ***Number*** types as **minlength** and **minnumber** validator 192 | - [x] `schema.regEx` as **pattern** validator 193 | - [x] `schema.defaultValue` as `formly.defaultValue` 194 | - [x] `schema.autoformly.templateOptions.rows` to be displayed as textarea 195 | - [x] ***Boolean*** type as checkbox 196 | - [x] `schema.autoformly.type` to be `formly.type` 197 | - [x] `schema.allowedValues` as select element (schema type is a String) 198 | - [x] validation for `schema.allowedValues` 199 | - [x] Support for ***Date*** type fields with min and max 200 | - [x] Support for server-side validation errors (like *unique*\) 201 | 202 | Contact 203 | ------- 204 | 205 | You can find me on [Gitter](https://gitter.im/wieldo/meteor-autoformly). 206 | -------------------------------------------------------------------------------- /docs/auto-formly-parsers.md: -------------------------------------------------------------------------------- 1 | autoFormlyParsers 2 | ========== 3 | 4 | ``` 5 | AngularJS service. 6 | ``` 7 | 8 | __File: [lib/client/auto-formly-parsers.js](../lib/client/auto-formly-parsers.js)__ 9 | 10 | - 11 | 12 | ### *autoFormlyParsers*.register(parser) 13 | 14 | ``` 15 | Register new parsing function 16 | ``` 17 | 18 | __Arguments__ 19 | 20 | * __collection__ *{Function}* 21 | 22 | Parsing function with three arguments: 23 | 24 | - *{string}* field's key 25 | - *{object}* copy of field's schema from SimpleSchema object 26 | - *{object}* object with formly configuration for field 27 | 28 | 29 | __Returns__ *{undefined}* 30 | 31 | There is no need to return something. Use formly configuration object as reference. 32 | -------------------------------------------------------------------------------- /docs/auto-formly.md: -------------------------------------------------------------------------------- 1 | autoFormly 2 | ========== 3 | 4 | ``` 5 | AngularJS service. 6 | ``` 7 | 8 | __File: [lib/client/auto-formly.js](../lib/client/auto-formly.js)__ 9 | 10 | - 11 | 12 | ### *autoFormly*.collection(collection, fields) 13 | 14 | ``` 15 | Parse Mongo.Collection to get array with fields 16 | ``` 17 | 18 | __Arguments__ 19 | 20 | * __collection__ *{CollectionCollection|AngularMeteorCollection}* 21 | 22 | Mongo Collection used directly or with $meteor AngularJS service 23 | 24 | * __options__ *{array|object|undefined}* 25 | 26 | Optional filtering fields using array with collection's keys 27 | or options object. 28 | 29 | * __options.all__ *{boolean}* 30 | 31 | Show all fields when true. Otherwise only these defined (see below) 32 | 33 | * __options.fields__ *{object}* 34 | 35 | Object with the field's key as property and formly field configuration (extends the auto-generated configuration). 36 | Instead of using formly configuration you can set field as visible or not using boolean value (true visible, false not used) 37 | 38 | 39 | __Returns__ *{Array}* 40 | 41 | Array with fields to use in formly-form. 42 | 43 | - 44 | 45 | ### *autoFormly*.schema(schema, options) 46 | 47 | ``` 48 | Parse SimpleSchema object to get array with fields 49 | ``` 50 | 51 | __Arguments__ 52 | 53 | * __schema__ *{SimpleSchema}* 54 | 55 | SimpleSchema instance 56 | 57 | * __options__ *{array|object|undefined}* 58 | 59 | See collection() 60 | 61 | __Returns__ *{Array}* 62 | 63 | Array with fields to use in formly-form. 64 | 65 | - 66 | 67 | ### *autoFormly*.errors(collection, fields) 68 | 69 | ``` 70 | Gets errors of fields and sets them in form controls. Use it when insertion or collection update fails. 71 | 72 | See example in readme. 73 | ``` 74 | 75 | __Arguments__ 76 | 77 | * __collection__ *{SimpleSchema}* 78 | 79 | SimpleSchema instance 80 | 81 | * __fields__ *{array}* 82 | 83 | Array with fields 84 | -------------------------------------------------------------------------------- /lib/client/auto-formly-component.js: -------------------------------------------------------------------------------- 1 | const {SetModule, Component, View,Inject} = angular2now; 2 | 3 | SetModule('autoFormly'); 4 | @Component({ 5 | selector: 'auto-formly', 6 | bind: { 7 | collection: '=', 8 | options: '=?', 9 | doc: '=?', 10 | onSuccess: '=?', 11 | onError: '=?' 12 | } 13 | }) 14 | @View({ 15 | templateUrl: angularTemplateUrl('wieldo:autoformly', 'lib/client/auto-formly-component.ng.html'), 16 | transclude: true 17 | }) 18 | @Inject(['autoFormly', '$meteor']) 19 | class autoFormlyComponent extends autoFormlyHelpers { 20 | // injectables 21 | autoFormly; 22 | $meteor; 23 | 24 | /** 25 | * collection (with set and get) 26 | */ 27 | _collection; 28 | /** 29 | * options (with set and get) 30 | */ 31 | _options; 32 | /** 33 | * document (with set and get) 34 | */ 35 | _doc; 36 | /** 37 | * success callback (with set and get) 38 | */ 39 | _onSuccess; 40 | /** 41 | * error callback (with set and get) 42 | */ 43 | _onError; 44 | /** 45 | * auto-bound array with formly fields 46 | */ 47 | formFields; 48 | /** 49 | * auto-bound object with formly model 50 | */ 51 | formModel; 52 | /** 53 | * auto-bound object with formly options 54 | */ 55 | formOptions; 56 | /** 57 | * auto-bound object with formly form 58 | */ 59 | form; 60 | /** 61 | * auto-bound string with form name 62 | */ 63 | formName; 64 | 65 | constructor(autoFormly, $meteor) { 66 | super(); 67 | this.autoFormly = autoFormly; 68 | this.$meteor = $meteor; 69 | 70 | this.formFields = this.createFormFields(); 71 | this.formModel = this.createFormModel(); 72 | } 73 | 74 | /** 75 | * Create fields based on collection and options 76 | * @returns {array} 77 | */ 78 | createFormFields() { 79 | return this.autoFormly.collection(this.collection, this.options); 80 | } 81 | 82 | /** 83 | * Create model based on document 84 | * @returns {Object} 85 | */ 86 | createFormModel() { 87 | return this.doc || {}; 88 | } 89 | 90 | /** 91 | * Submit form and save form model in collection 92 | */ 93 | submit() { 94 | if (!this.form.$valid) { 95 | this.callError(false); 96 | return; 97 | } 98 | 99 | this.$meteor.collection(this.getCollection(this.collection), false) 100 | .save(this.formModel) 101 | .then((result) => { 102 | // reset model 103 | this.formModel = {}; 104 | // reset form 105 | this.form.$setPristine(); 106 | this.form.$setUntouched(); 107 | // callback 108 | this.callSuccess(result); 109 | }) 110 | .catch((error) => { 111 | // set validity on fields 112 | this.autoFormly.errors(this.collection, this.formFields); 113 | // callback 114 | this.callError(error); 115 | }); 116 | } 117 | 118 | // 119 | // collection 120 | // 121 | set collection(collection) { 122 | this._collection = collection; 123 | } 124 | 125 | get collection() { 126 | return this._collection; 127 | } 128 | 129 | // 130 | // options 131 | // 132 | set options(options) { 133 | if (!angular.isObject(options)) { 134 | throw this.createError('Invalid options. Expected object'); 135 | } 136 | this._options = options; 137 | } 138 | 139 | get options() { 140 | return this._options; 141 | } 142 | 143 | // 144 | // document 145 | // 146 | set doc(doc) { 147 | if (!angular.isObject(doc)) { 148 | throw this.createError('Invalid document. Expected object'); 149 | } 150 | this._doc = angular.copy(doc); 151 | } 152 | 153 | get doc() { 154 | return this._doc; 155 | } 156 | 157 | // 158 | // onSuccess 159 | // 160 | set onSuccess(onSuccess) { 161 | if (!angular.isFunction(onSuccess)) { 162 | throw this.createError("Invalid success callback. Expected function"); 163 | } 164 | this._onSuccess = onSuccess; 165 | } 166 | 167 | get onSuccess() { 168 | return this._onSuccess; 169 | } 170 | 171 | /** 172 | * call success callback if defined 173 | * @param {Object|Array} result 174 | */ 175 | callSuccess(result) { 176 | if (this.onSuccess) { 177 | this.onSuccess(result); 178 | } 179 | } 180 | 181 | // 182 | // onError 183 | // 184 | set onError(onError) { 185 | if (!angular.isFunction(onError)) { 186 | throw this.createError("Invalid error callback. Expected function"); 187 | } 188 | this._onError = onError; 189 | } 190 | 191 | get onError() { 192 | return this._onError; 193 | } 194 | 195 | /** 196 | * call error callback if defined 197 | * @param {boolean|Error} error 198 | */ 199 | callError(error) { 200 | if (this.onError) { 201 | this.onError(error); 202 | } 203 | } 204 | } -------------------------------------------------------------------------------- /lib/client/auto-formly-component.ng.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 |
-------------------------------------------------------------------------------- /lib/client/auto-formly-helpers.js: -------------------------------------------------------------------------------- 1 | autoFormlyHelpers = class autoFormlyHelpers { 2 | createError(msg) { 3 | return new Error(`[AutoFormly] ${msg}`); 4 | } 5 | 6 | createNestedObject(obj, path) { 7 | path = this.pathNestedObject(path); 8 | for (let i = 0; i < path.length; i++) { 9 | obj = obj[path[i]] = obj[path[i]] || {}; 10 | } 11 | } 12 | 13 | getNestedObject(obj, path) { 14 | path = this.pathNestedObject(path); 15 | let objCopy = angular.copy(obj); 16 | for (let i = 0; i < path.length; i++) { 17 | if (!objCopy.hasOwnProperty(path[i])) { 18 | objCopy = undefined; 19 | break; 20 | } else { 21 | objCopy = objCopy[path[i]]; 22 | } 23 | } 24 | 25 | return objCopy; 26 | } 27 | 28 | pathNestedObject(path) { 29 | if ("string" === typeof path) { 30 | path = path.split('.'); 31 | } 32 | return path; 33 | } 34 | 35 | getSchema(collection) { 36 | if (!collection) { 37 | return; 38 | } 39 | 40 | // Mongo Collection 41 | if (angular.isFunction(collection.simpleSchema)) { 42 | return collection.simpleSchema(); 43 | } 44 | 45 | // AngularMeteorCollection 46 | if (collection.$$collection && angular.isFunction(collection.$$collection.simpleSchema)) { 47 | return collection.$$collection.simpleSchema(); 48 | } 49 | 50 | return; 51 | } 52 | 53 | getCollection(collection) { 54 | if (this.isCollection(collection)) { 55 | return collection; 56 | } 57 | 58 | if (this.isAngularMeteorCollection(collection)) { 59 | return collection.$$collection; 60 | } 61 | 62 | return; 63 | } 64 | 65 | isCollection(collection) { 66 | return collection && collection._c2; 67 | } 68 | 69 | isAngularMeteorCollection(collection) { 70 | return collection && collection.$$collection && this.isCollection(collection.$$collection); 71 | } 72 | 73 | isSchema(schema) { 74 | return schema && schema instanceof SimpleSchema; 75 | } 76 | }; -------------------------------------------------------------------------------- /lib/client/auto-formly-parsers.js: -------------------------------------------------------------------------------- 1 | /* global autoFormlyHelpers */ 2 | let {SetModule, Service} = angular2now; 3 | 4 | SetModule('autoFormly'); 5 | @Service('autoFormlyParser') 6 | class autoFormlyParser extends autoFormlyHelpers { 7 | 8 | parsers = []; 9 | 10 | constructor() { 11 | super(); 12 | } 13 | 14 | /** 15 | * register parser function. 16 | * Parser arguments: 17 | * - {string} fieldKey - absolute name of SimpleSchema field 18 | * - {object} fieldSchema - field's schema from SimpleSchema object 19 | * - {object} formlyField - formly field configuration object 20 | * 21 | * @public 22 | * @param {function} parser 23 | */ 24 | register(parser) { 25 | if (!angular.isFunction(parser)) { 26 | throw this.createError("Parser has to be a function"); 27 | } 28 | 29 | // push new parser 30 | this.parsers.push(parser); 31 | } 32 | 33 | /** 34 | * 35 | * @public 36 | * @param {Object} schema 37 | * @returns {array} Array of field configuration objects 38 | */ 39 | schema(schema) { 40 | let fieldKey; 41 | const fields = []; 42 | 43 | for (fieldKey in schema) { 44 | fields.push(this.field(fieldKey, schema[fieldKey])); 45 | } 46 | 47 | // remove undefined and without type 48 | return _.reject(_.compact(fields), (field) => "undefined" === typeof field.type); 49 | } 50 | 51 | /** 52 | * 53 | * @param {string} fieldKey 54 | * @param {Object} fieldSchema 55 | * @returns {Object} 56 | */ 57 | field(fieldKey, fieldSchema) { 58 | // todo: support for array of objects 59 | // maybe something like foo.$.bar -> foo[$key]bar will do the work 60 | if (fieldKey.indexOf('$') !== -1) { 61 | return; 62 | } 63 | 64 | const formlyField = fieldSchema.autoformly ? fieldSchema.autoformly : {}; 65 | 66 | this.runParsers(fieldKey, fieldSchema, formlyField); 67 | 68 | return formlyField; 69 | } 70 | 71 | /** 72 | * 73 | * @public 74 | * @param {string} fieldKey 75 | * @param {object} fieldSchema 76 | * @param {object} formlyField 77 | */ 78 | runParsers(fieldKey, fieldSchema, formlyField) { 79 | this.parsers.forEach((parser) => { 80 | // call with deep copy of fieldSchema to avoid overwriting 81 | parser.call({}, fieldKey, angular.copy(fieldSchema), formlyField); 82 | }); 83 | } 84 | } -------------------------------------------------------------------------------- /lib/client/auto-formly.js: -------------------------------------------------------------------------------- 1 | /* global autoFormlyHelpers */ 2 | let {SetModule, Service, Inject} = angular2now; 3 | 4 | SetModule('autoFormly'); 5 | @Service('autoFormly') 6 | @Inject(['autoFormlyParser']) 7 | /** 8 | * AngularJS Service 9 | * @property autoFormly 10 | * @public 11 | */ 12 | class autoFormly extends autoFormlyHelpers { 13 | 14 | // injectables 15 | autoFormlyParser; 16 | 17 | constructor(autoFormlyParser) { 18 | super(); 19 | 20 | this.autoFormlyParser = autoFormlyParser; 21 | } 22 | 23 | /** 24 | * Parse Mongo.Collection object 25 | * 26 | * @public 27 | * @method autoFormly.collection 28 | * @param {Collection|AngularMeteorCollection} collection - used directly or with $meteor service 29 | * @param {array|object} options - schema keys you want to use or formly fields configuration (optional) 30 | * @returns {array} 31 | */ 32 | collection(collection, options) { 33 | if (!this.isCollection(collection) && !this.isAngularMeteorCollection(collection)) { 34 | throw this.createError("Collection is not extended by Collection2"); 35 | } 36 | 37 | let schema = this.getSchema(this.getCollection(collection)); 38 | 39 | if (!schema) { 40 | throw this.createError("Collection has no schema"); 41 | } 42 | 43 | return this.schema(schema, options); 44 | } 45 | 46 | /** 47 | * Parse SchemaSchema object 48 | * 49 | * @public 50 | * @method autoFormly.schema 51 | * @param {SimpleSchema} schema 52 | * @param {array|object} options - schema keys you want to use or formly fields configuration (optional) 53 | * @returns {array} 54 | */ 55 | schema(schema, options) { 56 | if (!this.isSchema(schema)) { 57 | throw this.createError("Schema has to be instance of SimpleSchema"); 58 | } 59 | 60 | let sortedSchema; 61 | const schemaCopy = angular.copy(schema); 62 | 63 | /** 64 | * @deprecated avoid filtering with fields keys as array, use object configuration instead 65 | */ 66 | // filter and return 67 | if (angular.isArray(options)) { 68 | return this.fields(this.filterSchema(schemaCopy, options)); 69 | } 70 | 71 | // no options 72 | if (angular.isUndefined(options)) { 73 | return this.fields(schemaCopy.schema()); 74 | } 75 | 76 | // 77 | // handle options object 78 | // 79 | 80 | // pass only object 81 | if (!angular.isObject(options)) { 82 | throw this.createError("Options have to be an array or object"); 83 | } 84 | 85 | // use filter? 86 | if (options.all === false) { 87 | // pass only with defined fields 88 | if (!angular.isObject(options.fields)) { 89 | throw this.createError("Missing or invalid property fields in options"); 90 | } 91 | // skip fields marked as unused ("fieldKey": false) 92 | sortedSchema = this.filterSchema(schemaCopy, Object.keys(_.omit(options.fields, (val) => val === false))); 93 | } else { 94 | sortedSchema = schemaCopy.schema(); 95 | } 96 | 97 | // check each field 98 | if (options.fields) { 99 | _.each(sortedSchema, (fSchema, fKey) => { 100 | // and skip field if marked as unused 101 | if (options.fields[fKey] === false) { 102 | delete sortedSchema[fKey]; 103 | } 104 | // or extend autoformly in schema using defined field extension 105 | if (angular.isObject(options.fields[fKey])) { 106 | sortedSchema[fKey].autoformly = angular.merge({}, fSchema.autoformly, options.fields[fKey]); 107 | } 108 | }); 109 | } 110 | 111 | return this.fields(sortedSchema); 112 | } 113 | 114 | /** 115 | * @param {object} schema 116 | * @param {array} fields - schema keys you want to use (optional) 117 | * @returns {object} 118 | */ 119 | filterSchema(schema, fields) { 120 | if (!angular.isArray(fields)) { 121 | throw this.createError("Fields to filter have to be an array"); 122 | } 123 | 124 | const sorted = {}; 125 | 126 | fields.forEach((field) => { 127 | if (!schema.schema(field)) { 128 | throw this.createError(`There is no '${field}' in schema`); 129 | } 130 | sorted[field] = schema.schema(field); 131 | }); 132 | 133 | return sorted; 134 | } 135 | 136 | /** 137 | * @param {object} sortedSchema 138 | * @returns {array} 139 | */ 140 | fields(sortedSchema) { 141 | return this.autoFormlyParser.schema(sortedSchema); 142 | } 143 | 144 | /** 145 | * Handle errors and set validity on fields 146 | * 147 | * @public 148 | * @param {Collection|AngularMeteorCollection} collection 149 | * @param {array} fields formly fields 150 | * @param {string} context validation context 151 | */ 152 | errors(collection, fields, context) { 153 | // get things 154 | let col = this.getCollection(collection); 155 | let schema = this.getSchema(collection); 156 | 157 | // make sure fields variable is an array 158 | if (!angular.isArray(fields)) { 159 | throw this.createError("Missing or invalid fields"); 160 | } 161 | 162 | // check collection 163 | if (!col) { 164 | throw this.createError("Collection is not extended by Collection2"); 165 | } 166 | 167 | // check schema 168 | if (!schema) { 169 | throw this.createError("Collection has no schema"); 170 | } 171 | 172 | // get keys with error types 173 | const invalidKeys = schema.namedContext(context).invalidKeys(); 174 | // transform error type from SimpleSchema to autoFormly 175 | const typeTransform = { 176 | notUnique: "unique", 177 | minString: "minlength", 178 | maxString: "maxlength", 179 | minNumber: "minnumber", 180 | maxNumber: "maxnumber", 181 | regEx: "pattern" 182 | }; 183 | 184 | // go through errors 185 | invalidKeys.forEach((key) => { 186 | // match error with field object 187 | let field = _.find(fields, (field) => field.key === key.name); 188 | 189 | // if found 190 | if (field) { 191 | if (!field.formControl) { 192 | throw this.createError(`Field "${field.key}" has no formControl`); 193 | } 194 | // and has formControl property 195 | field.formControl.$setValidity(typeTransform[key.type] || key.type, false); 196 | } 197 | }); 198 | } 199 | } -------------------------------------------------------------------------------- /lib/client/formly-validator/unique.js: -------------------------------------------------------------------------------- 1 | const {SetModule} = angular2now; 2 | 3 | SetModule('autoFormly').run(['formlyValidator', (formlyValidator) => { 4 | formlyValidator.register('unique', () => { 5 | return true; 6 | }); 7 | }]); -------------------------------------------------------------------------------- /lib/client/main.js: -------------------------------------------------------------------------------- 1 | angular2now.options({controllerAs: 'vm'}); 2 | const {SetModule} = angular2now; 3 | 4 | SetModule('autoFormly', ['angular-meteor', 'formlyValidator']); -------------------------------------------------------------------------------- /lib/client/parsers/defaultvalue.js: -------------------------------------------------------------------------------- 1 | var {SetModule} = angular2now; 2 | 3 | SetModule('autoFormly') 4 | .run(['autoFormlyParser', (autoFormlyParser) => { 5 | 6 | autoFormlyParser.register((fieldKey, fieldSchema, formlyField) => { 7 | if (angular.isDefined(fieldSchema.defaultValue)) { 8 | formlyField.defaultValue = fieldSchema.defaultValue; 9 | } 10 | }); 11 | 12 | }]); -------------------------------------------------------------------------------- /lib/client/parsers/key.js: -------------------------------------------------------------------------------- 1 | var {SetModule} = angular2now; 2 | 3 | SetModule('autoFormly') 4 | .run(['autoFormlyParser', (autoFormlyParser) => { 5 | 6 | autoFormlyParser.register((fieldKey, fieldSchema, formlyField) => { 7 | formlyField.key = fieldKey; 8 | }); 9 | 10 | }]); -------------------------------------------------------------------------------- /lib/client/parsers/template-options/label.js: -------------------------------------------------------------------------------- 1 | var {SetModule} = angular2now; 2 | 3 | SetModule('autoFormly') 4 | .run(['autoFormlyParser', (autoFormlyParser) => { 5 | 6 | autoFormlyParser.register((fieldKey, fieldSchema, formlyField) => { 7 | autoFormlyParser.createNestedObject(formlyField, 'templateOptions'); 8 | 9 | // prevent overwriting 10 | if (!autoFormlyParser.getNestedObject(formlyField, 'templateOptions.label')) { 11 | formlyField.templateOptions.label = fieldSchema.label ? fieldSchema.label : fieldKey; 12 | } 13 | }); 14 | 15 | }]); 16 | -------------------------------------------------------------------------------- /lib/client/parsers/template-options/max-date.js: -------------------------------------------------------------------------------- 1 | const {SetModule} = angular2now; 2 | 3 | SetModule('autoFormly') 4 | .run(['autoFormlyParser', (autoFormlyParser) => { 5 | 6 | autoFormlyParser.register((fieldKey, fieldSchema, formlyField) => { 7 | if (!formlyField.templateOptions) { 8 | formlyField.templateOptions = {}; 9 | } 10 | 11 | if (formlyField.type === 'datepicker' && angular.isDefined(fieldSchema.max)) { 12 | formlyField.templateOptions.maxDate = fieldSchema.max; 13 | } 14 | }); 15 | 16 | }]); 17 | -------------------------------------------------------------------------------- /lib/client/parsers/template-options/min-date.js: -------------------------------------------------------------------------------- 1 | const {SetModule} = angular2now; 2 | 3 | SetModule('autoFormly') 4 | .run(['autoFormlyParser', (autoFormlyParser) => { 5 | 6 | autoFormlyParser.register((fieldKey, fieldSchema, formlyField) => { 7 | if (!formlyField.templateOptions) { 8 | formlyField.templateOptions = {}; 9 | } 10 | 11 | if (formlyField.type === 'datepicker' && angular.isDefined(fieldSchema.min)) { 12 | formlyField.templateOptions.minDate = fieldSchema.min; 13 | } 14 | }); 15 | 16 | }]); 17 | -------------------------------------------------------------------------------- /lib/client/parsers/template-options/options.js: -------------------------------------------------------------------------------- 1 | var {SetModule} = angular2now; 2 | 3 | SetModule('autoFormly') 4 | .run(['autoFormlyParser', (autoFormlyParser) => { 5 | 6 | autoFormlyParser.register((fieldKey, fieldSchema, formlyField) => { 7 | if (!formlyField.templateOptions) { 8 | formlyField.templateOptions = {}; 9 | } 10 | 11 | if (formlyField.type === 'select' && angular.isDefined(fieldSchema.allowedValues)) { 12 | formlyField.templateOptions.options = fieldSchema.allowedValues.map((value) => { 13 | return { 14 | name: value, 15 | value: value 16 | } 17 | }); 18 | } 19 | }); 20 | 21 | }]); 22 | -------------------------------------------------------------------------------- /lib/client/parsers/type.js: -------------------------------------------------------------------------------- 1 | var {SetModule} = angular2now; 2 | 3 | SetModule('autoFormly') 4 | .run(['autoFormlyParser', (autoFormlyParser) => { 5 | 6 | autoFormlyParser.register((fieldKey, fieldSchema, formlyField) => { 7 | 8 | if (autoFormlyParser.getNestedObject(fieldSchema, 'autoformly.type')) { 9 | formlyField.type = fieldSchema.autoformly.type; 10 | return; 11 | } 12 | 13 | if (fieldSchema.type === String) { 14 | 15 | if (angular.isDefined(fieldSchema.allowedValues)) { 16 | formlyField.type = 'select'; 17 | } else if (autoFormlyParser.getNestedObject(fieldSchema, 'autoformly.templateOptions.rows')) { 18 | formlyField.type = 'textarea'; 19 | } else { 20 | formlyField.type = 'input'; 21 | } 22 | return; 23 | } 24 | 25 | if (fieldSchema.type === Number) { 26 | formlyField.type = 'input'; 27 | return; 28 | } 29 | 30 | if (fieldSchema.type === Date) { 31 | formlyField.type = 'datepicker'; 32 | return; 33 | } 34 | 35 | if (fieldSchema.type === Boolean) { 36 | formlyField.type = 'checkbox'; 37 | return; 38 | } 39 | }); 40 | 41 | }]); 42 | -------------------------------------------------------------------------------- /lib/client/parsers/validation/messages.js: -------------------------------------------------------------------------------- 1 | var {SetModule} = angular2now; 2 | 3 | SetModule('autoFormly') 4 | .run(['autoFormlyParser', (autoFormlyParser) => { 5 | 6 | autoFormlyParser.register((fieldKey, fieldSchema, formlyField) => { 7 | if (autoFormlyParser.getNestedObject(fieldSchema, 'autoformly.validation.messages')) { 8 | autoFormlyParser.createNestedObject(formlyField, 'validation.messages'); 9 | formlyField.validation.messages = fieldSchema.autoformly.validation.messages; 10 | } 11 | }); 12 | 13 | }]); 14 | -------------------------------------------------------------------------------- /lib/client/parsers/validators/allowed.js: -------------------------------------------------------------------------------- 1 | const {SetModule} = angular2now; 2 | 3 | SetModule('autoFormly') 4 | .run(['autoFormlyParser', 'formlyValidator', (autoFormlyParser, formlyValidator) => { 5 | 6 | autoFormlyParser.register((fieldKey, fieldSchema, formlyField) => { 7 | if (fieldSchema.allowedValues) { 8 | formlyValidator.setFieldValidator(formlyField, 'allowed', fieldSchema.allowedValues); 9 | } 10 | }); 11 | 12 | }]); 13 | -------------------------------------------------------------------------------- /lib/client/parsers/validators/maxlength.js: -------------------------------------------------------------------------------- 1 | var {SetModule} = angular2now; 2 | 3 | SetModule('autoFormly') 4 | .run(['autoFormlyParser', 'formlyValidator', (autoFormlyParser, formlyValidator) => { 5 | 6 | autoFormlyParser.register((fieldKey, fieldSchema, formlyField) => { 7 | if (fieldSchema.max && fieldSchema.type === String) { 8 | formlyValidator.setFieldValidator(formlyField, 'maxlength', fieldSchema.max); 9 | } 10 | }); 11 | 12 | }]); 13 | -------------------------------------------------------------------------------- /lib/client/parsers/validators/maxnumber.js: -------------------------------------------------------------------------------- 1 | var {SetModule} = angular2now; 2 | 3 | SetModule('autoFormly') 4 | .run(['autoFormlyParser', 'formlyValidator', (autoFormlyParser, formlyValidator) => { 5 | 6 | autoFormlyParser.register((fieldKey, fieldSchema, formlyField) => { 7 | if (fieldSchema.max && fieldSchema.type === Number) { 8 | formlyValidator.setFieldValidator(formlyField, 'maxnumber', fieldSchema.max); 9 | } 10 | }); 11 | 12 | }]); 13 | -------------------------------------------------------------------------------- /lib/client/parsers/validators/minlength.js: -------------------------------------------------------------------------------- 1 | var {SetModule} = angular2now; 2 | 3 | SetModule('autoFormly') 4 | .run(['autoFormlyParser', 'formlyValidator', (autoFormlyParser, formlyValidator) => { 5 | 6 | autoFormlyParser.register((fieldKey, fieldSchema, formlyField) => { 7 | if (fieldSchema.min && fieldSchema.type === String) { 8 | formlyValidator.setFieldValidator(formlyField, 'minlength', fieldSchema.min); 9 | } 10 | }); 11 | 12 | }]); 13 | -------------------------------------------------------------------------------- /lib/client/parsers/validators/minnumber.js: -------------------------------------------------------------------------------- 1 | var {SetModule} = angular2now; 2 | 3 | SetModule('autoFormly') 4 | .run(['autoFormlyParser', 'formlyValidator', (autoFormlyParser, formlyValidator) => { 5 | 6 | autoFormlyParser.register((fieldKey, fieldSchema, formlyField) => { 7 | if (fieldSchema.min && fieldSchema.type === Number) { 8 | formlyValidator.setFieldValidator(formlyField, 'minnumber', fieldSchema.min); 9 | } 10 | }); 11 | 12 | }]); 13 | -------------------------------------------------------------------------------- /lib/client/parsers/validators/pattern.js: -------------------------------------------------------------------------------- 1 | var {SetModule} = angular2now; 2 | 3 | SetModule('autoFormly') 4 | .run(['autoFormlyParser', 'formlyValidator', (autoFormlyParser, formlyValidator) => { 5 | 6 | autoFormlyParser.register((fieldKey, fieldSchema, formlyField) => { 7 | if (fieldSchema.regEx) { 8 | formlyValidator.setFieldValidator(formlyField, 'pattern', fieldSchema.regEx); 9 | } 10 | }); 11 | 12 | }]); 13 | -------------------------------------------------------------------------------- /lib/client/parsers/validators/required.js: -------------------------------------------------------------------------------- 1 | var {SetModule} = angular2now; 2 | 3 | SetModule('autoFormly') 4 | .run(['autoFormlyParser', 'formlyValidator', (autoFormlyParser, formlyValidator) => { 5 | 6 | autoFormlyParser.register((fieldKey, fieldSchema, formlyField) => { 7 | if (!fieldSchema.optional) { 8 | formlyValidator.setFieldValidator(formlyField, 'required', true); 9 | } 10 | }); 11 | 12 | }]); 13 | -------------------------------------------------------------------------------- /lib/client/parsers/validators/unique.js: -------------------------------------------------------------------------------- 1 | const {SetModule} = angular2now; 2 | 3 | SetModule('autoFormly') 4 | .run(['autoFormlyParser', 'formlyValidator', (autoFormlyParser, formlyValidator) => { 5 | 6 | autoFormlyParser.register((fieldKey, fieldSchema, formlyField) => { 7 | if (fieldSchema.unique) { 8 | formlyValidator.setFieldValidator(formlyField, 'unique', true); 9 | } 10 | }); 11 | 12 | }]); 13 | -------------------------------------------------------------------------------- /lib/schema.js: -------------------------------------------------------------------------------- 1 | // Extend the schema options allowed by SimpleSchema 2 | SimpleSchema.extendOptions({ 3 | autoformly: Match.Optional(Object) 4 | }); -------------------------------------------------------------------------------- /package.js: -------------------------------------------------------------------------------- 1 | var both = ['client', 'server']; 2 | var client = 'client'; 3 | 4 | Package.describe({ 5 | name: "wieldo:autoformly", 6 | summary: "Create angular-formly forms with automatic insert and update, and automatic reactive validation.", 7 | version: "0.8.0", 8 | 9 | documentation: 'README.md', 10 | git: 'https://github.com/wieldo/meteor-autoformly.git' 11 | }); 12 | 13 | Package.onUse(function (api) { 14 | 15 | var packages = { 16 | use: [ 17 | 'aldeed:simple-schema@1.1.0', 18 | 'aldeed:collection2@2.0.0', 19 | 'underscore', 20 | 'check', 21 | 'angular@1.2.0', 22 | 'pbastowski:angular-babel@1.0.9', 23 | 'pbastowski:angular2-now@1.1.5', 24 | 'mys:angular-template-url@0.0.1', 25 | 'formly:angular-formly@7.3.9_3', 26 | 'wieldo:angular-formly-validator@1.5.0' 27 | ], 28 | imply: [ 29 | 'mys:angular-template-url', 30 | 'pbastowski:angular2-now', 31 | 'aldeed:simple-schema', 32 | 'aldeed:collection2', 33 | 'formly:angular-formly', 34 | 'wieldo:angular-formly-validator' 35 | ] 36 | }; 37 | 38 | api.versionsFrom("METEOR@1.0"); 39 | 40 | api.use(packages.use); 41 | 42 | api.imply(packages.imply); 43 | 44 | api.addFiles([ 45 | 'lib/client/main.js', 46 | 'lib/client/auto-formly-helpers.js', 47 | 'lib/client/auto-formly-parsers.js', 48 | 'lib/client/auto-formly.js', 49 | 'lib/client/auto-formly-component.ng.html', 50 | 'lib/client/auto-formly-component.js', 51 | // parsers 52 | 'lib/client/parsers/key.js', 53 | 'lib/client/parsers/type.js', 54 | 'lib/client/parsers/template-options/label.js', 55 | 'lib/client/parsers/template-options/options.js', 56 | 'lib/client/parsers/template-options/min-date.js', 57 | 'lib/client/parsers/template-options/max-date.js', 58 | 'lib/client/parsers/defaultvalue.js', 59 | // validation 60 | 'lib/client/parsers/validation/messages.js', 61 | // validators 62 | 'lib/client/parsers/validators/required.js', 63 | 'lib/client/parsers/validators/minlength.js', 64 | 'lib/client/parsers/validators/maxlength.js', 65 | 'lib/client/parsers/validators/minnumber.js', 66 | 'lib/client/parsers/validators/maxnumber.js', 67 | 'lib/client/parsers/validators/pattern.js', 68 | 'lib/client/parsers/validators/unique.js', 69 | 'lib/client/parsers/validators/allowed.js', 70 | // extend formlyValidator 71 | 'lib/client/formly-validator/unique.js' 72 | ], client); 73 | 74 | api.addFiles([ 75 | 'lib/schema.js' 76 | ], both); 77 | 78 | }); 79 | 80 | Package.onTest(function (api) { 81 | api.use([ 82 | 'mongo', 83 | 'underscore', 84 | 'sanjo:jasmine@0.21.0', 85 | 'velocity:helpers', 86 | 'velocity:console-reporter', 87 | 'angular:angular-mocks@1.4.7', 88 | 'pbastowski:angular-babel', 89 | 'wieldo:autoformly' 90 | ]); 91 | 92 | api.addFiles([ 93 | 'tests/client/schema.js', 94 | 'tests/client/collection.js' 95 | ], both); 96 | 97 | api.addFiles([ 98 | 'tests/client/auto-formly-parsers-spec.js', 99 | 'tests/client/auto-formly-spec.js', 100 | // parsers 101 | 'tests/client/parsers/key-spec.js', 102 | 'tests/client/parsers/type-spec.js', 103 | 'tests/client/parsers/defaultvalue-spec.js', 104 | 'tests/client/parsers/template-options/label-spec.js', 105 | 'tests/client/parsers/template-options/min-date-spec.js', 106 | 'tests/client/parsers/template-options/max-date-spec.js', 107 | // parsers validation 108 | 'tests/client/parsers/validation/messages-spec.js', 109 | // parsers validators 110 | 'tests/client/parsers/validators/required-spec.js', 111 | 'tests/client/parsers/validators/pattern-spec.js', 112 | 'tests/client/parsers/validators/minnumber-spec.js', 113 | 'tests/client/parsers/validators/maxnumber-spec.js', 114 | 'tests/client/parsers/validators/minlength-spec.js', 115 | 'tests/client/parsers/validators/maxlength-spec.js', 116 | 'tests/client/parsers/validators/unique-spec.js', 117 | 'tests/client/parsers/validators/allowed-spec.js' 118 | ], client); 119 | }); 120 | -------------------------------------------------------------------------------- /tests/client/auto-formly-parsers-spec.js: -------------------------------------------------------------------------------- 1 | describe('autoFormlyParser', () => { 2 | let autoFormlyParser; 3 | 4 | const emptyParsers = () => { 5 | autoFormlyParser.parsers = []; 6 | }; 7 | 8 | const throwErrorOnRegister = (parsers) => { 9 | parsers.forEach((parser) => { 10 | expect(() => { 11 | autoFormlyParser.register(parser); 12 | }).toThrowError(Error, "[AutoFormly] Parser has to be a function"); 13 | }); 14 | }; 15 | 16 | beforeEach(() => { 17 | module('autoFormly'); 18 | 19 | inject((_autoFormlyParser_) => { 20 | autoFormlyParser = _autoFormlyParser_; 21 | }); 22 | }); 23 | 24 | describe('register()', () => { 25 | 26 | it('should throw error when using number as parser', () => { 27 | throwErrorOnRegister([1, -1, 0, "1", "-1", "0"]); 28 | }); 29 | 30 | it('should throw error when using empty value as parser', () => { 31 | throwErrorOnRegister(["", null, undefined]); 32 | }); 33 | 34 | it('should throw error when using boolean value as parser', () => { 35 | throwErrorOnRegister([true, false]); 36 | }); 37 | 38 | it('should throw error when using object value as parser', () => { 39 | throwErrorOnRegister([{}, []]); 40 | }); 41 | 42 | it('should be able to register parser function', () => { 43 | const before = autoFormlyParser.parsers.length; 44 | 45 | autoFormlyParser.register(() => { 46 | }); 47 | expect(autoFormlyParser.parsers.length).toBe(before + 1); 48 | }); 49 | }); 50 | 51 | describe('runParsers()', () => { 52 | 53 | it('should be able to run parsers with field key, schema and formly configuration', () => { 54 | emptyParsers(); 55 | const spy = jasmine.createSpy('spy'); 56 | 57 | autoFormlyParser.register(spy); 58 | autoFormlyParser.runParsers("test1", {}, {key: 'test'}); 59 | 60 | expect(spy).toHaveBeenCalledWith("test1", {}, {key: 'test'}); 61 | }); 62 | it('shoud pass copy of fieldSchema in each parser', () => { 63 | emptyParsers(); 64 | const spy = jasmine.createSpy('spy'); 65 | const fieldSchema = { 66 | name: "test" 67 | }; 68 | 69 | autoFormlyParser.register((fk, fs) => { 70 | spy(); 71 | fs.name = "test3"; 72 | }); 73 | autoFormlyParser.runParsers("test", fieldSchema, {}); 74 | 75 | expect(spy).toHaveBeenCalled(); 76 | expect(fieldSchema.name).toBe("test"); 77 | }); 78 | 79 | }); 80 | 81 | describe('field()', () => { 82 | it('should not be able to handle schema field with array of objects type TODO', () => { 83 | expect(autoFormlyParser.field("parent.$.child")).toBeUndefined(); 84 | expect(autoFormlyParser.field("parent.$")).toBeUndefined(); 85 | }); 86 | 87 | it("should extend formly field based on schema's autoformly property", () => { 88 | const fieldKey = "test"; 89 | const fieldSchema = { 90 | name: "test", 91 | autoformly: { 92 | type: "test" 93 | } 94 | }; 95 | emptyParsers(); 96 | autoFormlyParser.register((fKey, fSchema, fFormly) => { 97 | fFormly.key = fSchema.name; 98 | }); 99 | 100 | expect(autoFormlyParser.field(fieldKey, fieldSchema)).toEqual({ 101 | key: "test", 102 | type: "test" 103 | }); 104 | }); 105 | 106 | it('should run parsers', () => { 107 | const fieldKey = "test"; 108 | const fieldSchema = { 109 | name: "test" 110 | }; 111 | 112 | emptyParsers(); 113 | autoFormlyParser.register((fKey, fSchema, fFormly) => { 114 | fFormly.key = fSchema.name; 115 | }); 116 | 117 | expect(autoFormlyParser.field(fieldKey, fieldSchema)).toEqual({ 118 | key: "test" 119 | }); 120 | }); 121 | }); 122 | 123 | describe('schema()', () => { 124 | it('should be handle run parsers and reject keys with $ sign', () => { 125 | emptyParsers(); 126 | autoFormlyParser.register((fKey, fSchema, fFormly) => { 127 | fFormly.key = fKey; 128 | fFormly.type = 'input'; 129 | }); 130 | 131 | const fields = autoFormlyParser.schema(UserSchema.schema()); 132 | 133 | expect(fields.length).toBe(20); 134 | }); 135 | }); 136 | 137 | }); -------------------------------------------------------------------------------- /tests/client/auto-formly-spec.js: -------------------------------------------------------------------------------- 1 | describe('autoFormly', () => { 2 | // 3 | // vars 4 | // 5 | 6 | let autoFormly; 7 | let $meteor; 8 | 9 | // 10 | // helpers 11 | // 12 | 13 | function fieldExist(key, fields) { 14 | return "undefined" !== typeof _.find(fields, (field) => field.key === key) 15 | } 16 | 17 | function collectionFail(values, message) { 18 | if (!angular.isArray(values)) { 19 | values = [values]; 20 | } 21 | values.forEach((value) => { 22 | expect(() => { 23 | autoFormly.collection(value); 24 | }).toThrowError(Error, `[AutoFormly] ${message}`); 25 | }); 26 | } 27 | 28 | function collectionPass(values) { 29 | if (!angular.isArray(values)) { 30 | values = [values]; 31 | } 32 | values.forEach((value) => { 33 | expect(() => { 34 | autoFormly.collection(value); 35 | }).not.toThrowError(); 36 | }); 37 | } 38 | 39 | // 40 | // tests 41 | // 42 | 43 | beforeEach(() => { 44 | module('angular-meteor'); 45 | module('autoFormly'); 46 | 47 | inject(function (_autoFormly_, _$meteor_) { 48 | autoFormly = _autoFormly_; 49 | $meteor = _$meteor_; 50 | }); 51 | }); 52 | 53 | describe("collection()", () => { 54 | it("should fail on non collection2 objects", () => { 55 | const values = [undefined, null, false, true, "", {}, "asd", 0, 1, -1]; 56 | values.push(() => { 57 | }); 58 | collectionFail(values, "Collection is not extended by Collection2"); 59 | }); 60 | 61 | it("should fail on collection not extended by Collection2", () => { 62 | collectionFail([CollectionFAIL, $meteor.collection(CollectionFAIL)], "Collection is not extended by Collection2"); 63 | }); 64 | 65 | it("should pass collection extended by Collection2", () => { 66 | collectionPass([CollectionOK, $meteor.collection(CollectionOK)]); 67 | }); 68 | }); 69 | 70 | describe("filterSchema()", () => { 71 | it("should fail when fields are not an array", () => { 72 | const values = ["s", "", 0, 1, true, false, undefined, null, {}, () => { 73 | }]; 74 | 75 | values.forEach((val) => { 76 | expect(() => { 77 | autoFormly.filterSchema({}, val); 78 | }).toThrowError(Error, "[AutoFormly] Fields to filter have to be an array"); 79 | }); 80 | }); 81 | 82 | it('should pass on array fields', () => { 83 | expect(() => { 84 | autoFormly.filterSchema({}, []); 85 | }).not.toThrowError(Error, "[AutoFormly] Fields to filter have to be an array"); 86 | }); 87 | 88 | it("should filter correctly", () => { 89 | const fields = autoFormly.filterSchema(UserSchema, ['username', 'createdAt']); 90 | expect(fields.username).toBeDefined(); 91 | expect(fields.createdAt).toBeDefined(); 92 | expect(fields.services).toBeUndefined(); 93 | }); 94 | }); 95 | 96 | describe('schema()', () => { 97 | it("should filter correctly using object", () => { 98 | const fields = autoFormly.schema(UserSchema, { 99 | fields: { 100 | username: true, 101 | createdAt: true, 102 | services: false 103 | } 104 | }); 105 | const keys = { 106 | username: false, 107 | createdAt: false, 108 | services: false 109 | }; 110 | 111 | _.each(keys, (val, key) => { 112 | keys[key] = fieldExist(key, fields); 113 | }); 114 | 115 | expect(keys.username).toBeTruthy(); 116 | expect(keys.createdAt).toBeTruthy(); 117 | expect(keys.services).toBeFalsy(); 118 | }); 119 | 120 | it("should extend autoformly configuration in schema", () => { 121 | const fields = autoFormly.schema(UserSchema, { 122 | fields: { 123 | username: { 124 | templateOptions: { 125 | label: "Test label" 126 | } 127 | } 128 | } 129 | }); 130 | const field = _.find(fields, (field) => field.key === 'username'); 131 | expect(field.templateOptions.label).toBe("Test label"); 132 | }); 133 | 134 | it('should show all fields excluding one', () => { 135 | // test all:true without fields 136 | const allFields = autoFormly.schema(UserSchema, { 137 | all: true 138 | }); 139 | 140 | // check all:true with one field 141 | expect(autoFormly.schema(UserSchema, { 142 | all: true, 143 | fields: { 144 | username: false 145 | } 146 | }).length).toEqual(allFields.length - 1); 147 | }); 148 | 149 | it('should show only specified fields', () => { 150 | const fields = autoFormly.schema(UserSchema, { 151 | all: false, 152 | fields: { 153 | username: true, 154 | createdAt: false 155 | } 156 | }); 157 | expect(fieldExist('username', fields)).toBeTruthy(); 158 | expect(fieldExist('createdAt', fields)).toBeFalsy(); 159 | }); 160 | 161 | it('should fail on non SimpleSchema objects', () => { 162 | const values = ["s", "", 0, 1, true, false, undefined, null, {}, () => { 163 | }]; 164 | 165 | values.forEach((val) => { 166 | expect(() => { 167 | autoFormly.schema(val); 168 | }).toThrowError(Error, "[AutoFormly] Schema has to be instance of SimpleSchema"); 169 | }); 170 | }); 171 | }); 172 | 173 | describe("errors()", () => { 174 | 175 | it("should fail on missing fields", () => { 176 | expect(() => { 177 | autoFormly.errors(CollectionOK); 178 | }).toThrowError(Error, "[AutoFormly] Missing or invalid fields"); 179 | }); 180 | 181 | it("should fail on invalid fields", () => { 182 | const fields = [{}, null, true, ""]; 183 | fields.forEach((field) => { 184 | expect(() => { 185 | autoFormly.errors(CollectionOK, field); 186 | }).toThrowError(Error, "[AutoFormly] Missing or invalid fields"); 187 | }); 188 | }); 189 | 190 | it("should fail on missing collection", () => { 191 | expect(() => { 192 | autoFormly.errors(undefined, []); 193 | }).toThrowError(Error, "[AutoFormly] Collection is not extended by Collection2"); 194 | }); 195 | 196 | it("should fail on invalid collection", () => { 197 | expect(() => { 198 | autoFormly.errors(CollectionFAIL, []); 199 | }).toThrowError(Error, "[AutoFormly] Collection is not extended by Collection2"); 200 | }); 201 | 202 | it("should pass on valid collection and fields", () => { 203 | expect(() => { 204 | autoFormly.errors(CollectionOK, []); 205 | }).not.toThrowError(); 206 | }); 207 | 208 | describe("with collection errors", () => { 209 | const CollectionOKMock = angular.copy(CollectionOK); 210 | const fieldMock = { 211 | key: 'profile.firstname', 212 | formControl: { 213 | $setValidity() { 214 | 215 | } 216 | } 217 | }; 218 | const error = {name: fieldMock.key, type: "minString"}; 219 | 220 | CollectionOKMock.simpleSchema() 221 | .namedContext() 222 | .addInvalidKeys([error]); 223 | 224 | it("should set validity and properly transform error type", () => { 225 | spyOn(fieldMock.formControl, '$setValidity'); 226 | autoFormly.errors(CollectionOKMock, [fieldMock]); 227 | 228 | expect(fieldMock.formControl.$setValidity).toHaveBeenCalledWith("minlength", false); 229 | }); 230 | 231 | it("should fail on field without formControl", () => { 232 | spyOn(fieldMock.formControl, '$setValidity'); 233 | 234 | expect(() => { 235 | autoFormly.errors(CollectionOKMock, [{key: fieldMock.key}]); 236 | }).toThrowError(Error, `[AutoFormly] Field "${fieldMock.key}" has no formControl`); 237 | expect(fieldMock.formControl.$setValidity).not.toHaveBeenCalled(); 238 | }); 239 | }); 240 | }); 241 | 242 | }); -------------------------------------------------------------------------------- /tests/client/collection.js: -------------------------------------------------------------------------------- 1 | CollectionOK = new Mongo.Collection('usersOK'); 2 | CollectionFAIL = new Mongo.Collection('usersFAIL'); 3 | 4 | CollectionOK.attachSchema(UserSchema); -------------------------------------------------------------------------------- /tests/client/parsers/defaultvalue-spec.js: -------------------------------------------------------------------------------- 1 | describe("autoFormlyParser defaultValue", () => { 2 | // 3 | // vars 4 | // 5 | 6 | let autoFormlyParser; 7 | 8 | // 9 | // tests 10 | // 11 | 12 | beforeEach(() => { 13 | module('autoFormly'); 14 | 15 | inject((_autoFormlyParser_) => { 16 | autoFormlyParser = _autoFormlyParser_; 17 | }); 18 | }); 19 | 20 | it("should set default value on formly field", () => { 21 | const field = autoFormlyParser.field("test", { 22 | defaultValue: "test" 23 | }); 24 | 25 | expect(field.defaultValue).toBe("test"); 26 | }); 27 | 28 | it("should set default value on formly field even if empty", () => { 29 | const field = autoFormlyParser.field("test", { 30 | defaultValue: "" 31 | }); 32 | 33 | expect(field.defaultValue).toBe(""); 34 | }); 35 | 36 | it("should set default value on formly field even if equals null", () => { 37 | const field = autoFormlyParser.field("test", { 38 | defaultValue: null 39 | }); 40 | 41 | expect(field.defaultValue).toBe(null); 42 | }); 43 | 44 | }); 45 | -------------------------------------------------------------------------------- /tests/client/parsers/key-spec.js: -------------------------------------------------------------------------------- 1 | describe("autoFormlyParser key", () => { 2 | let autoFormlyParser; 3 | 4 | beforeEach(() => { 5 | module('autoFormly'); 6 | 7 | inject((_autoFormlyParser_) => { 8 | autoFormlyParser = _autoFormlyParser_; 9 | }); 10 | }); 11 | 12 | it("should set key property on formly field", () => { 13 | const field = autoFormlyParser.field("test", { 14 | type: String 15 | }); 16 | 17 | expect(field.key).toBe("test"); 18 | }); 19 | }); -------------------------------------------------------------------------------- /tests/client/parsers/template-options/label-spec.js: -------------------------------------------------------------------------------- 1 | describe("autoFormlyParser templateOptions.label", () => { 2 | let autoFormlyParser; 3 | 4 | beforeEach(() => { 5 | module('autoFormly'); 6 | 7 | inject((_autoFormlyParser_) => { 8 | autoFormlyParser = _autoFormlyParser_; 9 | }); 10 | }); 11 | 12 | it("should set label property on formly field templateOptions when schema.label exists", () => { 13 | const field = autoFormlyParser.field("test", { 14 | type: String, 15 | label: "test-label" 16 | }); 17 | 18 | expect(field.templateOptions.label).toBe("test-label"); 19 | }); 20 | 21 | it("should set key as label property on formly field templateOptions when schema.label does not exist", () => { 22 | const field = autoFormlyParser.field("test", { 23 | type: String 24 | }); 25 | 26 | expect(field.templateOptions.label).toBe("test"); 27 | }); 28 | }); -------------------------------------------------------------------------------- /tests/client/parsers/template-options/max-date-spec.js: -------------------------------------------------------------------------------- 1 | describe("autoFormlyParser templateOptions.maxDate", () => { 2 | let autoFormlyParser; 3 | 4 | beforeEach(() => { 5 | module('autoFormly'); 6 | 7 | inject((_autoFormlyParser_) => { 8 | autoFormlyParser = _autoFormlyParser_; 9 | }); 10 | }); 11 | 12 | it("should set templateOptions.minDate", () => { 13 | const date = new Date(1995, 11, 17); 14 | const field = autoFormlyParser.field("test", { 15 | type: Date, 16 | max: date 17 | }); 18 | 19 | expect(field.templateOptions.maxDate).toEqual(date); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /tests/client/parsers/template-options/min-date-spec.js: -------------------------------------------------------------------------------- 1 | describe("autoFormlyParser templateOptions.minDate", () => { 2 | let autoFormlyParser; 3 | 4 | beforeEach(() => { 5 | module('autoFormly'); 6 | 7 | inject((_autoFormlyParser_) => { 8 | autoFormlyParser = _autoFormlyParser_; 9 | }); 10 | }); 11 | 12 | it("should set templateOptions.minDate", () => { 13 | const date = new Date(1995, 11, 17); 14 | const field = autoFormlyParser.field("test", { 15 | type: Date, 16 | min: date 17 | }); 18 | 19 | expect(field.templateOptions.minDate).toEqual(date); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /tests/client/parsers/type-spec.js: -------------------------------------------------------------------------------- 1 | describe("autoFormlyParsers type", () => { 2 | let autoFormlyParser; 3 | 4 | beforeEach(() => { 5 | module('autoFormly'); 6 | 7 | inject((_autoFormlyParser_) => { 8 | autoFormlyParser = _autoFormlyParser_; 9 | }); 10 | }); 11 | 12 | it("should set input as formly field type on String type", () => { 13 | const field = autoFormlyParser.field("test", { 14 | type: String 15 | }); 16 | 17 | expect(field.type).toBe("input"); 18 | }); 19 | 20 | it("should set input as formly field type on Number type", () => { 21 | const field = autoFormlyParser.field("test", { 22 | type: Number 23 | }); 24 | 25 | expect(field.type).toBe("input"); 26 | }); 27 | 28 | it("should set datepicker as formly field type on Date type", () => { 29 | const field = autoFormlyParser.field("test", { 30 | type: Date 31 | }); 32 | 33 | expect(field.type).toBe("datepicker"); 34 | }); 35 | 36 | it("should not set formly field type on Array type", () => { 37 | const field = autoFormlyParser.field("test", { 38 | type: Array 39 | }); 40 | 41 | expect(field.type).toBeUndefined(); 42 | }); 43 | 44 | it("should not set formly field type on Object type", () => { 45 | const field = autoFormlyParser.field("test", { 46 | type: Object 47 | }); 48 | 49 | expect(field.type).toBeUndefined(); 50 | }); 51 | 52 | it('should set field type if schema.autoformly.type is defined', () => { 53 | const field = autoFormlyParser.field("test", { 54 | type: Object, 55 | autoformly: { 56 | type: "input" 57 | } 58 | }); 59 | 60 | expect(field.type).toBe("input"); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /tests/client/parsers/validation/messages-spec.js: -------------------------------------------------------------------------------- 1 | describe("autoFormlyParser validation.messages", () => { 2 | // 3 | // vars 4 | // 5 | 6 | let autoFormlyParser; 7 | 8 | // 9 | // tests 10 | // 11 | 12 | beforeEach(() => { 13 | module('autoFormly'); 14 | 15 | inject((_autoFormlyParser_) => { 16 | autoFormlyParser = _autoFormlyParser_; 17 | }); 18 | }); 19 | 20 | it("should set validation messages on formly field", () => { 21 | const field = autoFormlyParser.field("test", { 22 | autoformly: { 23 | validation: { 24 | messages: { 25 | required: true 26 | } 27 | } 28 | } 29 | }); 30 | 31 | expect(field.validation.messages.required).toBe(true); 32 | }); 33 | 34 | }); 35 | -------------------------------------------------------------------------------- /tests/client/parsers/validators/allowed-spec.js: -------------------------------------------------------------------------------- 1 | describe("autoFormlyParsers validators allowed", () => { 2 | let autoFormlyParser; 3 | let formlyValidator; 4 | 5 | beforeEach(() => { 6 | module('autoFormly'); 7 | 8 | inject((_autoFormlyParser_, _formlyValidator_) => { 9 | autoFormlyParser = _autoFormlyParser_; 10 | formlyValidator = _formlyValidator_; 11 | }); 12 | }); 13 | 14 | it("should set allowed validator on schema field with allowed values", () => { 15 | const allowedValues = ['foo', 'bar']; 16 | const field = autoFormlyParser.field("test", { 17 | type: String, 18 | allowedValues: allowedValues 19 | }); 20 | 21 | expect(formlyValidator.getFieldValidator(field, 'allowed')).toEqual(allowedValues); 22 | }); 23 | 24 | }); -------------------------------------------------------------------------------- /tests/client/parsers/validators/maxlength-spec.js: -------------------------------------------------------------------------------- 1 | describe("autoFormlyParsers validators maxlength", () => { 2 | let autoFormlyParser; 3 | let formlyValidator; 4 | 5 | beforeEach(() => { 6 | module('autoFormly'); 7 | 8 | inject((_autoFormlyParser_, _formlyValidator_) => { 9 | autoFormlyParser = _autoFormlyParser_; 10 | formlyValidator = _formlyValidator_; 11 | }); 12 | }); 13 | 14 | it("should set maxlength validator on String with min value", () => { 15 | const field = autoFormlyParser.field("test", { 16 | type: String, 17 | max: 10 18 | }); 19 | 20 | expect(formlyValidator.getFieldValidator(field, 'maxlength')).toEqual(10); 21 | }); 22 | 23 | it("should set maxlength validator on String with min and max values", () => { 24 | const field = autoFormlyParser.field("test", { 25 | type: String, 26 | min: 10, 27 | max: 20 28 | }); 29 | 30 | expect(formlyValidator.getFieldValidator(field, 'maxlength')).toEqual(20); 31 | }); 32 | 33 | it("should not set maxlength validator on String without max value", () => { 34 | const field = autoFormlyParser.field("test", { 35 | type: String, 36 | min: 20 37 | }); 38 | 39 | expect(formlyValidator.getFieldValidator(field, 'maxlength')).toBeUndefined(); 40 | }); 41 | 42 | it("should not set maxlength validator on Number with min value", () => { 43 | const field = autoFormlyParser.field("test", { 44 | type: Number, 45 | max: 20 46 | }); 47 | 48 | expect(formlyValidator.getFieldValidator(field, 'maxlength')).toBeUndefined(); 49 | }); 50 | 51 | it("should not set maxlength validator on Date with min value", () => { 52 | const field = autoFormlyParser.field("test", { 53 | type: Date, 54 | max: 20 55 | }); 56 | 57 | expect(formlyValidator.getFieldValidator(field, 'maxlength')).toBeUndefined(); 58 | }); 59 | 60 | }); -------------------------------------------------------------------------------- /tests/client/parsers/validators/maxnumber-spec.js: -------------------------------------------------------------------------------- 1 | describe("autoFormlyParsers validators maxnumber", () => { 2 | let autoFormlyParser; 3 | let formlyValidator; 4 | 5 | beforeEach(() => { 6 | module('autoFormly'); 7 | 8 | inject((_autoFormlyParser_, _formlyValidator_) => { 9 | autoFormlyParser = _autoFormlyParser_; 10 | formlyValidator = _formlyValidator_; 11 | }); 12 | }); 13 | 14 | it("should set maxnumber validator on Number with max value", () => { 15 | const field = autoFormlyParser.field("test", { 16 | type: Number, 17 | max: 10 18 | }); 19 | 20 | expect(formlyValidator.getFieldValidator(field, 'maxnumber')).toEqual(10); 21 | }); 22 | 23 | it("should set maxnumber validator on Number with max and min values", () => { 24 | const field = autoFormlyParser.field("test", { 25 | type: Number, 26 | min: 10, 27 | max: 20 28 | }); 29 | 30 | expect(formlyValidator.getFieldValidator(field, 'maxnumber')).toEqual(20); 31 | }); 32 | 33 | it("should not set maxnumber validator on Number without max value", () => { 34 | const field = autoFormlyParser.field("test", { 35 | type: Number, 36 | min: 20 37 | }); 38 | 39 | expect(formlyValidator.getFieldValidator(field, 'maxnumber')).toBeUndefined(); 40 | }); 41 | 42 | it("should not set maxnumber validator on String with max value", () => { 43 | const field = autoFormlyParser.field("test", { 44 | type: String, 45 | max: 20 46 | }); 47 | 48 | expect(formlyValidator.getFieldValidator(field, 'maxnumber')).toBeUndefined(); 49 | }); 50 | 51 | it("should not set maxnumber validator on Date with max value", () => { 52 | const field = autoFormlyParser.field("test", { 53 | type: Date, 54 | max: 20 55 | }); 56 | 57 | expect(formlyValidator.getFieldValidator(field, 'maxnumber')).toBeUndefined(); 58 | }); 59 | 60 | }); -------------------------------------------------------------------------------- /tests/client/parsers/validators/minlength-spec.js: -------------------------------------------------------------------------------- 1 | describe("autoFormlyParsers validators minlength", () => { 2 | let autoFormlyParser; 3 | let formlyValidator; 4 | 5 | beforeEach(() => { 6 | module('autoFormly'); 7 | 8 | inject((_autoFormlyParser_, _formlyValidator_) => { 9 | autoFormlyParser = _autoFormlyParser_; 10 | formlyValidator = _formlyValidator_; 11 | }); 12 | }); 13 | 14 | it("should set minlength validator on String with min value", () => { 15 | const field = autoFormlyParser.field("test", { 16 | type: String, 17 | min: 10 18 | }); 19 | 20 | expect(formlyValidator.getFieldValidator(field, 'minlength')).toEqual(10); 21 | }); 22 | 23 | it("should set minlength validator on String with min and max value", () => { 24 | const field = autoFormlyParser.field("test", { 25 | type: String, 26 | min: 10, 27 | max: 20 28 | }); 29 | 30 | expect(formlyValidator.getFieldValidator(field, 'minlength')).toEqual(10); 31 | }); 32 | 33 | it("should not set minlength validator on String without min value", () => { 34 | const field = autoFormlyParser.field("test", { 35 | type: String, 36 | max: 20 37 | }); 38 | 39 | expect(formlyValidator.getFieldValidator(field, 'minlength')).toBeUndefined(); 40 | }); 41 | 42 | it("should not set minlength validator on Number with min value", () => { 43 | const field = autoFormlyParser.field("test", { 44 | type: Number, 45 | min: 20 46 | }); 47 | 48 | expect(formlyValidator.getFieldValidator(field, 'minlength')).toBeUndefined(); 49 | }); 50 | 51 | it("should not set minlength validator on Date with min value", () => { 52 | const field = autoFormlyParser.field("test", { 53 | type: Date, 54 | min: 20 55 | }); 56 | 57 | expect(formlyValidator.getFieldValidator(field, 'minlength')).toBeUndefined(); 58 | }); 59 | 60 | }); -------------------------------------------------------------------------------- /tests/client/parsers/validators/minnumber-spec.js: -------------------------------------------------------------------------------- 1 | describe("autoFormlyParsers validators minnumber", () => { 2 | let autoFormlyParser; 3 | let formlyValidator; 4 | 5 | beforeEach(() => { 6 | module('autoFormly'); 7 | 8 | inject((_autoFormlyParser_, _formlyValidator_) => { 9 | autoFormlyParser = _autoFormlyParser_; 10 | formlyValidator = _formlyValidator_; 11 | }); 12 | }); 13 | 14 | it("should set minnumber validator on Number with min value", () => { 15 | const field = autoFormlyParser.field("test", { 16 | type: Number, 17 | min: 10 18 | }); 19 | 20 | expect(formlyValidator.getFieldValidator(field, 'minnumber')).toEqual(10); 21 | }); 22 | 23 | it("should set minnumber validator on Number with min and max value", () => { 24 | const field = autoFormlyParser.field("test", { 25 | type: Number, 26 | min: 10, 27 | max: 20 28 | }); 29 | 30 | expect(formlyValidator.getFieldValidator(field, 'minnumber')).toEqual(10); 31 | }); 32 | 33 | it("should not set minnumber validator on Number without min value", () => { 34 | const field = autoFormlyParser.field("test", { 35 | type: Number, 36 | max: 20 37 | }); 38 | 39 | expect(formlyValidator.getFieldValidator(field, 'minnumber')).toBeUndefined(); 40 | }); 41 | 42 | it("should not set minnumber validator on String with min value", () => { 43 | const field = autoFormlyParser.field("test", { 44 | type: String, 45 | min: 20 46 | }); 47 | 48 | expect(formlyValidator.getFieldValidator(field, 'minnumber')).toBeUndefined(); 49 | }); 50 | 51 | it("should not set minnumber validator on Date with min value", () => { 52 | const field = autoFormlyParser.field("test", { 53 | type: Date, 54 | min: 20 55 | }); 56 | 57 | expect(formlyValidator.getFieldValidator(field, 'minnumber')).toBeUndefined(); 58 | }); 59 | 60 | }); -------------------------------------------------------------------------------- /tests/client/parsers/validators/pattern-spec.js: -------------------------------------------------------------------------------- 1 | describe("autoFormlyParsers validators pattern", () => { 2 | let autoFormlyParser; 3 | let formlyValidator; 4 | 5 | beforeEach(() => { 6 | module('autoFormly'); 7 | 8 | inject((_autoFormlyParser_, _formlyValidator_) => { 9 | autoFormlyParser = _autoFormlyParser_; 10 | formlyValidator = _formlyValidator_; 11 | }); 12 | }); 13 | 14 | it("should set pattern validator on schema.regEx", () => { 15 | const field = autoFormlyParser.field("test", { 16 | regEx: SimpleSchema.RegEx.Email 17 | }); 18 | 19 | expect(formlyValidator.getFieldValidator(field, 'pattern')).toEqual(SimpleSchema.RegEx.Email); 20 | }); 21 | 22 | it("should not set pattern validator on missing schema.regEx", () => { 23 | const field = autoFormlyParser.field("test", {}); 24 | 25 | expect(formlyValidator.getFieldValidator(field, 'pattern')).toBeUndefined(); 26 | }); 27 | }); -------------------------------------------------------------------------------- /tests/client/parsers/validators/required-spec.js: -------------------------------------------------------------------------------- 1 | describe("autoFormlyParsers validators required", () => { 2 | let autoFormlyParser; 3 | let formlyValidator; 4 | 5 | beforeEach(() => { 6 | module('autoFormly'); 7 | 8 | inject((_autoFormlyParser_, _formlyValidator_) => { 9 | autoFormlyParser = _autoFormlyParser_; 10 | formlyValidator = _formlyValidator_; 11 | }); 12 | }); 13 | 14 | it("should set required validator on optional:false", () => { 15 | const field = autoFormlyParser.field("test", { 16 | optional: false 17 | }); 18 | 19 | expect(field.transformers.validators.required).toBeTruthy(); 20 | }); 21 | 22 | it("should set required validator on missing optional property", () => { 23 | const field = autoFormlyParser.field("test", {}); 24 | 25 | expect(formlyValidator.getFieldValidator(field, 'required')).toBeTruthy(); 26 | }); 27 | 28 | it("should not set required validator on optional:true", () => { 29 | const field = autoFormlyParser.field("test", { 30 | optional: true 31 | }); 32 | 33 | expect(formlyValidator.getFieldValidator(field, 'required')).toBeUndefined(); 34 | }); 35 | }); -------------------------------------------------------------------------------- /tests/client/parsers/validators/unique-spec.js: -------------------------------------------------------------------------------- 1 | describe("autoFormlyParsers validators unique", () => { 2 | let autoFormlyParser; 3 | let formlyValidator; 4 | 5 | beforeEach(() => { 6 | module('autoFormly'); 7 | 8 | inject((_autoFormlyParser_, _formlyValidator_) => { 9 | autoFormlyParser = _autoFormlyParser_; 10 | formlyValidator = _formlyValidator_; 11 | }); 12 | }); 13 | 14 | it("should set allowed validator on schema field with allowed values", () => { 15 | const field = autoFormlyParser.field("test", { 16 | type: String, 17 | unique: true 18 | }); 19 | 20 | expect(formlyValidator.getFieldValidator(field, 'unique')).toEqual(true); 21 | }); 22 | 23 | }); -------------------------------------------------------------------------------- /tests/client/schema.js: -------------------------------------------------------------------------------- 1 | const userProfile = new SimpleSchema({ 2 | firstname: { 3 | type: String, 4 | min: 3, 5 | max: 10 6 | }, 7 | lastname: { 8 | type: String, 9 | min: 5, 10 | max: 15 11 | }, 12 | birthday: { 13 | type: Date, 14 | optional: true 15 | }, 16 | gender: { 17 | type: String, 18 | allowedValues: ['Male', 'Female'], 19 | optional: true, 20 | defaultValue: "Male" 21 | }, 22 | language: { 23 | type: String, 24 | allowedValues: ['PL', 'EN'], 25 | defaultValue: 'EN', 26 | optional: true 27 | } 28 | }); 29 | 30 | const userLinkedServices = new SimpleSchema({ 31 | facebook: { 32 | type: Boolean, 33 | defaultValue: false 34 | }, 35 | google: { 36 | type: Boolean, 37 | defaultValue: false 38 | }, 39 | profile: { 40 | type: userProfile 41 | } 42 | }); 43 | 44 | /** 45 | * Users schema 46 | * @type {SimpleSchema} 47 | */ 48 | UserSchema = new SimpleSchema({ 49 | username: { 50 | type: String, 51 | optional: true 52 | }, 53 | registered_emails: { 54 | type: [Object], 55 | blackbox: true, 56 | optional: true 57 | }, 58 | emails: { 59 | type: Array, 60 | optional: true 61 | }, 62 | "emails.$": { 63 | type: Object 64 | }, 65 | "emails.$.address": { 66 | type: String, 67 | regEx: SimpleSchema.RegEx.Email 68 | }, 69 | "emails.$.verified": { 70 | type: Boolean 71 | }, 72 | createdAt: { 73 | type: Date 74 | }, 75 | profile: { 76 | type: userProfile 77 | }, 78 | linkedServices: { 79 | type: userLinkedServices 80 | }, 81 | // Make sure this services field is in your schema if you're using any of the accounts packages 82 | services: { 83 | type: Object, 84 | optional: true, 85 | blackbox: true 86 | } 87 | }); --------------------------------------------------------------------------------