-
-
Save renoirb/5524afa1e0a28304cd6ed5e29812630f to your computer and use it in GitHub Desktop.
Renoir's Challenge Use an JSON object to describe a form // source http://jsbin.com/kavume
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv="content-type" content="text/html; charset=utf-8" /> | |
<title>Renoir's Challenge</title> | |
<meta name="description" content="Use an JSON object to describe a form" /> | |
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> | |
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script> | |
<script src="https://npmcdn.com/api-check@latest/dist/api-check.js"></script> | |
<script src="https://npmcdn.com/[email protected]/dist/formly.js"></script> | |
<script src="https://npmcdn.com/[email protected]/dist/angular-formly-templates-bootstrap.js"></script> | |
</head> | |
<body ng-app="renoirChallenge" ng-controller="UserProfileCtrl as view"> | |
<div class="container"> | |
<h1>{{::view.title}}</h1> | |
<form ng-submit="view.onSubmit()" name="view.form" novalidate> | |
<formly-form model="view.model" fields="view.fields" options="view.options" form="view.form"> | |
<div class=well> | |
<button type="submit" class="btn btn-primary submit-button" ng-disabled="view.form.$invalid">Submit</button> | |
<button type="button" class="btn btn-default" ng-click="view.options.resetModel()">Reset</button> | |
</div> | |
</formly-form> | |
</form> | |
<h2>Form Data</h2> | |
<pre>{{view.model | json}}</pre> | |
</div> | |
<script id="jsbin-javascript"> | |
/*jshint esnext: true */ | |
/*global angular */ | |
/** | |
* Renoir's challenge | |
* | |
* Use input JSON object to describe a form. | |
* | |
* Relevant help I used: | |
* - http://angular-formly.com/ | |
* - https://hacks.mozilla.org/2015/08/es6-in-depth-modules/ | |
* - http://www.michaelbromley.co.uk/blog/350/exploring-es6-classes-in-angularjs-1-x | |
* - https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input | |
* - http://stackoverflow.com/questions/32113604/why-is-the-new-es6-find-method-not-recognised-in-jsbin | |
**/ | |
"use strict"; | |
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); | |
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | |
console.clear(); | |
var inputs = { | |
"email": { | |
"docs": "Email address of the user. Used for authentication. Must be unique.", | |
"type": "Text", | |
"presentation": { | |
"label": "Email", | |
"type": "email", | |
"order": 1 | |
}, | |
"validation": { | |
"required": true | |
} | |
}, | |
"password": { | |
"docs": "Encoded user password. Used for authentication. Write unencoded, saves encoded.", | |
"type": "Text", | |
"presentation": { | |
"label": "Password", | |
"type": "password", | |
"order": 2 | |
} | |
}, | |
"fullName": { | |
"docs": "Human's full name. For client display and human readability", | |
"type": "Text", | |
"presentation": { | |
"label": "Full Name", | |
"visible": "none", | |
"order": 9 | |
}, | |
"validation": { | |
"required": true | |
} | |
}, | |
"firstName": { | |
"docs": "Human's first name. For client display and human readability.", | |
"type": "Text", | |
"presentation": { | |
"label": "First Name", | |
"order": 3 | |
}, | |
"validation": { | |
"minLen": 1, | |
"maxLen": 50 | |
} | |
}, | |
"lastName": { | |
"docs": "Human's last name. For client display and human readability.", | |
"type": "Text", | |
"presentation": { | |
"label": "Last Name", | |
"order": 4 | |
}, | |
"validation": { | |
"minLen": 1, | |
"maxLen": 50 | |
} | |
} | |
}; | |
{ | |
var validFieldTypes; | |
(function () { | |
/** | |
* Prepare field prior to add them to controller fields | |
* | |
* Where we can SwitchCase logic we may want to add. | |
**/ | |
var prepareField = function prepareField(field, orig) { | |
var fType = validFieldTypes.find(function (v) { | |
return orig.presentation.type === v; | |
}) || null; | |
if (!!fType) { | |
field.templateOptions.type = fType; | |
} | |
// Lets cut some corners now. sorry about that! | |
if (!!orig.validation) { | |
if (!!orig.validation.minLen) { | |
field.templateOptions.minlength = orig.validation.minLen; | |
} | |
if (!!orig.validation.maxLen) { | |
field.templateOptions.maxlength = orig.validation.maxLen; | |
} | |
if (!!orig.validation.required) { | |
field.templateOptions.required = true; | |
} | |
} | |
return field; | |
}; | |
/** | |
* ECMAScript 2015 FTW! | |
* | |
* This *IS* a Module, and implicitly in strict mode | |
**/ | |
validFieldTypes = ['email', 'password']; | |
var UserProfile = (function () { | |
function UserProfile() { | |
_classCallCheck(this, UserProfile); | |
this.title = 'Your profile'; | |
this.model = {}; | |
this.options = {}; | |
var fields = {}, | |
ordering = []; | |
var _iteratorNormalCompletion = true; | |
var _didIteratorError = false; | |
var _iteratorError = undefined; | |
try { | |
for (var _iterator = Object.keys(inputs)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | |
var k = _step.value; | |
var iter = inputs[k], | |
dto = { templateOptions: {} }; | |
dto.key = k; | |
// Did not see use of any other input types | |
dto.type = 'input'; | |
dto.templateOptions.label = iter.presentation.label; | |
// I was unsure if you wanted this text there or not. | |
dto.templateOptions.description = iter.docs; | |
// I assumed order was ALWAYS there. Maybe I should not trust blindly | |
ordering[Number(iter.presentation.order)] = k; | |
fields[k] = prepareField(dto, iter); | |
} | |
} catch (err) { | |
_didIteratorError = true; | |
_iteratorError = err; | |
} finally { | |
try { | |
if (!_iteratorNormalCompletion && _iterator["return"]) { | |
_iterator["return"](); | |
} | |
} finally { | |
if (_didIteratorError) { | |
throw _iteratorError; | |
} | |
} | |
} | |
ordering = ordering.filter(function (v) { | |
return v !== undefined; | |
}); | |
this.fields = ordering.map(function (currentValue) { | |
return fields[currentValue]; | |
}); | |
} | |
_createClass(UserProfile, [{ | |
key: "onSubmit", | |
value: function onSubmit() { | |
alert(JSON.stringify(this.model), null, 2); | |
} | |
}]); | |
return UserProfile; | |
})(); | |
angular.module('renoirChallenge', ['formly', 'formlyBootstrap']).controller('UserProfileCtrl', UserProfile); | |
})(); | |
} | |
</script> | |
<script id="jsbin-source-html" type="text/html"><!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv="content-type" content="text/html; charset=utf-8" /> | |
<title>Renoir's Challenge</title> | |
<meta name="description" content="Use an JSON object to describe a form" /> | |
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> | |
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"><\/script> | |
<script src="https://npmcdn.com/api-check@latest/dist/api-check.js"><\/script> | |
<script src="https://npmcdn.com/[email protected]/dist/formly.js"><\/script> | |
<script src="https://npmcdn.com/[email protected]/dist/angular-formly-templates-bootstrap.js"><\/script> | |
</head> | |
<body ng-app="renoirChallenge" ng-controller="UserProfileCtrl as view"> | |
<div class="container"> | |
<h1>{{::view.title}}</h1> | |
<form ng-submit="view.onSubmit()" name="view.form" novalidate> | |
<formly-form model="view.model" fields="view.fields" options="view.options" form="view.form"> | |
<div class=well> | |
<button type="submit" class="btn btn-primary submit-button" ng-disabled="view.form.$invalid">Submit</button> | |
<button type="button" class="btn btn-default" ng-click="view.options.resetModel()">Reset</button> | |
</div> | |
</formly-form> | |
</form> | |
<h2>Form Data</h2> | |
<pre>{{view.model | json}}</pre> | |
</div> | |
</body> | |
</html> | |
</script> | |
<script id="jsbin-source-javascript" type="text/javascript">/*jshint esnext: true */ | |
/*global angular */ | |
/** | |
* Renoir's challenge | |
* | |
* Use input JSON object to describe a form. | |
* | |
* Relevant help I used: | |
* - http://angular-formly.com/ | |
* - https://hacks.mozilla.org/2015/08/es6-in-depth-modules/ | |
* - http://www.michaelbromley.co.uk/blog/350/exploring-es6-classes-in-angularjs-1-x | |
* - https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input | |
* - http://stackoverflow.com/questions/32113604/why-is-the-new-es6-find-method-not-recognised-in-jsbin | |
**/ | |
console.clear(); | |
var inputs = { | |
"email":{ | |
"docs":"Email address of the user. Used for authentication. Must be unique.", | |
"type":"Text", | |
"presentation" : { | |
"label":"Email", | |
"type":"email", | |
"order":1 | |
}, | |
"validation" : { | |
"required" : true | |
} | |
}, | |
"password":{ | |
"docs":"Encoded user password. Used for authentication. Write unencoded, saves encoded.", | |
"type":"Text", | |
"presentation" : { | |
"label":"Password", | |
"type": "password", | |
"order":2 | |
} | |
}, | |
"fullName" : { | |
"docs" : "Human's full name. For client display and human readability", | |
"type" : "Text", | |
"presentation" : { | |
"label" : "Full Name", | |
"visible":"none", | |
"order":9 | |
}, | |
"validation" : { | |
"required" : true | |
} | |
}, | |
"firstName":{ | |
"docs":"Human's first name. For client display and human readability.", | |
"type":"Text", | |
"presentation" : { | |
"label":"First Name", | |
"order":3 | |
}, | |
"validation" : { | |
"minLen":1, | |
"maxLen":50 | |
} | |
}, | |
"lastName":{ | |
"docs":"Human's last name. For client display and human readability.", | |
"type":"Text", | |
"presentation" : { | |
"label":"Last Name", | |
"order":4 | |
}, | |
"validation" : { | |
"minLen":1, | |
"maxLen":50 | |
} | |
} | |
}; | |
{ | |
/** | |
* ECMAScript 2015 FTW! | |
* | |
* This *IS* a Module, and implicitly in strict mode | |
**/ | |
var validFieldTypes = ['email','password']; | |
/** | |
* Prepare field prior to add them to controller fields | |
* | |
* Where we can SwitchCase logic we may want to add. | |
**/ | |
function prepareField(field, orig) { | |
let fType = validFieldTypes.find( v => orig.presentation.type === v ) || null; | |
if (!!fType) { | |
field.templateOptions.type = fType; | |
} | |
// Lets cut some corners now. sorry about that! | |
if (!!orig.validation) { | |
if (!!orig.validation.minLen) { | |
field.templateOptions.minlength = orig.validation.minLen; | |
} | |
if (!!orig.validation.maxLen) { | |
field.templateOptions.maxlength = orig.validation.maxLen; | |
} | |
if (!!orig.validation.required) { | |
field.templateOptions.required = true; | |
} | |
} | |
return field; | |
} | |
class UserProfile { | |
constructor() { | |
this.title = 'Your profile'; | |
this.model = {}; | |
this.options = {}; | |
let fields = {}, | |
ordering = []; | |
for (let k of Object.keys(inputs)) { | |
let iter = inputs[k] | |
, dto = {templateOptions:{}}; | |
dto.key = k; | |
// Did not see use of any other input types | |
dto.type = 'input'; | |
dto.templateOptions.label = iter.presentation.label; | |
// I was unsure if you wanted this text there or not. | |
dto.templateOptions.description = iter.docs; | |
// I assumed order was ALWAYS there. Maybe I should not trust blindly | |
ordering[Number(iter.presentation.order)] = k; | |
fields[k] = prepareField(dto, iter); | |
} | |
ordering = ordering.filter(v => v !== undefined); | |
this.fields = ordering.map( currentValue => fields[currentValue] ); | |
} | |
onSubmit() { | |
alert(JSON.stringify(this.model), null, 2); | |
} | |
} | |
angular | |
.module('renoirChallenge', [ | |
'formly' | |
,'formlyBootstrap' | |
]) | |
.controller('UserProfileCtrl', UserProfile); | |
}</script></body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*jshint esnext: true */ | |
/*global angular */ | |
/** | |
* Renoir's challenge | |
* | |
* Use input JSON object to describe a form. | |
* | |
* Relevant help I used: | |
* - http://angular-formly.com/ | |
* - https://hacks.mozilla.org/2015/08/es6-in-depth-modules/ | |
* - http://www.michaelbromley.co.uk/blog/350/exploring-es6-classes-in-angularjs-1-x | |
* - https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input | |
* - http://stackoverflow.com/questions/32113604/why-is-the-new-es6-find-method-not-recognised-in-jsbin | |
**/ | |
"use strict"; | |
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); | |
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | |
console.clear(); | |
var inputs = { | |
"email": { | |
"docs": "Email address of the user. Used for authentication. Must be unique.", | |
"type": "Text", | |
"presentation": { | |
"label": "Email", | |
"type": "email", | |
"order": 1 | |
}, | |
"validation": { | |
"required": true | |
} | |
}, | |
"password": { | |
"docs": "Encoded user password. Used for authentication. Write unencoded, saves encoded.", | |
"type": "Text", | |
"presentation": { | |
"label": "Password", | |
"type": "password", | |
"order": 2 | |
} | |
}, | |
"fullName": { | |
"docs": "Human's full name. For client display and human readability", | |
"type": "Text", | |
"presentation": { | |
"label": "Full Name", | |
"visible": "none", | |
"order": 9 | |
}, | |
"validation": { | |
"required": true | |
} | |
}, | |
"firstName": { | |
"docs": "Human's first name. For client display and human readability.", | |
"type": "Text", | |
"presentation": { | |
"label": "First Name", | |
"order": 3 | |
}, | |
"validation": { | |
"minLen": 1, | |
"maxLen": 50 | |
} | |
}, | |
"lastName": { | |
"docs": "Human's last name. For client display and human readability.", | |
"type": "Text", | |
"presentation": { | |
"label": "Last Name", | |
"order": 4 | |
}, | |
"validation": { | |
"minLen": 1, | |
"maxLen": 50 | |
} | |
} | |
}; | |
{ | |
var validFieldTypes; | |
(function () { | |
/** | |
* Prepare field prior to add them to controller fields | |
* | |
* Where we can SwitchCase logic we may want to add. | |
**/ | |
var prepareField = function prepareField(field, orig) { | |
var fType = validFieldTypes.find(function (v) { | |
return orig.presentation.type === v; | |
}) || null; | |
if (!!fType) { | |
field.templateOptions.type = fType; | |
} | |
// Lets cut some corners now. sorry about that! | |
if (!!orig.validation) { | |
if (!!orig.validation.minLen) { | |
field.templateOptions.minlength = orig.validation.minLen; | |
} | |
if (!!orig.validation.maxLen) { | |
field.templateOptions.maxlength = orig.validation.maxLen; | |
} | |
if (!!orig.validation.required) { | |
field.templateOptions.required = true; | |
} | |
} | |
return field; | |
}; | |
/** | |
* ECMAScript 2015 FTW! | |
* | |
* This *IS* a Module, and implicitly in strict mode | |
**/ | |
validFieldTypes = ['email', 'password']; | |
var UserProfile = (function () { | |
function UserProfile() { | |
_classCallCheck(this, UserProfile); | |
this.title = 'Your profile'; | |
this.model = {}; | |
this.options = {}; | |
var fields = {}, | |
ordering = []; | |
var _iteratorNormalCompletion = true; | |
var _didIteratorError = false; | |
var _iteratorError = undefined; | |
try { | |
for (var _iterator = Object.keys(inputs)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | |
var k = _step.value; | |
var iter = inputs[k], | |
dto = { templateOptions: {} }; | |
dto.key = k; | |
// Did not see use of any other input types | |
dto.type = 'input'; | |
dto.templateOptions.label = iter.presentation.label; | |
// I was unsure if you wanted this text there or not. | |
dto.templateOptions.description = iter.docs; | |
// I assumed order was ALWAYS there. Maybe I should not trust blindly | |
ordering[Number(iter.presentation.order)] = k; | |
fields[k] = prepareField(dto, iter); | |
} | |
} catch (err) { | |
_didIteratorError = true; | |
_iteratorError = err; | |
} finally { | |
try { | |
if (!_iteratorNormalCompletion && _iterator["return"]) { | |
_iterator["return"](); | |
} | |
} finally { | |
if (_didIteratorError) { | |
throw _iteratorError; | |
} | |
} | |
} | |
ordering = ordering.filter(function (v) { | |
return v !== undefined; | |
}); | |
this.fields = ordering.map(function (currentValue) { | |
return fields[currentValue]; | |
}); | |
} | |
_createClass(UserProfile, [{ | |
key: "onSubmit", | |
value: function onSubmit() { | |
alert(JSON.stringify(this.model), null, 2); | |
} | |
}]); | |
return UserProfile; | |
})(); | |
angular.module('renoirChallenge', ['formly', 'formlyBootstrap']).controller('UserProfileCtrl', UserProfile); | |
})(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment