Skip to content

Commit 2b57c7e

Browse files
committed
Redraw form on schemaFormRedraw event
1 parent 8faef06 commit 2b57c7e

File tree

3 files changed

+153
-64
lines changed

3 files changed

+153
-64
lines changed

docs/index.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Documentation
33

44
1. [Basic Usage](#basic-usage)
55
1. [Handling Submit](#handling-submit)
6+
1. [Updating Form](#updating-form)
67
1. [Global Options](#global-options)
78
1. [Form defaults in schema](#form-defaults-in-schema)
89
1. [Form types](#form-types)
@@ -148,6 +149,28 @@ And the HTML would be something like this:
148149
```
149150
150151
152+
Updating Form
153+
-------------
154+
155+
Schema Form watches `sf-form` and `sf-schema` and will redraw the form if one or both changes, but
156+
only if they change completly, i.e. not the same object and/or form instance. For performance
157+
reasons we have opted to not watch schema and form deeply. So if you have updated a part of the
158+
schema or the form definition you can trigger a redraw by issuing the event `schemaFormRedraw`.
159+
160+
ex:
161+
```javascript
162+
function Ctrl($scope) {
163+
$scope.removeLastField = function() {
164+
$scope.form.pop()
165+
$scope.$broadcast('schemaFormRedraw')
166+
}
167+
}
168+
```
169+
170+
171+
172+
173+
151174
Global Options
152175
--------------
153176
Schema Form also have two options you can set globally via the `sf-options`
@@ -1078,6 +1101,16 @@ form. Below is a list of these events and how they are propagated.
10781101
|:--------------------:|:----------------------:|:-----:|:----------------------------------:|
10791102
| `sf-render-finished` | After form is rendered | emit | The sf-schema directives's element |
10801103
1104+
1105+
Schema form also listens to events.
1106+
1107+
| Event | What | Docs|
1108+
|:--------------------:|:----------------------:|:---------------------------------------:|
1109+
| `schemaFormValidate` | Validates all fields | [Handling Submit](#handling-submit) |
1110+
| `schemaFormRedraw` | Redraws form | [Updating Form](#updating-form) |
1111+
1112+
1113+
10811114
### Manual field insertion
10821115
There is a limited feature for controlling manually where a generated field should go so you can
10831116
,as an example, wrap it in custom html. Consider the feature experimental.

src/directives/schema-form.js

Lines changed: 82 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -58,88 +58,107 @@ angular.module('schemaForm')
5858
}
5959
}
6060
});
61-
//Since we are dependant on up to three
62-
//attributes we'll do a common watch
61+
6362
var lastDigest = {};
6463
var childScope;
65-
scope.$watch(function() {
6664

67-
var schema = scope.schema;
68-
var form = scope.initialForm || ['*'];
65+
// Common renderer function, can either be triggered by a watch or by an event.
66+
var render = function(schema, form) {
67+
var merged = schemaForm.merge(schema, form, ignore, scope.options);
68+
var frag = document.createDocumentFragment();
69+
70+
// Create a new form and destroy the old one.
71+
// Not doing keeps old form elements hanging around after
72+
// they have been removed from the DOM
73+
// https://github.com/Textalk/angular-schema-form/issues/200
74+
if (childScope) {
75+
childScope.$destroy();
76+
}
77+
childScope = scope.$new();
6978

70-
//The check for schema.type is to ensure that schema is not {}
71-
if (form && schema && schema.type &&
72-
(lastDigest.form !== form || lastDigest.schema !== schema) &&
73-
Object.keys(schema.properties).length > 0) {
74-
lastDigest.schema = schema;
75-
lastDigest.form = form;
79+
//make the form available to decorators
80+
childScope.schemaForm = {form: merged, schema: schema};
81+
82+
//clean all but pre existing html.
83+
element.children(':not(.schema-form-ignore)').remove();
7684

77-
var merged = schemaForm.merge(schema, form, ignore, scope.options);
78-
var frag = document.createDocumentFragment();
85+
// Find all slots.
86+
var slots = {};
87+
var slotsFound = element[0].querySelectorAll('*[sf-insert-field]');
7988

80-
// Create a new form and destroy the old one.
81-
// Not doing keeps old form elements hanging around after
82-
// they have been removed from the DOM
83-
// https://github.com/Textalk/angular-schema-form/issues/200
84-
if (childScope) {
85-
childScope.$destroy();
89+
for (var i = 0; i < slotsFound.length; i++) {
90+
slots[slotsFound[i].getAttribute('sf-insert-field')] = slotsFound[i];
91+
}
92+
93+
//Create directives from the form definition
94+
angular.forEach(merged, function(obj, i) {
95+
var n = document.createElement(attrs.sfDecorator ||
96+
snakeCase(schemaFormDecorators.defaultDecorator, '-'));
97+
n.setAttribute('form', 'schemaForm.form[' + i + ']');
98+
99+
// Check if there is a slot to put this in...
100+
if (obj.key) {
101+
var slot = slots[sfPath.stringify(obj.key)];
102+
if (slot) {
103+
while (slot.firstChild) {
104+
slot.removeChild(slot.firstChild);
105+
}
106+
slot.appendChild(n);
107+
return;
108+
}
86109
}
87-
childScope = scope.$new();
88110

89-
//make the form available to decorators
90-
childScope.schemaForm = {form: merged, schema: schema};
111+
// ...otherwise add it to the frag
112+
frag.appendChild(n);
91113

92-
//clean all but pre existing html.
93-
element.children(':not(.schema-form-ignore)').remove();
114+
});
94115

95-
// Find all slots.
96-
var slots = {};
97-
var slotsFound = element[0].querySelectorAll('*[sf-insert-field]');
116+
element[0].appendChild(frag);
98117

99-
for (var i = 0; i < slotsFound.length; i++) {
100-
slots[slotsFound[i].getAttribute('sf-insert-field')] = slotsFound[i];
101-
}
118+
//compile only children
119+
$compile(element.children())(childScope);
102120

103-
//Create directives from the form definition
104-
angular.forEach(merged, function(obj, i) {
105-
var n = document.createElement(attrs.sfDecorator ||
106-
snakeCase(schemaFormDecorators.defaultDecorator, '-'));
107-
n.setAttribute('form','schemaForm.form['+i+']');
108-
109-
// Check if there is a slot to put this in...
110-
if (obj.key) {
111-
var slot = slots[sfPath.stringify(obj.key)];
112-
if (slot) {
113-
while (slot.firstChild) {
114-
slot.removeChild(slot.firstChild);
115-
}
116-
slot.appendChild(n);
117-
return;
118-
}
121+
//ok, now that that is done let's set any defaults
122+
schemaForm.traverseSchema(schema, function(prop, path) {
123+
if (angular.isDefined(prop['default'])) {
124+
var val = sfSelect(path, scope.model);
125+
if (angular.isUndefined(val)) {
126+
sfSelect(path, scope.model, prop['default']);
119127
}
128+
}
129+
});
130+
131+
scope.$emit('sf-render-finished', element);
132+
};
120133

121-
// ...otherwise add it to the frag
122-
frag.appendChild(n);
134+
//Since we are dependant on up to three
135+
//attributes we'll do a common watch
136+
scope.$watch(function() {
123137

124-
});
138+
var schema = scope.schema;
139+
var form = scope.initialForm || ['*'];
125140

126-
element[0].appendChild(frag);
141+
//The check for schema.type is to ensure that schema is not {}
142+
if (form && schema && schema.type &&
143+
(lastDigest.form !== form || lastDigest.schema !== schema) &&
144+
Object.keys(schema.properties).length > 0) {
145+
lastDigest.schema = schema;
146+
lastDigest.form = form;
127147

128-
//compile only children
129-
$compile(element.children())(childScope);
148+
render(schema, form);
149+
}
150+
});
130151

131-
//ok, now that that is done let's set any defaults
132-
schemaForm.traverseSchema(schema, function(prop, path) {
133-
if (angular.isDefined(prop['default'])) {
134-
var val = sfSelect(path, scope.model);
135-
if (angular.isUndefined(val)) {
136-
sfSelect(path, scope.model, prop['default']);
137-
}
138-
}
139-
});
140-
};
141-
scope.$emit('sf-render-finished', element);
152+
// We also listen to the event schemaFormRedraw so you can manually trigger a change if
153+
// part of the form or schema is chnaged without it being a new instance.
154+
scope.$on('schemaFormRedraw', function() {
155+
var schema = scope.schema;
156+
var form = scope.initialForm || ['*'];
157+
if (schema) {
158+
render(schema, form);
159+
}
142160
});
161+
143162
}
144163
};
145164
}

test/directives/schema-form-test.js

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1704,7 +1704,7 @@ describe('directive',function(){
17041704

17051705
$compile(tmpl)(scope);
17061706
$rootScope.$apply();
1707-
1707+
17081708
tmpl.children().find('.schema-form-text').length.should.be.equal(1);
17091709

17101710
setTimeout(function() {
@@ -1717,5 +1717,42 @@ describe('directive',function(){
17171717
});
17181718
});
17191719

1720+
it('should redraw form on schemaFormRedraw event',function(done) {
1721+
1722+
inject(function($compile, $rootScope){
1723+
var scope = $rootScope.$new();
1724+
scope.person = {};
1725+
1726+
scope.schema = {
1727+
type: 'object',
1728+
properties: {
1729+
name: {type: 'string'}
1730+
}
1731+
};
1732+
1733+
scope.form = [{
1734+
key: 'name',
1735+
type: 'text'
1736+
}];
1737+
1738+
var tmpl = angular.element('<form sf-schema="schema" sf-form="form" sf-model="person"></form>');
1739+
1740+
$compile(tmpl)(scope);
1741+
$rootScope.$apply();
1742+
1743+
tmpl.children().find('.schema-form-text').length.should.be.equal(1);
1744+
tmpl.children().find('.schema-form-textarea').length.should.be.equal(0);
1745+
1746+
setTimeout(function() {
1747+
scope.form[0].type = 'textarea';
1748+
scope.$broadcast('schemaFormRedraw');
1749+
$rootScope.$apply();
1750+
tmpl.children().find('.schema-form-text').length.should.be.equal(0);
1751+
done();
1752+
}, 0);
1753+
1754+
});
1755+
});
1756+
17201757

17211758
});

0 commit comments

Comments
 (0)