Skip to content

Commit 3d514d2

Browse files
committed
Manual field definitions
Reuse decorator functions in handcrafted forms.
1 parent 32f98dc commit 3d514d2

File tree

8 files changed

+219
-25
lines changed

8 files changed

+219
-25
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div class="btn-group" ng-transclude></div>
Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,51 @@
11
angular.module('schemaForm').config(['schemaFormDecoratorsProvider',function(decoratorsProvider){
2+
var base = 'directives/decorators/bootstrap/';
23

3-
decoratorsProvider.create('bootstrapDecorator',{
4-
textarea: 'directives/decorators/bootstrap/textarea.html',
5-
fieldset: 'directives/decorators/bootstrap/fieldset.html',
6-
section: 'directives/decorators/bootstrap/section.html',
7-
actions: 'directives/decorators/bootstrap/actions.html',
8-
select: 'directives/decorators/bootstrap/select.html',
9-
checkbox: 'directives/decorators/bootstrap/checkbox.html',
10-
checkboxes: 'directives/decorators/bootstrap/checkboxes.html',
11-
number: 'directives/decorators/bootstrap/default.html',
12-
submit: 'directives/decorators/bootstrap/submit.html',
13-
button: 'directives/decorators/bootstrap/submit.html',
14-
'default': 'directives/decorators/bootstrap/default.html'
4+
decoratorsProvider.createDecorator('bootstrapDecorator',{
5+
textarea: base+'textarea.html',
6+
fieldset: base+'fieldset.html',
7+
section: base+'section.html',
8+
actions: base+'actions.html',
9+
select: base+'select.html',
10+
checkbox: base+'checkbox.html',
11+
checkboxes: base+'checkboxes.html',
12+
number: base+'default.html',
13+
submit: base+'submit.html',
14+
button: base+'submit.html',
15+
'default': base+'default.html'
1516
},[
1617
function(form){
1718
if (form.readonly && form.key && form.type !== 'fieldset') {
18-
return 'directives/decorators/bootstrap/readonly.html';
19+
return base+'readonly.html';
1920
}
2021
}
2122
]);
2223

23-
}]);
24+
//manual use directives
25+
decoratorsProvider.createDirectives({
26+
textarea: base+'textarea.html',
27+
select: base+'select.html',
28+
checkbox: base+'checkbox.html',
29+
checkboxes: base+'checkboxes.html',
30+
number: base+'default.html',
31+
submit: base+'submit.html',
32+
button: base+'submit.html',
33+
text: base+'default.html',
34+
date: base+'default.html',
35+
password: base+'default.html',
36+
input: base+'default.html'
37+
});
38+
39+
}]).directive('sfFieldset',function(){
40+
return {
41+
transclude: true,
42+
scope: true,
43+
templateUrl: 'directives/decorators/bootstrap/fieldset-trcl.html',
44+
link: function(scope,element,attrs) {
45+
scope.title = scope.$eval(attrs.title);
46+
}
47+
};
48+
});
2449

2550

2651

src/directives/decorators/bootstrap/default.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<div class="form-group" ng-class="{'has-error': hasError()}">
22
<label ng-show="showTitle()">{{form.title}}</label>
33

4-
<input type="{{form.type}}"
4+
<input ng-show="form.key"
5+
type="{{form.type}}"
56
placeholder="{{form.placeholder}}"
67
class="form-control"
78
ng-required="form.required"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<fieldset ng-disabled="form.readonly">
2+
<legend ng-show="form.title">{{ form.title }}</legend>
3+
<div ng-transclude></div>
4+
</fieldset>

