Skip to content

Commit 5b6ecb9

Browse files
committed
Merge branch 'feature/builder' into development
2 parents befb55a + f03e62e commit 5b6ecb9

File tree

6 files changed

+609
-173
lines changed

6 files changed

+609
-173
lines changed

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

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,32 @@
11
angular.module('schemaForm').config(['schemaFormDecoratorsProvider', function(decoratorsProvider) {
22
var base = 'directives/decorators/bootstrap/';
33

4-
decoratorsProvider.createDecorator('bootstrapDecorator', {
5-
textarea: base + 'textarea.html',
6-
fieldset: base + 'fieldset.html',
7-
array: base + 'array.html',
8-
tabarray: base + 'tabarray.html',
9-
tabs: base + 'tabs.html',
10-
section: base + 'section.html',
11-
conditional: base + 'section.html',
12-
actions: base + 'actions.html',
13-
select: base + 'select.html',
14-
checkbox: base + 'checkbox.html',
15-
checkboxes: base + 'checkboxes.html',
16-
number: base + 'default.html',
17-
password: base + 'default.html',
18-
submit: base + 'submit.html',
19-
button: base + 'submit.html',
20-
radios: base + 'radios.html',
21-
'radios-inline': base + 'radios-inline.html',
22-
radiobuttons: base + 'radio-buttons.html',
23-
help: base + 'help.html',
24-
'default': base + 'default.html'
4+
decoratorsProvider.defineDecorator('bootstrapDecorator', {
5+
textarea: {template: base + 'textarea.html', replace: false},
6+
fieldset: {template: base + 'fieldset.html', replace: false},
7+
/*fieldset: {template: base + 'fieldset.html', replace: true, builder: function(args) {
8+
var children = args.build(args.form.items, args.path + '.items');
9+
console.log('fieldset children frag', children.childNodes)
10+
args.fieldFrag.appendChild(children);
11+
}},*/
12+
array: {template: base + 'array.html', replace: false},
13+
tabarray: {template: base + 'tabarray.html', replace: false},
14+
tabs: {template: base + 'tabs.html', replace: false},
15+
section: {template: base + 'section.html', replace: false},
16+
conditional: {template: base + 'section.html', replace: false},
17+
actions: {template: base + 'actions.html', replace: false},
18+
select: {template: base + 'select.html', replace: false},
19+
checkbox: {template: base + 'checkbox.html', replace: false},
20+
checkboxes: {template: base + 'checkboxes.html', replace: false},
21+
number: {template: base + 'default.html', replace: false},
22+
password: {template: base + 'default.html', replace: false},
23+
submit: {template: base + 'submit.html', replace: false},
24+
button: {template: base + 'submit.html', replace: false},
25+
radios: {template: base + 'radios.html', replace: false},
26+
'radios-inline': {template: base + 'radios-inline.html', replace: false},
27+
radiobuttons: {template: base + 'radio-buttons.html', replace: false},
28+
help: {template: base + 'help.html', replace: false},
29+
'default': {template: base + 'default.html', replace: false}
2530
}, []);
2631

2732
//manual use directives

src/directives/field.js

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
angular.module('schemaForm').directive('sfField',
2+
['$parse', '$compile', '$http', '$templateCache', '$interpolate', '$q', 'sfErrorMessage',
3+
'sfPath','sfSelect',
4+
function($parse, $compile, $http, $templateCache, $interpolate, $q, sfErrorMessage,
5+
sfPath, sfSelect) {
6+
7+
return {
8+
restrict: 'AE',
9+
replace: false,
10+
transclude: false,
11+
scope: true,
12+
require: '?^sfSchema',
13+
link: function(scope, element, attrs, sfSchema) {
14+
15+
//The ngModelController is used in some templates and
16+
//is needed for error messages,
17+
scope.$on('schemaFormPropagateNgModelController', function(event, ngModel) {
18+
event.stopPropagation();
19+
event.preventDefault();
20+
scope.ngModel = ngModel;
21+
});
22+
23+
//Keep error prone logic from the template
24+
scope.showTitle = function() {
25+
return scope.form && scope.form.notitle !== true && scope.form.title;
26+
};
27+
28+
scope.listToCheckboxValues = function(list) {
29+
var values = {};
30+
angular.forEach(list, function(v) {
31+
values[v] = true;
32+
});
33+
return values;
34+
};
35+
36+
scope.checkboxValuesToList = function(values) {
37+
var lst = [];
38+
angular.forEach(values, function(v, k) {
39+
if (v) {
40+
lst.push(k);
41+
}
42+
});
43+
return lst;
44+
};
45+
46+
scope.buttonClick = function($event, form) {
47+
if (angular.isFunction(form.onClick)) {
48+
form.onClick($event, form);
49+
} else if (angular.isString(form.onClick)) {
50+
if (sfSchema) {
51+
//evaluating in scope outside of sfSchemas isolated scope
52+
sfSchema.evalInParentScope(form.onClick, {'$event': $event, form: form});
53+
} else {
54+
scope.$eval(form.onClick, {'$event': $event, form: form});
55+
}
56+
}
57+
};
58+
59+
/**
60+
* Evaluate an expression, i.e. scope.$eval
61+
* but do it in sfSchemas parent scope sf-schema directive is used
62+
* @param {string} expression
63+
* @param {Object} locals (optional)
64+
* @return {Any} the result of the expression
65+
*/
66+
scope.evalExpr = function(expression, locals) {
67+
if (sfSchema) {
68+
//evaluating in scope outside of sfSchemas isolated scope
69+
return sfSchema.evalInParentScope(expression, locals);
70+
}
71+
72+
return scope.$eval(expression, locals);
73+
};
74+
75+
/**
76+
* Evaluate an expression, i.e. scope.$eval
77+
* in this decorators scope
78+
* @param {string} expression
79+
* @param {Object} locals (optional)
80+
* @return {Any} the result of the expression
81+
*/
82+
scope.evalInScope = function(expression, locals) {
83+
if (expression) {
84+
return scope.$eval(expression, locals);
85+
}
86+
};
87+
88+
/**
89+
* Interpolate the expression.
90+
* Similar to `evalExpr()` and `evalInScope()`
91+
* but will not fail if the expression is
92+
* text that contains spaces.
93+
*
94+
* Use the Angular `{{ interpolation }}`
95+
* braces to access properties on `locals`.
96+
*
97+
* @param {string} content The string to interpolate.
98+
* @param {Object} locals (optional) Properties that may be accessed in the
99+
* `expression` string.
100+
* @return {Any} The result of the expression or `undefined`.
101+
*/
102+
scope.interp = function(expression, locals) {
103+
return (expression && $interpolate(expression)(locals));
104+
};
105+
106+
//This works since we ot the ngModel from the array or the schema-validate directive.
107+
scope.hasSuccess = function() {
108+
if (!scope.ngModel) {
109+
return false;
110+
}
111+
return scope.ngModel.$valid &&
112+
(!scope.ngModel.$pristine || !scope.ngModel.$isEmpty(scope.ngModel.$modelValue));
113+
};
114+
115+
scope.hasError = function() {
116+
if (!scope.ngModel) {
117+
return false;
118+
}
119+
return scope.ngModel.$invalid && !scope.ngModel.$pristine;
120+
};
121+
122+
/**
123+
* DEPRECATED: use sf-messages instead.
124+
* Error message handler
125+
* An error can either be a schema validation message or a angular js validtion
126+
* error (i.e. required)
127+
*/
128+
scope.errorMessage = function(schemaError) {
129+
return sfErrorMessage.interpolate(
130+
(schemaError && schemaError.code + '') || 'default',
131+
(scope.ngModel && scope.ngModel.$modelValue) || '',
132+
(scope.ngModel && scope.ngModel.$viewValue) || '',
133+
scope.form,
134+
scope.options && scope.options.validationMessage
135+
);
136+
};
137+
138+
// Rebind our part of the form to the scope.
139+
var once = scope.$watch(attrs.sfField, function(form) {
140+
if (form) {
141+
console.warn('got form!!!!', form)
142+
// Workaround for 'updateOn' error from ngModelOptions
143+
// see https://github.com/Textalk/angular-schema-form/issues/255
144+
// and https://github.com/Textalk/angular-schema-form/issues/206
145+
form.ngModelOptions = form.ngModelOptions || {};
146+
scope.form = form;
147+
148+
/*
149+
//ok let's replace that template!
150+
//We do this manually since we need to bind ng-model properly and also
151+
//for fieldsets to recurse properly.
152+
var templatePromise;
153+
154+
// type: "template" is a special case. It can contain a template inline or an url.
155+
// otherwise we find out the url to the template and load them.
156+
if (form.type === 'template' && form.template) {
157+
templatePromise = $q.when(form.template);
158+
} else {
159+
var url = form.type === 'template' ? form.templateUrl : templateUrl(name, form);
160+
templatePromise = $http.get(url, {cache: $templateCache}).then(function(res) {
161+
return res.data;
162+
});
163+
}
164+
*/
165+
/*
166+
templatePromise.then(function(template) {
167+
if (form.key) {
168+
var key = form.key ?
169+
sfPathProvider.stringify(form.key).replace(/"/g, '"') : '';
170+
template = template.replace(
171+
/\$\$value\$\$/g,
172+
'model' + (key[0] !== '[' ? '.' : '') + key
173+
);
174+
}
175+
element.html(template);
176+
*/
177+
// Do we have a condition? Then we slap on an ng-if on all children,
178+
// but be nice to existing ng-if.
179+
/*if (form.condition) {
180+
181+
var evalExpr = 'evalExpr(form.condition,{ model: model, "arrayIndex": arrayIndex})';
182+
if (form.key) {
183+
evalExpr = 'evalExpr(form.condition,{ model: model, "arrayIndex": arrayIndex, "modelValue": model' + sfPath.stringify(form.key) + '})';
184+
}
185+
186+
angular.forEach(element.children(), function(child) {
187+
var ngIf = child.getAttribute('ng-if');
188+
child.setAttribute(
189+
'ng-if',
190+
ngIf ?
191+
'(' + ngIf +
192+
') || (' + evalExpr +')'
193+
: evalExpr
194+
);
195+
});
196+
}*/
197+
//$compile(element.contents())(scope);
198+
//});
199+
200+
// Where there is a key there is probably a ngModel
201+
if (form.key) {
202+
// It looks better with dot notation.
203+
scope.$on(
204+
'schemaForm.error.' + form.key.join('.'),
205+
function(event, error, validationMessage, validity) {
206+
if (validationMessage === true || validationMessage === false) {
207+
validity = validationMessage;
208+
validationMessage = undefined;
209+
}
210+
211+
if (scope.ngModel && error) {
212+
if (scope.ngModel.$setDirty()) {
213+
scope.ngModel.$setDirty();
214+
} else {
215+
// FIXME: Check that this actually works on 1.2
216+
scope.ngModel.$dirty = true;
217+
scope.ngModel.$pristine = false;
218+
}
219+
220+
// Set the new validation message if one is supplied
221+
// Does not work when validationMessage is just a string.
222+
if (validationMessage) {
223+
if (!form.validationMessage) {
224+
form.validationMessage = {};
225+
}
226+
form.validationMessage[error] = validationMessage;
227+
}
228+
229+
scope.ngModel.$setValidity(error, validity === true);
230+
231+
if (validity === true) {
232+
// Setting or removing a validity can change the field to believe its valid
233+
// but its not. So lets trigger its validation as well.
234+
scope.$broadcast('schemaFormValidate');
235+
}
236+
}
237+
});
238+
239+
// Clean up the model when the corresponding form field is $destroy-ed.
240+
// Default behavior can be supplied as a globalOption, and behavior can be overridden in the form definition.
241+
scope.$on('$destroy', function() {
242+
// If the entire schema form is destroyed we don't touch the model
243+
if (!scope.externalDestructionInProgress) {
244+
var destroyStrategy = form.destroyStrategy ||
245+
(scope.options && scope.options.destroyStrategy) || 'remove';
246+
// No key no model, and we might have strategy 'retain'
247+
if (form.key && destroyStrategy !== 'retain') {
248+
249+
// Get the object that has the property we wan't to clear.
250+
var obj = scope.model;
251+
if (form.key.length > 1) {
252+
obj = sfSelect(form.key.slice(0, form.key.length - 1), obj);
253+
}
254+
255+
// We can get undefined here if the form hasn't been filled out entirely
256+
if (obj === undefined) {
257+
return;
258+
}
259+
260+
// Type can also be a list in JSON Schema
261+
var type = (form.schema && form.schema.type) || '';
262+
263+
// Empty means '',{} and [] for appropriate types and undefined for the rest
264+
//console.log('destroy', destroyStrategy, form.key, type, obj);
265+
if (destroyStrategy === 'empty' && type.indexOf('string') !== -1) {
266+
obj[form.key.slice(-1)] = '';
267+
} else if (destroyStrategy === 'empty' && type.indexOf('object') !== -1) {
268+
obj[form.key.slice(-1)] = {};
269+
} else if (destroyStrategy === 'empty' && type.indexOf('array') !== -1) {
270+
obj[form.key.slice(-1)] = [];
271+
} else if (destroyStrategy === 'null') {
272+
obj[form.key.slice(-1)] = null;
273+
} else {
274+
delete obj[form.key.slice(-1)];
275+
}
276+
}
277+
}
278+
});
279+
}
280+
281+
once();
282+
}
283+
});
284+
}
285+
};
286+
}
287+
]);

0 commit comments

Comments
 (0)