This repository was archived by the owner on Aug 15, 2020. It is now read-only.
forked from json-schema-form/angular-schema-form
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathschema-form.js
170 lines (143 loc) · 5.76 KB
/
schema-form.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/*
FIXME: real documentation
<form sf-form="form" sf-schema="schema" sf-decorator="foobar"></form>
*/
angular.module('schemaForm')
.directive('sfSchema',
['$compile', 'schemaForm', 'schemaFormDecorators', 'sfSelect', 'sfPath', 'sfRetainModel',
function($compile, schemaForm, schemaFormDecorators, sfSelect, sfPath, sfRetainModel) {
var SNAKE_CASE_REGEXP = /[A-Z]/g;
var snakeCase = function(name, separator) {
separator = separator || '_';
return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
return (pos ? separator : '') + letter.toLowerCase();
});
};
return {
scope: {
schema: '=sfSchema',
initialForm: '=sfForm',
model: '=sfModel',
options: '=sfOptions'
},
controller: ['$scope', function($scope) {
this.evalInParentScope = function(expr, locals) {
return $scope.$parent.$eval(expr, locals);
};
}],
replace: false,
restrict: 'A',
transclude: true,
require: '?form',
link: function(scope, element, attrs, formCtrl, transclude) {
//expose form controller on scope so that we don't force authors to use name on form
scope.formCtrl = formCtrl;
//We'd like to handle existing markup,
//besides using it in our template we also
//check for ng-model and add that to an ignore list
//i.e. even if form has a definition for it or form is ["*"]
//we don't generate it.
var ignore = {};
transclude(scope, function(clone) {
clone.addClass('schema-form-ignore');
element.prepend(clone);
if (element[0].querySelectorAll) {
var models = element[0].querySelectorAll('[ng-model]');
if (models) {
for (var i = 0; i < models.length; i++) {
var key = models[i].getAttribute('ng-model');
//skip first part before .
ignore[key.substring(key.indexOf('.') + 1)] = true;
}
}
}
});
var lastDigest = {};
var childScope;
// Common renderer function, can either be triggered by a watch or by an event.
var render = function(schema, form) {
var merged = schemaForm.merge(schema, form, ignore, scope.options);
var frag = document.createDocumentFragment();
// Create a new form and destroy the old one.
// Not doing keeps old form elements hanging around after
// they have been removed from the DOM
// https://github.com/Textalk/angular-schema-form/issues/200
if (childScope) {
childScope.$destroy();
}
childScope = scope.$new();
//make the form available to decorators
childScope.schemaForm = {form: merged, schema: schema};
//clean all but pre existing html.
element.children(':not(.schema-form-ignore)').remove();
// Find all slots.
var slots = {};
var slotsFound = element[0].querySelectorAll('*[sf-insert-field]');
for (var i = 0; i < slotsFound.length; i++) {
slots[slotsFound[i].getAttribute('sf-insert-field')] = slotsFound[i];
}
//Create directives from the form definition
angular.forEach(merged, function(obj, i) {
var n = document.createElement(attrs.sfUseDecorator ||
snakeCase(schemaFormDecorators.defaultDecorator, '-'));
n.setAttribute('form', 'schemaForm.form[' + i + ']');
// Check if there is a slot to put this in...
if (obj.key) {
var slot = slots[sfPath.stringify(obj.key)];
if (slot) {
while (slot.firstChild) {
slot.removeChild(slot.firstChild);
}
slot.appendChild(n);
return;
}
}
// ...otherwise add it to the frag
frag.appendChild(n);
});
element[0].appendChild(frag);
//compile only children
$compile(element.children())(childScope);
//ok, now that that is done let's set any defaults
if (!scope.options || scope.options.setSchemaDefaults !== false) {
schemaForm.traverseSchema(schema, function(prop, path) {
if (angular.isDefined(prop['default'])) {
var val = sfSelect(path, scope.model);
if (angular.isUndefined(val)) {
sfSelect(path, scope.model, prop['default']);
}
}
});
}
scope.$emit('sf-render-finished', element);
};
//Since we are dependant on up to three
//attributes we'll do a common watch
scope.$watch(function() {
var schema = scope.schema;
var form = scope.initialForm || ['*'];
//The check for schema.type is to ensure that schema is not {}
if (form && schema && schema.type &&
(lastDigest.form !== form || lastDigest.schema !== schema) &&
Object.keys(schema.properties).length > 0) {
lastDigest.schema = schema;
lastDigest.form = form;
render(schema, form);
}
});
// We also listen to the event schemaFormRedraw so you can manually trigger a change if
// part of the form or schema is chnaged without it being a new instance.
scope.$on('schemaFormRedraw', function() {
var schema = scope.schema;
var form = scope.initialForm || ['*'];
if (schema) {
render(schema, form);
}
});
scope.$on('$destroy', function() {
sfRetainModel.setFlag(true);
});
}
};
}
]);