Skip to content

Commit df27aa0

Browse files
committed
Merge branch 'feature/conditionsconditionsconditions' into development
2 parents 5ada453 + bd43b7d commit df27aa0

File tree

5 files changed

+232
-172
lines changed

5 files changed

+232
-172
lines changed

docs/index.md

Lines changed: 149 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ Documentation
1717
1. [copyValueTo](#copyvalueto)
1818
1. [Specific options and types](#specific-options-and-types)
1919
1. [fieldset and section](#fieldset-and-section)
20-
1. [conditional](#conditional)
2120
1. [select and checkboxes](#select-and-checkboxes)
2221
1. [actions](#actions)
2322
1. [button](#button)
@@ -29,6 +28,7 @@ Documentation
2928
1. [Post process function](#post-process-function)
3029
1. [Events](#events)
3130
1. [Manual field insertion](#manual-field-insertion)
31+
1. [Deprecated fields](#deprecated-fields)
3232
1. [Extending Schema Form](extending.md)
3333

3434
Basic Usage
@@ -208,7 +208,6 @@ Schema Form currently supports the following form field types out of the box:
208208
|:--------------|:------------------------|
209209
| fieldset | a fieldset with legend |
210210
| section | just a div |
211-
| conditional | a section with a ```ng-if``` |
212211
| actions | horizontal button list, can only submit and buttons as items |
213212
| text | input with type text |
214213
| textarea | a textarea |
@@ -343,7 +342,8 @@ General options most field types can handle:
343342
// and their items will inherit it.
344343
htmlClass: "street foobar", // CSS Class(es) to be added to the container div
345344
fieldHtmlClass: "street" // CSS Class(es) to be added to field input (or similar)
346-
copyValueTo: ["address.street"] // Copy values to these schema keys.
345+
copyValueTo: ["address.street"], // Copy values to these schema keys.
346+
condition: "person.age < 18" // Show or hide field depending on an angular expression
347347
}
348348
```
349349
@@ -461,7 +461,7 @@ See [Global Options](#global-options) for an example how you set entire form
461461
to validate on blur.
462462
463463
### copyValueTo
464-
This option has a very specific use case. Imagine you have the same option in several places, but you want them to be controlled from just one field. You specify what keys the value should be copied to, and the *viewValue* will be copied to these keys on the model. **Note: changing the model directly will not copy the value, it's intended for copying user input**. The recieving fields can be shown, but the intent for them is to be hidden.
464+
This option has a very specific use case. Imagine you have the same option in several places, but you want them to be controlled from just one field. You specify what keys the value should be copied to, and the *viewValue* will be copied to these keys on the model. **Note: changing the model directly will not copy the value, it's intended for copying user input**. The recieving fields can be shown, but the intent for them is to be hidden.
465465
466466
Ex.
467467
```javascript
@@ -471,29 +471,12 @@ Ex.
471471
}
472472
```
473473
474-
Specific options and types
475-
--------------------------
476-
477-
### fieldset and section
474+
### condition
475+
The `condition` option lets you hide or show a field depending on an angular expression. Beneath
476+
the surface it uses `ng-if` so the hidden field is *not* part of the form.
478477
479-
*fieldset* and *section* doesn't need a key. You can create generic groups with them.
480-
They do need a list of ```items``` to have as children.
481-
```javascript
482-
{
483-
type: "fieldset",
484-
items: [
485-
"name",
486-
{ key: "surname", notitle: true }
487-
]
488-
}
489-
```
490-
491-
### conditional
492-
493-
A *conditional* is exactly the same as a *section*, i.e. a `<div>` with other form elements in
494-
it, hence they need an `items` property. They also need a `condition` which is
495-
a string with an angular expression. If that expression evaluates as thruthy the *conditional*
496-
will be rendered into the DOM otherwise not. The expression is evaluated in the parent scope of
478+
`condition` should be a string with an angular expression. If that expression evaluates as thruthy
479+
the field will be rendered into the DOM otherwise not. The expression is evaluated in the parent scope of
497480
the `sf-schema` directive (the same as onClick on buttons) but with access to the current model
498481
and current array index under the name `model` and `arrayIndex`. This is useful for hiding/showing
499482
parts of a form depending on another form control.
@@ -526,17 +509,14 @@ function FormCtrl($scope) {
526509
"name",
527510
"eligible",
528511
{
529-
type: "conditional",
530-
condition: "person.eligible", //or "model.eligable"
531-
items: [
532-
"code"
533-
]
512+
key: "code",
513+
condition: "person.eligible", //or "model.eligable"
534514
}
535515
]
536516
}
537517
```
538-
Note that angulars two-way binding automatically will update the conditional block, no need for
539-
event handlers and such. The condition need not reference a model value it could be anything in
518+
Note that angulars two-way binding automatically will update the conditional field, no need for
519+
event handlers and such. The condition need not reference a model value it could be anything on
540520
scope.
541521
542522
The same example, but inside an array:
@@ -578,19 +558,37 @@ function FormCtrl($scope) {
578558
"persons[].name",
579559
"persons[].eligible",
580560
{
581-
type: "conditional",
582-
condition: "persons[arrayIndex].eligible", //or "model.eligable"
583-
items: [
584-
"persons[].code"
585-
]
561+
key: "persons[].code",
562+
condition: "persons[arrayIndex].eligible", //or "model[arrayIndex].eligable"
586563
}
587564
]
588565
}
589566
]
590567
}
591568
```
592569
593-
Note that arrays inside arrays won't work with conditional.
570+
Note that arrays inside arrays won't work with conditions.
571+
572+
573+
574+
575+
Specific options and types
576+
--------------------------
577+
578+
### fieldset and section
579+
580+
*fieldset* and *section* doesn't need a key. You can create generic groups with them.
581+
They do need a list of ```items``` to have as children.
582+
```javascript
583+
{
584+
type: "fieldset",
585+
items: [
586+
"name",
587+
{ key: "surname", notitle: true }
588+
]
589+
}
590+
```
591+
594592
595593
### select and checkboxes
596594
@@ -1112,3 +1110,115 @@ $scope.form = [
11121110
<!-- the rest of the form, i.e. name and comment will be generated here -->
11131111
</form>
11141112
```
1113+
1114+
1115+
1116+
Deprecated fields
1117+
-----------------
1118+
1119+
### conditional
1120+
1121+
The *conditional* type is now deprecated since every form type now supports the form option
1122+
`condition`.
1123+
1124+
A *conditional* is exactly the same as a *section*, i.e. a `<div>` with other form elements in
1125+
it, hence they need an `items` property. They also need a `condition` which is
1126+
a string with an angular expression. If that expression evaluates as thruthy the *conditional*
1127+
will be rendered into the DOM otherwise not. The expression is evaluated in the parent scope of
1128+
the `sf-schema` directive (the same as onClick on buttons) but with access to the current model
1129+
and current array index under the name `model` and `arrayIndex`. This is useful for hiding/showing
1130+
parts of a form depending on another form control.
1131+
1132+
ex. A checkbox that shows an input field for a code when checked
1133+
1134+
```javascript
1135+
function FormCtrl($scope) {
1136+
$scope.person = {}
1137+
1138+
$scope.schema = {
1139+
"type": "object",
1140+
"properties": {
1141+
"name": {
1142+
"type": "string",
1143+
"title": "Name"
1144+
},
1145+
"eligible": {
1146+
"type": "boolean",
1147+
"title": "Eligible for awesome things"
1148+
},
1149+
"code": {
1150+
"type":"string"
1151+
"title": "The Code"
1152+
}
1153+
}
1154+
}
1155+
1156+
$scope.form = [
1157+
"name",
1158+
"eligible",
1159+
{
1160+
type: "conditional",
1161+
condition: "person.eligible", //or "model.eligable"
1162+
items: [
1163+
"code"
1164+
]
1165+
}
1166+
]
1167+
}
1168+
```
1169+
Note that angulars two-way binding automatically will update the conditional block, no need for
1170+
event handlers and such. The condition need not reference a model value it could be anything in
1171+
scope.
1172+
1173+
The same example, but inside an array:
1174+
1175+
```javascript
1176+
function FormCtrl($scope) {
1177+
$scope.persons = []
1178+
1179+
$scope.schema = {
1180+
"type": "object",
1181+
"properties": {
1182+
"persons": {
1183+
"type": "array",
1184+
"items": {
1185+
"type": "object",
1186+
"properties": {
1187+
"name": {
1188+
"type": "string",
1189+
"title": "Name"
1190+
},
1191+
"eligible": {
1192+
"type": "boolean",
1193+
"title": "Eligible for awesome things"
1194+
},
1195+
"code": {
1196+
"type":"string"
1197+
"title": "The Code"
1198+
}
1199+
}
1200+
}
1201+
}
1202+
}
1203+
}
1204+
1205+
$scope.form = [
1206+
{
1207+
"key": "persons",
1208+
"items": [
1209+
"persons[].name",
1210+
"persons[].eligible",
1211+
{
1212+
type: "conditional",
1213+
condition: "persons[arrayIndex].eligible", //or "model.eligable"
1214+
items: [
1215+
"persons[].code"
1216+
]
1217+
}
1218+
]
1219+
}
1220+
]
1221+
}
1222+
```
1223+
1224+
Note that arrays inside arrays won't work with conditional.

examples/data/array.json

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,10 @@
5252
"comments[].name",
5353
"comments[].email",
5454
{
55-
"type": "conditional",
55+
"key": "comments[].spam",
56+
"type": "checkbox",
57+
"title": "Yes I want spam.",
5658
"condition": "model.comments[arrayIndex].email",
57-
"items": [
58-
{
59-
"key": "comments[].spam",
60-
"type": "checkbox",
61-
"title": "Yes I want spam."
62-
}
63-
]
6459
},
6560
{
6661
"key": "comments[].comment",
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
<div class="schema-form-section {{form.htmlClass}}"
2-
ng-if="!form.condition || evalExpr(form.condition,{ model: model, 'arrayIndex': arrayIndex })">
1+
<div class="schema-form-section {{form.htmlClass}}">
32
<sf-decorator ng-repeat="item in form.items" form="item"></sf-decorator>
43
</div>

src/services/decorators.js

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -40,29 +40,6 @@ angular.module('schemaForm').provider('schemaFormDecorators',
4040
scope: true,
4141
require: '?^sfSchema',
4242
link: function(scope, element, attrs, sfSchema) {
43-
//rebind our part of the form to the scope.
44-
var once = scope.$watch(attrs.form, function(form) {
45-
46-
if (form) {
47-
scope.form = form;
48-
49-
//ok let's replace that template!
50-
//We do this manually since we need to bind ng-model properly and also
51-
//for fieldsets to recurse properly.
52-
var url = templateUrl(name, form);
53-
$http.get(url, {cache: $templateCache}).then(function(res) {
54-
var key = form.key ?
55-
sfPathProvider.stringify(form.key).replace(/"/g, '&quot;') : '';
56-
var template = res.data.replace(
57-
/\$\$value\$\$/g,
58-
'model' + (key[0] !== '[' ? '.' : '') + key
59-
);
60-
element.html(template);
61-
$compile(element.contents())(scope);
62-
});
63-
once();
64-
}
65-
});
6643

6744
//Keep error prone logic from the template
6845
scope.showTitle = function() {
@@ -158,8 +135,47 @@ angular.module('schemaForm').provider('schemaFormDecorators',
158135

159136
//Otherwise we only have input number not being a number
160137
return 'Not a number';
161-
162138
};
139+
140+
// Rebind our part of the form to the scope.
141+
var once = scope.$watch(attrs.form, function(form) {
142+
if (form) {
143+
scope.form = form;
144+
145+
//ok let's replace that template!
146+
//We do this manually since we need to bind ng-model properly and also
147+
//for fieldsets to recurse properly.
148+
var url = templateUrl(name, form);
149+
$http.get(url, {cache: $templateCache}).then(function(res) {
150+
var key = form.key ?
151+
sfPathProvider.stringify(form.key).replace(/"/g, '&quot;') : '';
152+
var template = res.data.replace(
153+
/\$\$value\$\$/g,
154+
'model' + (key[0] !== '[' ? '.' : '') + key
155+
);
156+
element.html(template);
157+
158+
// Do we have a condition? Then we slap on an ng-if on all children,
159+
// but be nice to existing ng-if.
160+
if (form.condition) {
161+
element.children().each(function() {
162+
var ngIf = this.getAttribute('ng-if');
163+
this.setAttribute(
164+
'ng-if',
165+
ngIf ?
166+
'(' + ngIf +
167+
') || (evalExpr(form.condition,{ model: model, "arrayIndex": arrayIndex }))'
168+
: 'evalExpr(form.condition,{ model: model, "arrayIndex": arrayIndex })'
169+
);
170+
});
171+
}
172+
173+
$compile(element.contents())(scope);
174+
});
175+
176+
once();
177+
}
178+
});
163179
}
164180
};
165181
}

0 commit comments

Comments
 (0)