Skip to content

Commit 52b6c0b

Browse files
committed
New type "conditional"
Same as section (uses same template actually), but with an ng-if attached to it so it can be rendered or not depending on an expression.
1 parent 1a2adc5 commit 52b6c0b

File tree

7 files changed

+136
-5
lines changed

7 files changed

+136
-5
lines changed

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ Schema Form currently supports the following form field types:
6161
|:--------------|:------------------------|
6262
| fieldset | a fieldset with legend |
6363
| section | just a div |
64+
| conditional | a section with a ```ng-if``` |
6465
| actions | horizontal button list, can only submit and buttons as items |
6566
| text | input with type text |
6667
| textarea | a textarea |
@@ -214,6 +215,56 @@ They do need a list of ```items``` to have as children.
214215
}
215216
```
216217
218+
A *conditional* is exactly the same as a *section*, i.e. a ```<div>``` with other form elements in
219+
it, hence they *section* needs an ```items``` property. They also need a ```condition``` which is
220+
a string with an angular expression. If that expression evaluates as thruthy the *conditional*
221+
will be rendered into the DOM otherwise not. The expression is evaluated in the parent scope of
222+
the ```sf-schema``` directive (the same as onClick on buttons). This is useful for hiding/showing
223+
parts of a form depending on another form control.
224+
225+
ex. A checkbox that shows an input field for a code when checked
226+
227+
```javascript
228+
function FormCtrl($scope) {
229+
$scope.person = {}
230+
231+
$scope.schema = {
232+
"type": "object",
233+
"properties": {
234+
"name": {
235+
"type": "string",
236+
"title": "Name"
237+
},
238+
"eligible": {
239+
"type": "boolean",
240+
"title": "Eligible for awesome things"
241+
},
242+
"code": {
243+
"type":"string"
244+
"title": "The Code"
245+
}
246+
}
247+
}
248+
249+
$scope.form = [
250+
"name",
251+
"eligible",
252+
{
253+
type: "conditional",
254+
condition: "person.eligible",
255+
items: [
256+
"code"
257+
]
258+
}
259+
]
260+
}
261+
```
262+
Note that angulars two-way binding automatically will update the conditional block, no need for
263+
event handlers and such. The condition need not reference a model value it could be anything in
264+
scope.
265+
266+
267+
217268
218269
*select* and *checkboxes* can take an object, ```titleMap```, where key is the value to be saved on the model
219270
and the value is the title of the option.

src/bootstrap-example.html

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,28 @@ <h3>Schema</h3>
117117
"description": "I agree to sell my undying soul",
118118
"type": "boolean",
119119
"default": true,
120+
},
121+
"soulserial": {
122+
"title": "Soul Serial No",
123+
"type": "string"
120124
}
121125
}
122126
};
123127

124128
$scope.form = [
125-
'*',
129+
"name",
130+
{ key: "favorite"},
131+
"attributes",
132+
"shoesize",
133+
"things",
134+
"soul",
135+
{
136+
type: "conditional",
137+
condition: "person.soul",
138+
items: [
139+
{ key: "soulserial", placeholder: "ex. 666" }
140+
]
141+
},
126142
{
127143
type: 'actions',
128144
items: [
@@ -168,7 +184,7 @@ <h3>Schema</h3>
168184

169185

170186
$scope.sayNo = function() {
171-
alert('Noooooooo')
187+
alert('Noooooooo');
172188
}
173189

174190
}

src/directives/decorators/bootstrap/bootstrap-decorator.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ angular.module('schemaForm').config(['schemaFormDecoratorsProvider',function(dec
55
textarea: base+'textarea.html',
66
fieldset: base+'fieldset.html',
77
section: base+'section.html',
8+
conditional: base+'section.html',
89
actions: base+'actions.html',
910
select: base+'select.html',
1011
checkbox: base+'checkbox.html',
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
<div>
2-
<bootstrap-decorator ng-repeat="item in form.items" form="item"></bootstrap-decorator>
1+
<div ng-if="!form.condition || evalExpr(form.condition)">
2+
<sf-decorator ng-repeat="item in form.items" form="item"></sf-decorator>
33
</div>

src/directives/schema-form.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ function($compile, schemaForm, schemaFormDecorators){
3434
},
3535
controller: ['$scope',function($scope){
3636
this.evalInParentScope = function(expr,locals){
37-
$scope.$parent.$eval(expr,locals);
37+
return $scope.$parent.$eval(expr,locals);
3838
};
3939
}],
4040
replace: false,

src/services/decorators.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,22 @@ angular.module('schemaForm').provider('schemaFormDecorators',['$compileProvider'
8888
}
8989
};
9090

91+
/**
92+
* Evaluate an expression, i.e. scope.$eval
93+
* but do it in sfSchemas parent scope sf-schema directive is used
94+
* @param {string} expression
95+
* @param {Object} locals (optional)
96+
* @return {Any} the result of the expression
97+
*/
98+
scope.evalExpr = function(expression,locals) {
99+
if (sfSchema) {
100+
//evaluating in scope outside of sfSchemas isolated scope
101+
return sfSchema.evalInParentScope(expression,locals);
102+
}
103+
104+
return scope.$eval(expression,locals);
105+
};
106+
91107
/**
92108
* Error message handler
93109
* An error can either be a schema validation message or a angular js validtion

test/schema-form-test.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,53 @@ describe('Schema form',function(){
680680
});
681681
});
682682

683+
it('should handle a simple div with a condition if "conditional" is specified',function(done){
684+
685+
inject(function($compile,$rootScope){
686+
var scope = $rootScope.$new();
687+
scope.person = { show: false };
688+
689+
scope.schema = exampleSchema;
690+
691+
scope.form = [{
692+
type: "conditional",
693+
condition: "person.show",
694+
items: [
695+
{
696+
key: 'name',
697+
notitle: true
698+
},
699+
{
700+
key: 'gender',
701+
notitle: true
702+
}
703+
]
704+
}];
705+
706+
var tmpl = angular.element('<form sf-schema="schema" sf-form="form" sf-model="person"></form>');
707+
708+
$compile(tmpl)(scope);
709+
$rootScope.$apply();
710+
711+
tmpl.children().length.should.be.equal(0);
712+
713+
//Do a setTimeout so we kan do another $apply
714+
setTimeout(function(){
715+
scope.person.show = true;
716+
scope.$apply();
717+
tmpl.children().length.should.be.equal(1);
718+
tmpl.children().eq(0).is('div').should.be.true;
719+
tmpl.children().eq(0).hasClass('btn-group').should.be.false;
720+
tmpl.children().eq(0).children().length.should.be.eq(2);
721+
done();
722+
},10);
723+
724+
});
725+
});
726+
727+
728+
729+
683730
it('should handle "action" groups, same as "section" but with a bootstrap class "btn-group"',function(){
684731

685732
inject(function($compile,$rootScope){

0 commit comments

Comments
 (0)