src/directives/schema-validate.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ angular.module('schemaForm').directive('schemaValidate',function(){
1313
schema = scope.$eval(attrs.schemaValidate);
1414
}
1515

16+
//Still might be undefined, especially if form has no schema...
17+
if (!schema) {
18+
return viewValue;
19+
}
20+
1621
//required is handled by ng-required
1722
if (angular.isUndefined(viewValue)) {
1823
return undefined;

src/field-example.html

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<!DOCTYPE html>
2+
<html ng-app="test">
3+
<head>
4+
<title>Boostrap Schema Form example</title>
5+
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
6+
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap-theme.min.css">
7+
<style type="text/css">
8+
9+
body,html {
10+
min-height: 1400px;
11+
}
12+
13+
.red {
14+
border: 1px solid red;
15+
background: #fee;
16+
}
17+
18+
</style>
19+
</head>
20+
<body ng-controller="TestCtrl">
21+
22+
<div class="col-md-12">
23+
<h1>Field Example</h1>
24+
25+
<div class="col-sm-12">
26+
<form name="ngform">
27+
<sf-fieldset title="Hardcoded Example">
28+
<sf-text title="Name"
29+
key="name"
30+
model="person"
31+
description="Anything but Sue"
32+
schema="{ type: 'string', minLength: 3}"
33+
placeholder="Your name please"></sf-text>
34+
35+
<sf-select title="Taste" title-map="titles" key="taste" model="person"></sf-select>
36+
37+
<!-- actions is a special case that can't be easily hardcoded -->
38+
<sf-decorator form="{
39+
type: 'actions',
40+
items: [
41+
{ type: 'submit', title: 'Ok'},
42+
{ type: 'button', title: 'cancel'}
43+
]}"></sf-decorator>
44+
45+
</sf-fieldset>
46+
</form>
47+
<h3>Model</h3>
48+
<pre>{{pretty()}}</pre>
49+
</div>
50+
51+
</div>
52+
</div>
53+
<script type="text/javascript" src="http://code.jquery.com/jquery-2.1.0.min.js"></script>
54+
<script type="text/javascript" src="../bower_components/tv4/tv4.js"></script>
55+
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.min.js"></script>
56+
<script type="text/javascript" src="module.js"></script>
57+
<script type="text/javascript" src="services/schema-form.js"></script>
58+
<script type="text/javascript" src="services/decorators.js"></script>
59+
<script type="text/javascript" src="directives/decorators/bootstrap/bootstrap-decorator.js"></script>
60+
<script type="text/javascript" src="directives/schema-form.js"></script>
61+
<script type="text/javascript" src="directives/schema-validate.js"></script>
62+
<script type="text/javascript">
63+
64+
angular.module('test',['schemaForm']);
65+
66+
function TestCtrl($scope){
67+
$scope.person = { taste: 'NaN' };
68+
$scope.pretty = function(){
69+
return JSON.stringify($scope.person,undefined,2,2);
70+
};
71+
72+
$scope.titles = {
73+
'NaN': 'Not a freaking number',
74+
'undefind': 'Just not defined',
75+
'null': 'Brainfart'
76+
};
77+
}
78+
79+
</script>
80+
</body>
81+
</html>

src/services/decorators.js

Lines changed: 86 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ angular.module('schemaForm').provider('schemaFormDecorators',['$compileProvider'
4242
//rebind our part of the form to the scope.
4343
var once = scope.$watch(attrs.form,function(form){
4444

45-
4645
if (form) {
4746
scope.form = form;
4847

@@ -51,7 +50,7 @@ angular.module('schemaForm').provider('schemaFormDecorators',['$compileProvider'
5150
//for fieldsets to recurse properly.
5251
var url = templateUrl(name,form);
5352
$http.get(url,{ cache: $templateCache }).then(function(res){
54-
var template = res.data.replace(/\$\$value\$\$/g,'model.'+form.key);
53+
var template = res.data.replace(/\$\$value\$\$/g,'model.'+(form.key || ""));
5554
$compile(template)(scope,function(clone){
5655
element.replaceWith(clone);
5756
});
@@ -87,24 +86,78 @@ angular.module('schemaForm').provider('schemaFormDecorators',['$compileProvider'
8786
}]);
8887
};
8988

89+
var createManualDirective = function(type,templateUrl,transclude) {
90+
transclude = angular.isDefined(transclude)? transclude : false;
91+
$compileProvider.directive('sf'+angular.uppercase(type[0])+type.substr(1), function(){
92+
return {
93+
restrict: "EAC",
94+
scope: true,
95+
replace: true,
96+
transclude: transclude,
97+
template: '<sf-decorator form="form"></sf-decorator>',
98+
link: function(scope,element,attrs) {
99+
var watchThis = {
100+
'items': 'c',
101+
'titleMap': 'c',
102+
'schema': 'c'
103+
};
104+
var form = { type: type };
105+
var once = true;
106+
angular.forEach(attrs,function(value,name){
107+
if (name[0] !== '$' && name.indexOf('ng') !== 0 && name !== 'sfField') {
108+
109+
var updateForm = function(val){
110+
if (angular.isDefined(val) && val !== form[name]) {
111+
form[name] = val;
112+
113+
//when we have type, and if specified key we apply it on scope.
114+
if (once && form.type && (form.key || angular.isUndefined(attrs.key))) {
115+
scope.form = form;
116+
once = false;
117+
}
118+
}
119+
};
120+
121+
if (name === 'model') {
122+
//"model" is bound to scope under the name "model" since this is what the decorators
123+
//know and love.
124+
scope.$watch(value,function(val){
125+
if (val && scope.model !== val) {
126+
scope.model = val;
127+
}
128+
});
129+
} else if (watchThis[name] === 'c') {
130+
//watch collection
131+
scope.$watchCollection(value,updateForm);
132+
} else {
133+
//$observe
134+
attrs.$observe(name,updateForm);
135+
}
136+
}
137+
});
138+
}
139+
};
140+
});
141+
};
142+
143+
144+
90145
/**
91-
* Create a decorator directive
146+
* Create a decorator directive and its sibling "manual" use directives.
92147
* The directive can be used to create form fields or other form entities.
93148
* It can be used in conjunction with <schema-form> directive in which case the decorator is
94149
* given it's configuration via a the "form" attribute.
95150
*
96-
* ex. Basic usage with form and schema
97-
* <sf-decorator form="myform" schema="myschema"></sf-decorator>
98-
*
99-
* ex. "Manual" usage
100-
* <sf-decorator sf-type="" sf-title=""
151+
* ex. Basic usage
152+
* <sf-decorator form="myform"></sf-decorator>
153+
**
101154
* @param {string} name directive name (CamelCased)
102155
* @param {Object} mappings, an object that maps "type" => "templateUrl"
103156
* @param {Array} rules (optional) a list of functions, function(form){}, that are each tried in turn,
104157
* if they return a string then that is used as the templateUrl. Rules come before
105158
* mappings.
106159
*/
107-
this.create = function(name,mappings,rules){
160+
this.createDecorator = function(name,mappings,rules){
108161
directives[name] = {
109162
mappings: mappings || {},
110163
rules: rules || []
@@ -116,6 +169,30 @@ angular.module('schemaForm').provider('schemaFormDecorators',['$compileProvider'
116169
createDirective(name);
117170
};
118171

172+
/**
173+
* Creates a directive of a decorator
174+
* Usable when you want to use the decorators without using <schema-form> directive.
175+
* Specifically when you need to reuse styling.
176+
*
177+
* ex. createDirective('text','...')
178+
* <sf-text title="foobar" model="person" key="name" schema="schema"></sf-text>
179+
*
180+
* @param {string} type The type of the directive, resulting directive will have sf- prefixed
181+
* @param {string} templateUrl
182+
* @param {boolean} transclude (optional) sets transclude option of directive, defaults to false.
183+
*/
184+
this.createDirective = createManualDirective;
185+
186+
/**
187+
* Same as createDirective, but takes an object where key is 'type' and value is 'templateUrl'
188+
* Useful for batching.
189+
* @param {Object} mappings
190+
*/
191+
this.createDirectives = function(mappings) {
192+
angular.forEach(mappings,function(url,type){
193+
createManualDirective(type,url);
194+
});
195+
};
119196

120197
/**
121198
* Getter for directive mappings

test/schema-form-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -805,7 +805,7 @@ describe('Schema form',function(){
805805
describe('decorator factory service',function(){
806806
it('should enable you to create new decorator directives',function(){
807807
module(function(schemaFormDecoratorsProvider){
808-
schemaFormDecoratorsProvider.create('foobar',{ 'foo':'/bar.html' },[angular.noop]);
808+
schemaFormDecoratorsProvider.createDecorator('foobar',{ 'foo':'/bar.html' },[angular.noop]);
809809
});
810810

811811
inject(function($rootScope,$compile,$templateCache){

0 commit comments

Comments
 (0)