Skip to content

Commit 266a69b

Browse files
committedJul 21, 2015
Arrays! Now with validation support.
1 parent 84c23ff commit 266a69b

File tree

2 files changed

+73
-58
lines changed

2 files changed

+73
-58
lines changed
 

‎src/array.html

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,31 @@
22
sf-field-model="sf-new-array"
33
sf-new-array>
44
<h3 ng-show="form.title && form.notitle !== true"></h3>
5-
<ol class="list-group" ui-sortable sf-field-model ng-model schema-validate="form">
5+
<ol class="list-group" ui-sortable>
66
<li class="list-group-item {{form.fieldHtmlClass}} schema-form-array-items"
77
sf-field-model="ng-repeat"
88
ng-repeat="item in $$value$$ track by $index ">
99
<button ng-hide="form.readonly || form.remove === null"
1010
ng-click="deleteFromArray($index)"
11-
ng-disabled="form.schema.minItems >= $index"
11+
ng-disabled="form.schema.minItems >= modelArray.length"
1212
style="position: relative; z-index: 20;"
1313
type="button" class="close pull-right">
1414
<span aria-hidden="true">&times;</span><span class="sr-only">Close</span>
1515
</button>
1616
</li>
1717
</ol>
18-
<div class="clearfix" style="padding: 15px;">
18+
<div class="clearfix" style="padding: 15px;" ng-model="modelArray" schema-validate="form">
19+
<div class="help-block"
20+
ng-show="(hasError() && errorMessage(schemaError())) || form.description"
21+
ng-bind-html="(hasError() && errorMessage(schemaError())) || form.description"></div>
22+
1923
<button ng-hide="form.readonly || form.add === null"
2024
ng-click="appendToArray()"
21-
ng-disabled="form.schema.maxItems <= getModelArray().length"
25+
ng-disabled="form.schema.maxItems <= modelArray.length"
2226
type="button"
2327
class="btn {{ form.style.add || 'btn-default' }} pull-right">
2428
<i class="glyphicon glyphicon-plus"></i>
2529
{{ form.add || 'Add'}}
2630
</button>
2731
</div>
28-
<div class="help-block"
29-
ng-show="(hasError() && errorMessage(schemaError())) || form.description"
30-
ng-bind-html="(hasError() && errorMessage(schemaError())) || form.description"></div>
3132
</div>

‎src/bootstrap-decorator.js

