Open apaokin opened 6 years ago
You could define a custom validator that sends the data to the server: See http://www.alpacajs.org/docs/api/validation.html
Something like this...
"options": {
"validator": function(callback) {
const value = this.getValue();
const request = new XmlHttpRequest();
request.load = function() {
if (request.status >= 200 && request.status <= 299) {
callback({ "status": true });
} else {
callback({
"status": false,
"message": request.responseText
});
}
}
request.open(...);
request.send(JSON.stringify(value));
}
}
Thank you for answer. I can modify my question: "How to validate on button click only"? I found an example here, but validation on button click and validation on field changing are used. Also I want to submit and get errors using only one request
Okay, did you mean this issue: https://github.com/gitana/alpaca/issues/563? The problem is that this trick only works for primitive data types. Therefore, these properties must be set recursively. If you're worried now, worry about architecture. In my opinion, you should always encapsulate Alpaca and build your own classes around it, so that transformations can be defined in general and are not made for every application. Although this intervention is separately ugly. But the recursive code should also be done quickly (not tested yet):
function setValidate(control, validate) {
control.options.validate = validate;
Object.keys(control.childrenByPropertyId).forEach(k => {
setValidate(control.childrenByPropertyId[k], validate);
});
}
This function could then be used as follows (as in the above linked issue):
"options": {
"validator": function(callback) {
const value = this.getValue();
const request = new XmlHttpRequest();
request.load = function() {
if (request.status >= 200 && request.status <= 299) {
callback({ "status": true });
} else {
callback({
"status": false,
"message": request.responseText
});
}
}
request.open(...);
request.send(JSON.stringify(value));
},
"form": {
"buttons": {
"submit": {
"click": function() {
setValidate(this.topControl, true);
this.validate(true);
this.refreshValidationState(true);
if (this.isValid(true)) {
window.alert(JSON.stringify(this.getValue()));
}
}
}
}
}
},
"postRender": function() {
setValidate(this.topControl, false);
this.validate(true);
this.refreshValidationState(true);
}
}
I hope this improvised code works halfway :)
Update:
This seems to work, but arrays are very buggy (commented out):
function setValidateOnConfig(config, validate) {
if (config.schema === undefined) config.schema = {};
if (config.options === undefined) config.options = {};
setValidateOnConfigImpl(config.schema, config.options, validate);
}
function setValidateOnConfigImpl(schema, options, validate) {
options.validate = false;
if (schema.properties !== undefined) {
if (options.fields === undefined) options.fields = {};
Object.keys(schema.properties).forEach(k => {
setValidateOnConfigImpl(schema.properties[k], options.fields[k]);
});
} /* else if (schema.items !== undefined) {
if (options.items === undefined) options.items = {};
setValidateOnConfigImpl(schema.items, options.items, validate);
} */
}
function setValidateOnControl(control, validate) {
control.options.validate = validate;
if (control.childrenByPropertyId !== undefined && Object.keys(control.childrenByPropertyId).length > 0) {
Object.keys(control.childrenByPropertyId).forEach(k => {
setValidateOnControl(control.childrenByPropertyId[k], validate);
});
} /* else if (control.children !== undefined && control.children.length > 0) {
control.children.forEach(c => {
setValidateOnControl(c, validate);
});
} */
}
const config = {
"schema": {
"type": "object",
"properties": {
"number": {
"type": "number"
}
}
},
"options": {
"fields": {
"number": {
"label": "Number"
}
},
"validator": function(callback) {
if (this.options.validate) {
callback({
"status": false,
"message": "... Server Response ..."
});
} else {
callback({ "status": this.validate(true) });
}
},
"form": {
"buttons": {
"submit": {
"click": function() {
setValidateOnControl(this.topControl, true);
this.validate(true);
this.refreshValidationState(true);
window.setTimeout(() => {
if (this.isValid(true)) {
window.alert(JSON.stringify(this.getValue()));
}
}, 0);
}
}
}
}
}
}
setValidateOnConfig(config, false);
$("#field1").alpaca(config);
Note:
A validator can also be defined for each individual field and does not have to be defined for all fields.
Example:
options.items.fields.myField.validate = false
options.items.fields.myField.validator = function (callback) { ... }
So you can avoid the recursion and send only the single field to the server. Then you have to overwrite the validate attribute in (e.g.) the button click event handler for one field only.
If you only have the possibility to send a post-request and only if it fails to handle an error, this is of course not so good. Then the validator must send a (data-changing) post-request, which it should not do under any circumstances. That cannot be solved. With this longing, you can only use damage limitation and make sure that the validator is only called when the button has been clicked, which now works at least for the first time. The validator is then called up for each change. So you would have to set validate back to false, which is a bit paradoxical.
The desired functionality is to be welcomed. I hope there is an even more elegant solution.
Thank you for answer. I decided to show errors after server-side validation in the special div.
Great answer from @WhileTrueEndWhile ! In addition, if you also want to perform custom validation on conditional dependencies (e.g number
that depends on condition
), you can use an observable such as subscribe
inside the postRender
block:
...
"postRender": function(control) {
var condition = control.childrenByPropertyId["condition"];
var number = control.childrenByPropertyId["number"];
condition.subscribe(number, function(val) {
// See answer @WhileTrueEndWhile
this.validate(true);
this.refreshValidationState(true);
});
}
Helpful links:
P.S. A big thanks to all contributors for this great project! :tada:
I have this code. I just want to add errors here: promise.fail(function(arg) { //ADDING SOME ERRORS }); 1)I wish to find out how to add errors to specific field 2) I may want to remove them in future 3) Is there built-in support for rendering all or specific errors in one div, for example?
Thanks.