From 8b39431a39129410c839562334bf0e752d074c3f Mon Sep 17 00:00:00 2001 From: Bryan Wyman Date: Tue, 18 Mar 2014 11:54:04 -0700 Subject: [PATCH 1/9] Add grunt, karma and basic unit test Gives the ability to run unit tests and provides a basic test. --- .gitignore | 2 ++ Gruntfile.js | 22 +++++++++++++++++++ bower.json | 5 ++++- karma.conf.js | 55 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 11 ++++++++++ test/testBasic.js | 6 ++++++ 6 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 Gruntfile.js create mode 100644 karma.conf.js create mode 100644 package.json create mode 100644 test/testBasic.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..68b9e27 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +bower_components/ diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..26fd13c --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,22 @@ + +module.exports = function (grunt) { + 'use strict'; + grunt.initConfig({ + connect: { + options: { + port: 9001, + hostname: '0.0.0.0' + } + }, + karma: { + unit: { + configFile: 'karma.conf.js', + singleRun: true + } + } + }); + + grunt.loadNpmTasks('grunt-contrib-connect'); + grunt.loadNpmTasks('grunt-karma'); + grunt.registerTask('test', ['karma']); +}; \ No newline at end of file diff --git a/bower.json b/bower.json index 8e4092c..42dafbc 100644 --- a/bower.json +++ b/bower.json @@ -2,5 +2,8 @@ "name": "angularjs-dynamic-form", "version": "0.0.0", "description": "AngularJS directive for creating dynamic forms based on schemas that aren't known until runtime", - "license": "MIT" + "license": "MIT", + "dependencies": { + "angular": "1.2.7" + } } diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 0000000..f9a8bdf --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,55 @@ +// Karma configuration +// http://karma-runner.github.io/0.10/config/configuration-file.html + +module.exports = function(config) { + config.set({ + // base path, that will be used to resolve files and exclude + basePath: '', + + // testing framework to use (jasmine/mocha/qunit/...) + frameworks: ['jasmine'], + + plugins: [ + 'karma-phantomjs-launcher', + 'karma-jasmine' + ], + + // list of files / patterns to load in the browser + files: [ + 'bower_components/angular/angular.js', + //'bower_components/angular/angular-mocks.js', + 'src/*.js', + 'test/*.js' + ], + + // list of files / patterns to exclude + exclude: [], + + // web server port + port: 8080, + + // level of logging + // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG + logLevel: config.LOG_INFO, + + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: false, + + + // Start these browsers, currently available: + // - Chrome + // - ChromeCanary + // - Firefox + // - Opera + // - Safari (only Mac) + // - PhantomJS + // - IE (only Windows) + browsers: ['PhantomJS'], + + + // Continuous Integration mode + // if true, it capture browsers, run tests and exit + singleRun: true + }); +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..2401fff --- /dev/null +++ b/package.json @@ -0,0 +1,11 @@ +{ + "devDependencies": { + "grunt-karma": "^0.8.2", + "grunt-cli": "^0.1.13", + "bower": "^1.3.1", + "karma": "^0.12.1", + "grunt-contrib-connect": "^0.7.1", + "karma-jasmine": "^0.1.5", + "karma-phantomjs-launcher": "^0.1.2" + } +} diff --git a/test/testBasic.js b/test/testBasic.js new file mode 100644 index 0000000..9e4a1fe --- /dev/null +++ b/test/testBasic.js @@ -0,0 +1,6 @@ +// let's have a really basic test to run +describe('version', function () { + it('Should be equal to iteslf', function () { + expect('1').toEqual('1'); + }); +}); \ No newline at end of file From dcffbd569cb3f2f3b692361e1aa324de19925dd3 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Tue, 18 Mar 2014 12:05:50 -0700 Subject: [PATCH 2/9] Add Travis-CI build configuration. --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..98682d9 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - "0.10" +install: npm install && bower install +script: grunt test From fbe85fd33ea951058817cdf55f4869319adfbcfd Mon Sep 17 00:00:00 2001 From: Bryan Wyman Date: Thu, 20 Mar 2014 14:54:58 -0700 Subject: [PATCH 3/9] Add some unit tests --- Gruntfile.js | 11 +-- bower.json | 4 ++ karma.conf.js | 4 +- test/testDynamicForm.js | 151 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 164 insertions(+), 6 deletions(-) create mode 100644 test/testDynamicForm.js diff --git a/Gruntfile.js b/Gruntfile.js index 26fd13c..7363a2e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -3,9 +3,12 @@ module.exports = function (grunt) { 'use strict'; grunt.initConfig({ connect: { - options: { - port: 9001, - hostname: '0.0.0.0' + server: { + options: { + port: 9001, + base: '.', + hostname: '0.0.0.0' + } } }, karma: { @@ -19,4 +22,4 @@ module.exports = function (grunt) { grunt.loadNpmTasks('grunt-contrib-connect'); grunt.loadNpmTasks('grunt-karma'); grunt.registerTask('test', ['karma']); -}; \ No newline at end of file +}; diff --git a/bower.json b/bower.json index 42dafbc..cc22610 100644 --- a/bower.json +++ b/bower.json @@ -5,5 +5,9 @@ "license": "MIT", "dependencies": { "angular": "1.2.7" + }, + "devDependencies": { + "angular-mocks": "1.2.7", + "jasmine": "~1.3.1" } } diff --git a/karma.conf.js b/karma.conf.js index f9a8bdf..61334f2 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -11,13 +11,14 @@ module.exports = function(config) { plugins: [ 'karma-phantomjs-launcher', + 'karma-chrome-launcher', 'karma-jasmine' ], // list of files / patterns to load in the browser files: [ 'bower_components/angular/angular.js', - //'bower_components/angular/angular-mocks.js', + 'bower_components/angular-mocks/angular-mocks.js', 'src/*.js', 'test/*.js' ], @@ -47,7 +48,6 @@ module.exports = function(config) { // - IE (only Windows) browsers: ['PhantomJS'], - // Continuous Integration mode // if true, it capture browsers, run tests and exit singleRun: true diff --git a/test/testDynamicForm.js b/test/testDynamicForm.js new file mode 100644 index 0000000..f3dda9d --- /dev/null +++ b/test/testDynamicForm.js @@ -0,0 +1,151 @@ +describe('angularjs-dynamic-form-test', function () { + + var element; + var $scope; + var compile; + var scopeFields = [ + { + caption: 'Name', + model: 'name', + type: 'string', + maxLength: 25 + }, + { + caption: 'Street Address', + model: 'address.street', + type: 'string', + maxLength: 25 + }, + { + caption: 'State', + model: 'address.state', + type: 'select', + options: [ + { + caption: 'California', + value: 'CA' + }, + { + caption: 'New York', + value: 'NY' + }, + { + caption: 'Washington', + value: 'WA' + } + ] + }, + ]; + + var scopeData = { + name: 'John Smith', + nicknames: ['bill', 'dan', 'grumpy'], + address: {} + }; + var html = '' + + '' + + '' + + ''; + + + + beforeEach(module('dynamic-form')); + + beforeEach(inject(function ($compile, $rootScope) { + $scope = $rootScope.$new(); + compile = $compile; + $scope.fields = []; + $scope.data= {}; + + element = angular.element(html); + })); + + it('Should have added control groups for each element', function () { + $scope.fields = [ + { + caption: 'Name', + model: 'name', + type: 'string', + }, + { + caption: 'Street Address', + model: 'address.street', + type: 'string', + }, + { + caption: 'State', + model: 'address.state', + type: 'select', + options: [ + { + caption: 'California', + value: 'CA' + }, + { + caption: 'New York', + value: 'NY' + }, + { + caption: 'Washington', + value: 'WA' + } + ] + }, + ]; + + compile(element)($scope); + $scope.$digest(); + + expect(element.children().length).toBe(scopeFields.length); + }); + + it('Should display a string field as an input field', function () { + $scope.fields = [ + {caption: 'Name', model: 'name', type: 'string'} + ]; + + $scope.data = { + name: 'John Smith' + }; + + compile(element)($scope); + $scope.$digest(); + + expect(element.children().length).toBe(1); + expect(element.find('label').html()).toBe('Name'); + expect(element.find('input').length).toBe(1); + expect(element.find('input').val()).toBe('John Smith'); + }); + + it('Should display option fields as select with options', function () { + $scope.fields = [ + { + caption: 'State', + model: 'address.state', + type: 'select', + options: [ + { + caption: 'California', + value: 'CA' + }, + { + caption: 'New York', + value: 'NY' + }, + { + caption: 'Washington', + value: 'WA' + } + ] + } + ]; + compile(element)($scope); + $scope.$digest(); + + expect(element.children().length).toBe(1); + expect(element.children().find('select').length).toBe(1); + // 1 extra default option + expect(element.children().find('option').length).toBe(4); + }); +}); \ No newline at end of file From 9f08b068253f35af426537a5e3fca013822b39f7 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Mon, 5 May 2014 13:46:55 -0700 Subject: [PATCH 4/9] Add some basic docs to the README. --- README.md | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 528761d..053f947 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,93 @@ runtime. For example, this could be useful for building a generic database manip includes a form for manipulating a row of data, with the database itself providing the schema information at runtime. -This is still a work in progress and is not feature-complete nor well tested. It's probably best -not used in a production application without further work. +Usage +----- + +To make the directive available to your application, load the source file (or build it into your +application's bundle) and declare a dependency on the module. For example: + +```js + var app = angular.module(['ng', 'ngRoute', 'dynamic-form']); +``` + +With this dependency declared you can use the directive in any of your templates: + +```html + + + + + +``` + +The children of the `dynamic-table-form` element are field configurations, selected by the +key given in the `dynamic-field-type` attribute. The `fields` attribute is an AngularJS +expression evaluating to a field configuration as described below. The `data` attribute +is an expression evaluating to an object whose properties will be edited by the form. + +A field configuration must be provided so that the directive knows which fields to show. +This is just an array of field descriptions placed into the scope. Here's an example: + +```js + +$scope.fields = [ + { + caption: 'Name', + model: 'name', + type: 'string', + maxLength: 25 + }, + { + caption: 'Age', + model: 'age', + type: 'number' + }, + { + caption: 'State', + model: 'state', + type: 'choice', + options: [ + { + caption: 'California', + value: 'CA' + }, + { + caption: 'New York', + value: 'NY' + }, + { + caption: 'Washington', + value: 'WA' + } + ] + } +]; +```` + +The core properties of a field description are `caption`, `model` and `type`: + +* `caption` is the literal string to show next to the field. +* `model` is an Angular expression, relative to the form's data object, identifying where in the data structure this field's value belongs. +* `type` is matched with the `dynamic-field-type` attributes in the directive to identify the appropriate UI to show for this field. + +Other type-specific properties may be added and accessed from the type's template element. + +The `dynamic-table-form` directive will then create one child element for each +element in the field configuration, binding the appropriate field element to +the field's value. The field elements are evaluated in a scope with the following +members: + +* `value` is two-way bound to the result of the expression given in `model` in the field description. +* `config` is the field description itself, from which type-specific properties may be retrieved. + +All field elements should interact with `value` in some way. The most common way is to reference +it as the `ng-model` of a form element as shown in the HTML example earlier in this page. + +A more complete example is available in [example/](example/) in the repository, and you can also +[see the example live](http://saymedia.github.io/angularjs-dynamic-form/example/). + +License +------- Copyright 2013 Say Media Ltd. All Rights Reserved. See the LICENSE file for distribution terms. From 861e15bb5d8d1fa309d45b33e93d65dfc7584931 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Mon, 5 May 2014 14:05:47 -0700 Subject: [PATCH 5/9] Clarify the status of Collection Types in README. There are bits of code partially handling this, and one is shown in the example, so it's worth clarifying that this doesn't actually work yet to avoid confusion. --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index 053f947..9cb4ef9 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,27 @@ it as the `ng-model` of a form element as shown in the HTML example earlier in t A more complete example is available in [example/](example/) in the repository, and you can also [see the example live](http://saymedia.github.io/angularjs-dynamic-form/example/). +Collection Types +---------------- + +The original design for this library called for supporting special "collection types", which +are fields representing arrays or objects. The intent was that fields could have specifiers +like `array` which would cause the module to first look for a field type called +`array` which could then transclude in a field for each item -- in this example the field +element for `string`. This would allow a single field type to be created for editing arrays +of any type, delegating to another field type for editing individual elements. + +This feature is not yet implemented, although the collection type syntax can be parsed +and will be ignored. The practical implication of this for the moment is that it is not +possible to have field types containing `<` and `>` symbols. + +The example linked above contains an ``array`` field element, but it is inoperable. +Support for this may be added in a future version. + +In the mean time, arrays of specific types can be supported manually by the caller, by +creating a type name like `arrayOfString` and then populating that type's field element +with a UI for adding and removing strings to the array given in `value`. + License ------- From f02a92d30b6fe3011d081ffb90fc752a2ad386e3 Mon Sep 17 00:00:00 2001 From: Bryan Wyman Date: Wed, 7 May 2014 14:44:11 -0700 Subject: [PATCH 6/9] allow updates for angular maintenance --- bower.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index cc22610..3832c9b 100644 --- a/bower.json +++ b/bower.json @@ -4,10 +4,10 @@ "description": "AngularJS directive for creating dynamic forms based on schemas that aren't known until runtime", "license": "MIT", "dependencies": { - "angular": "1.2.7" + "angular": "~1.2.7" }, "devDependencies": { - "angular-mocks": "1.2.7", + "angular-mocks": "~1.2.7", "jasmine": "~1.3.1" } } From 4fc38416db9c5d071a249c89891c6b7f0cf8cda7 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Thu, 15 May 2014 07:43:03 -0700 Subject: [PATCH 7/9] Declare the main module of this package, for bower. --- bower.json | 1 + 1 file changed, 1 insertion(+) diff --git a/bower.json b/bower.json index 3832c9b..ec35536 100644 --- a/bower.json +++ b/bower.json @@ -3,6 +3,7 @@ "version": "0.0.0", "description": "AngularJS directive for creating dynamic forms based on schemas that aren't known until runtime", "license": "MIT", + "main": "src/angulardynamicform.js", "dependencies": { "angular": "~1.2.7" }, From bceeb4fd969ee036d068c79d84b6b04200348c70 Mon Sep 17 00:00:00 2001 From: Brad Choate Date: Tue, 10 Mar 2015 11:27:55 -0700 Subject: [PATCH 8/9] Assign a class for each row, named after the model. --- src/angulardynamicform.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/angulardynamicform.js b/src/angulardynamicform.js index 4700012..06504f9 100644 --- a/src/angulardynamicform.js +++ b/src/angulardynamicform.js @@ -94,6 +94,14 @@ var fieldScope = $scope.$new(true); fieldScope.config = field; + rowElem.addClass('control-group--' + + field.model.replace(/([a-z])([A-Z])/g, + function (m) { + return m[0] + '-' + m[1]; + } + ).toLowerCase() + ); + var modelGet = $parse(field.model); var modelSet = modelGet.assign; From b61ea4e8a5015d600e41685fcdea9914d52c2381 Mon Sep 17 00:00:00 2001 From: Brad Choate Date: Fri, 20 Apr 2018 13:37:30 -0700 Subject: [PATCH 9/9] Adding name, version properties to package.json --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index 2401fff..4fcc0c0 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,6 @@ { + "name": "angularjs-dynamic-form", + "version": "0.0.0", "devDependencies": { "grunt-karma": "^0.8.2", "grunt-cli": "^0.1.13",