Lines changed: 65 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ function(decoratorsProvider, sfBuilderProvider, sfPathProvider) {
66
var simpleTransclusion = sfBuilderProvider.builders.simpleTransclusion;
77
var ngModelOptions = sfBuilderProvider.builders.ngModelOptions;
88
var ngModel = sfBuilderProvider.builders.ngModel;
9+
var sfField = sfBuilderProvider.builders.sfField;
910

1011
var array = function(args) {
1112
console.log('array', args);
@@ -24,7 +25,7 @@ function(decoratorsProvider, sfBuilderProvider, sfPathProvider) {
2425
args.form.schema.items.type.indexOf('array') === -1) {
2526
console.log('setting state modelValue', args.form);
2627
var strKey = sfPathProvider.stringify(args.form.key).replace(/"/g, '&quot;') + '[$index]';
27-
state.modelValue = 'getModelArray()[$index]'; //(args.state.modelName || 'model') + (strKey[0] !== '[' ? '.' : '') + strKey;
28+
state.modelValue = 'modelArray[$index]'; //(args.state.modelName || 'model') + (strKey[0] !== '[' ? '.' : '') + strKey;
2829
//state.modelValue = 'model' + sfPathProvider.normalize(args.form.key) + '[$index]'; // 'modelArray[$index]';
2930
} else {
3031
state.modelName = 'item';
@@ -34,15 +35,15 @@ function(decoratorsProvider, sfBuilderProvider, sfPathProvider) {
3435
items.appendChild(childFrag);
3536
}
3637
};
37-
var defaults = [ngModel, ngModelOptions];
38+
var defaults = [sfField, ngModel, ngModelOptions];
3839
decoratorsProvider.defineDecorator('bootstrapDecorator', {
3940
textarea: {template: base + 'textarea.html', builder: defaults},
40-
fieldset: {template: base + 'fieldset.html', builder: simpleTransclusion},
41-
array: {template: base + 'array.html', builder: [ngModelOptions, ngModel, array]},
41+
fieldset: {template: base + 'fieldset.html', builder: [sfField, simpleTransclusion]},
42+
array: {template: base + 'array.html', builder: [sfField, ngModelOptions, ngModel, array]},
4243
tabarray: {template: base + 'tabarray.html', replace: false},
4344
tabs: {template: base + 'tabs.html', replace: false},
44-
section: {template: base + 'section.html', builder: simpleTransclusion},
45-
conditional: {template: base + 'section.html', builder: simpleTransclusion},
45+
section: {template: base + 'section.html', builder: [sfField, simpleTransclusion]},
46+
conditional: {template: base + 'section.html', builder: [sfField, simpleTransclusion]},
4647
actions: {template: base + 'actions.html', builder: defaults},
4748
select: {template: base + 'select.html', builder: defaults},
4849
checkbox: {template: base + 'checkbox.html', builder: defaults},
@@ -58,27 +59,10 @@ function(decoratorsProvider, sfBuilderProvider, sfPathProvider) {
5859
'default': {template: base + 'default.html', builder: defaults}
5960
}, []);
6061

61-
}]).filter('minLength', function() {
62-
return function(input, min) {
63-
input = input || [];
64-
var diff = min - input.length;
65-
if (diff > 0) {
66-
return input.concat(new Array(diff));
67-
}
68-
return input;
69-
};
70-
}).directive('sfPropagateNgModel', [function() {
71-
return {
72-
scope: false,
73-
require: 'ngModel',
74-
link: function(scope, element, attrs, ngModel) {
75-
// We need the ngModelController on several places,
76-
// most notably for errors.
77-
// So we emit it up to the decorator directive so it can put it on scope.
78-
scope.$emit('schemaFormPropagateNgModelController', ngModel);
79-
}
80-
};
81-
}]).directive('sfNewArray', ['sfSelect', 'sfPath', function(sel, sfPath) {
62+
}])
63+
64+
/* Directives here are WIP, will be moved to main repo or their own files when soldifying */
65+
.directive('sfNewArray', ['sfSelect', 'sfPath', function(sel, sfPath) {
8266
return {
8367
scope: false,
8468
link: function(scope, element, attrs) {
@@ -88,69 +72,99 @@ function(decoratorsProvider, sfBuilderProvider, sfPathProvider) {
8872
TODO
8973
----
9074
* se till att valideringsfel på arrayen syns tyligare, kanske över add knappen?
75+
* kanske css för disable knappen på listor också.
9176
* testa att ändra arrayen utanför, testa att byta ut arrayen helt utanför.
9277
* om den inte validerar när ngt förändras får vi lägga till en watch
9378
* testa en async validator och en vanlig $validator
9479
* implementera onChange med en watchCollection
95-
* implementera
9680
* disabla add och ta bort knapparna om limits har nåtts.
9781
9882
*/
9983

84+
scope.modelArray = scope.$eval(attrs.sfNewArray);
85+
86+
// We need to have a ngModel to hook into validation. It doesn't really play well with
87+
// arrays though so we both need to trigger validation and onChange.
88+
// So we watch the value as well. But watching an array can be tricky. We wan't to know
89+
// when it changes so we can validate,
90+
var watchFn = function() {
91+
//scope.modelArray = modelArray;
92+
scope.modelArray = scope.$eval(attrs.sfNewArray);
93+
console.warn('array watch!')
94+
// validateField method is exported by schema-validate
95+
if (scope.validateField) {
96+
console.warn('calling validate field');
97+
scope.validateField();
98+
}
99+
};
100+
101+
var onChangeFn = function() {
102+
if (scope.form && scope.form.onChange) {
103+
if (angular.isFunction(form.onChange)) {
104+
form.onChange(ctrl.$modelValue, form);
105+
} else {
106+
scope.evalExpr(form.onChange, {'modelValue': ctrl.$modelValue, form: form});
107+
}
108+
}
109+
};
110+
111+
// We need the form definition to make a decision on how we should listen.
112+
var once = scope.$watch('form', function(form) {
113+
if (!form) {
114+
return;
115+
}
116+
// If we have "uniqueItems" set to true, we must deep watch for changes.
117+
if (scope.form && scope.form.schema && scope.form.schema.uniqueItems === true) {
118+
scope.$watch(attrs.sfNewArray, watchFn, true);
119+
120+
// We still need to trigger onChange though.
121+
scope.$watch([attrs.sfNewArray, attrs.sfNewArray + '.length'], onChangeFn);
122+
123+
} else {
124+
// Otherwise we like to check if the instance of the array has changed, or if something
125+
// has been added/removed.
126+
scope.$watch([attrs.sfNewArray, attrs.sfNewArray + '.length'], function() {
127+
watchFn();
128+
onChangeFn();
129+
});
130+
}
131+
132+
once();
133+
});
100134

101135
scope.appendToArray = function() {
102136
var empty;
103137

104138
// Same old add empty things to the array hack :(
105139
if (scope.form && scope.form.schema) {
106-
107140
if (scope.form.schema.items) {
108141
if (scope.form.schema.items.type === 'object') {
109142
empty = {};
110143
} else if (scope.form.schema.items.type === 'array') {
111144
empty = [];
112145
}
113146
}
114-
115-
// FIXME: valdiate maxItems and minItems and make sure button is disables if needed.
116147
}
117148

118-
var model = scope.getModelArray();
149+
var model = scope.modelArray;
119150
if (!model) {
120151
// Create and set an array if needed.
121152
var selection = sfPath.parse(attrs.sfNewArray);
122153
model = [];
123154
sel(selection, scope, model);
124155
if (scope.ngModel) {
125-
ngModel.$setViewValue(model);
156+
scope.ngModel.$setViewValue(model);
126157
}
127158
}
128159
model.push(empty);
129-
130-
// validateField method is exported by schema-validate
131-
if (scope.validateField) {
132-
scope.validateField();
133-
}
134160
};
135161

136162
scope.deleteFromArray = function(index) {
137-
var model = scope.getModelArray();
163+
var model = scope.modelArray;
138164
if (model) {
139165
model.splice(index, 1);
140166
}
141-
142-
// validateField method is exported by schema-validate
143-
if (scope.validateField) {
144-
scope.validateField();
145-
}
146-
};
147-
148-
scope.getModelArray = function() {
149-
// Some simple perf testing sets $eval speeds at roughly the same as using sfSelect.
150-
var model = scope.$eval(attrs.sfNewArray);
151-
return model;
152167
};
153-
154168
}
155169
};
156170
}]);

0 commit comments

Comments
 (0)
Please sign in to comment.