Angular Schema Form custom synchronous validation not working at the very onset - angular-schema-form

I am using the below Schema and Form. Please check the JSFiddle below for how this behaves. I'd like for form to be Not Valid when I say yes to "Do I have to agree" but then did not check the "agree" and clicked the save button. Looks like I first have to select the agree and then un-select it for the validation message to appear.
http://jsfiddle.net/mutharasus/Ljp7bhgn/
$scope.schema = {
"type": "object",
"properties": {
"doIHaveToAgree": {"type": "boolean"},
"agree": { "type": "boolean" }
},
"required": ["doIHaveToAgree"]
};
$scope.form = [
{
"key":"doIHaveToAgree",
"title": "Do I have to agree",
"type":"radios-inline",
"titleMap":[{"value":true,"name":"Yes"},{"value":false,"name":"No"}]
},
{
"key": "agree",
"condition": "model.doIHaveToAgree",
"validationMessage": {
"agreeRequired": "You have to agree"
},
"$validators": {
"agreeRequired": function(value){
return value;
}
}
},
{
"type": "submit",
"title": "Save"
}
];
This appears to be as a result of https://github.com/Textalk/angular-schema-form/issues/388
I do not want to make "agree" required since that would only work for the UI. On the server side the schema validation would fail when "doIHaveToAgree" is false.
Is there any way for me to trigger the $validate() on the field manually since the $validate() is not being invoked as part of the schemaFormValidate event?

Related

schemaform adding the user input to a schema array

Background
I am making a form using angular-schema-form
Setup
I am trying to make an array of items that a user can make using a form. So, the user can add as many items into the array as they want.
For now the items in the array contain a command type.
Command Type should be a dropdown containing SSH, REST, and whatever the user enters in as the personalized command type.
Code so far
SCHEMA
{
"type": "object",
"properties": {
"personalizedCommandType": {
"title": "Personalized Command Type",
"type": "string"
},
"commands": {
"type": "array",
"title": "Actions",
"items": {
"type": "object",
"properties": {
"commandType": {
"title": "Command Type",
"type": "string",
"enum": [
"REST",
"SSH"
]
}
}
}
}
}
}
FORM
[
{
"type": "help",
"helpvalue": "<h5>Command</h5>"
},
{
"key":"personalizedCommandType"
},
{
"title":"Command",
"key": "commands",
"items": [
"commands[].commandType"
]
}
]
One can test this code here: http://schemaform.io/examples/bootstrap-example.html . Just copy and paste in my code.
Question
As one can see, the code I have now has a field with Personalized Command Type and an array of dropdowns with the 2 options SSH and REST. But I want to drop to also contain the value of the Personalized Command Type once the user has entered it.
NOTE
copyValueTo does not seem to have the functionality that I want given that it can only change values in the model, but I want it to change the enum array in the schema.
Use the onChange option:
[
{
"type": "help",
"helpvalue": "<h5>Command</h5>"
},
{
"key":"personalizedCommandType"
onChange: "updateSchema(modelValue,form)"
},
{
"title":"Command",
"key": "commands",
"items": [
"commands[].commandType"
]
}
]
Update the Schema:
var defaultEnum = ["REST","SSH"];
$scope.updateSchema = function(modelValue,form) {
var currentEnum = $scope.schema.commands.items.properties.commandType.enum;
angular.copy(defaultEnum, currentEnum);
if (modelValue) {
currentEnum.push(modelValue);
};
$scope.$broadcast('schemaFormRedraw');
};

Angular Schema Form - "required" not working select and checkbox fields

I'm new to Angular Schema Form and having some issues with select and checkbox fields required validation.
Under $scope.schema I have the select field named designation:
"designation": {
"title": "Designation",
"type": "select",
"default": undefined
}
$scope.form (declaring designation and agreeTerms):
{
"key": "designation",
"type": "select",
"title": "Designation",
"titleMap": [
{ value: undefined, name: "Select a designation" }, // first value
{ value: "Andersson", name: "Andersson" },
{ value: "Johansson", name: "Johansson" },
{ value: "other", name: "Something else..."}
]
},
{
"key": "agreeTerms",
"type": "checkbox",
"title": "I agree to ..."
}
Both designation and agreeTerms are defined in the schema's required property.
When I submit the form, both fields however pass the required validation. What I'm expecting the UI to show are the Required messages underneath/after the fields. That is not happening.
Things I've tried:
assign first value of the select field to '' and null and match that with the schema default value.
change the select field type to object in the schema; this worked and passed the required validation but the property didn't show up in the model
Please help :)
You need to change the schema type to string as select is not a valid schema type property.
"designation": {
"title": "Designation",
"type": "string",
"default": undefined
}
Then in your form tag you should have ng-submit="submitForm(ngform,modelData)":
<form sf-schema="schema" sf-form="form" sf-model="model" ng-submit="submitForm(ngform,modelData)" sf-options="option"></form>
Then within your controller you can broadcast on submit to validate:
$scope.submitForm = function(form) {
// First we broadcast an event so all fields validate themselves
$scope.$broadcast('schemaFormValidate');
};

JSON schema deeper object uniqueness

I'm trying to get into JSON schema definitions and wanted to find out, how to achieve a deeper object uniqueness in the schema definition. Please look at the following example definition, in this case a simple IO of a module.
{
"$schema": "http://json-schema.org/draft-06/schema#",
"type": "object",
"required": ["modulIOs"],
"properties": {
"modulIOs": {
"type": "array",
"uniqueItems": true,
"items": {
"allOf": [
{
"type": "object",
"required": ["ioPosition","ioType","ioFunction"],
"additionalProperties": false,
"properties": {
"ioPosition": {
"type": "integer"
},
"ioType": {
"type":"string",
"enum": ["in","out"]
},
"ioFunction": {
"type":"string"
}
}
}
]
}
}
}
}
When I validate the following with i.E. draft-06 I get a positive validation.
{"modulIOs":
[
{
"ioPosition":1,
"ioType":"in",
"ioFunction":"240 V AC in"
},
{
"ioPosition":1,
"ioType":"in",
"ioFunction":"24 V DC in"
}
]
}
I'm aware that the validation is successfull because the validator does what he's intended to - it checks the structure of a JSON-object, but is there a possibility to validate object value data in deeper objects or do i need to perform the check elsewhere?
This is not currently possible with JSON Schema (at draft-7).
There is an issue raised on the official spec repo github for this: https://github.com/json-schema-org/json-schema-spec/issues/538
If you (or anyone reading this) really wants this, please thumbsup the first issue comment.
It's currently unlikely to make it into the next draft, and even if it did, time to impleemntations picking it up may be slow.
You'll need to do this validation after your JSON Schema validation process.
You can validate data value of your object fields by using JSON schema validation.
For example, if you need to check if ioPosition is between 0 and 100 you can use:
"ioPosition": {
"type": "integer",
"minimum": 0,
"maximum": 100
}
If you need to validate ioFunction field you can use regualr expression such as:
"ioFunction": {
"type": "string",
"pattern": "^[0-9]+ V [A,D]C"
}
Take a look at json-schema-validation.

angularjs: extending directive then make modifications

I am working in ServiceNow and am trying to extend an ootb directive, then make some modifications to a couple functions. So far I've figured out how to extend the directive:
function (spModelDirective){
return angular.extend({}, spModelDirective[0], {
templateUrl:'lbl_custom_template.xml'
});
}
Within this directive, there's a function called getNestedFields that I would like to make edits to:
function getNestedFields(fields, containers) {
if (!containers)
return;
for (var _container in containers) {
var container = containers[_container];
if (container.columns) {
for (var _col in container.columns) {
var col = container.columns[_col];
for (var _field in col.fields) {
var field = col.fields[_field];
if (field.type == "container" && container.caption != "")
getNestedFields(fields, [field]);
else if (field.type == "checkbox_container")
getNestedFields(fields, field.containers);
else if (field.type == "field" || container.caption=="")
fields.push(formModel._fields[field.name]);
}
}
}
}
}
Can someone provide some guidance on what the correct syntax for this would be?
More information
Our team cloned the ootb widget-form and am trying to create a custom layout. Basically, we want each form section to be it's own tab much like the back-end form instead of one long form, which is what the ootb widget-form currently does. In the very first line of the sp-variable-layout template, it shows:
<fieldset ng-init="$last ? execItemScripts() : null" ng-show="isContainerVisible(container)" ng-repeat="container in containers">
The ng-repeat of container in containers considers each form section as a separate container (which is perfect), BUT it also considers any splits as a separate container as well. So for example, if my form's layout looks like this:
This will create two tabs: one that has every field within the begin and end splits AND a separate tab with everything after the end split. The JSON object that is created looks like this:
{
"_bootstrap_cells": 6,
"_count": 2,
"visible": true,
"columns": [{
"fields": [{
"name": "type_of_account",
"type": "field"
}, {
"name": "routing_transit_number",
"type": "field"
}]
}, {
"fields": [{
"name": "type_of_payment",
"type": "field"
}, {
"name": "check_digit",
"type": "field"
}]
}],
"caption": "Direct Deposit",
"id": "b456b9d2137ac340177c36328144b0ef",
"scope_name": "x_dnf_table"
}, {
"_bootstrap_cells": 12,
"_count": 1,
"visible": true,
"columns": [{
"fields": [{
"name": "account_number",
"type": "field"
}, {
"name": "account_title",
"type": "field"
}, {
"name": "financial_institution_name",
"type": "field"
}]
}],
"caption": "",
"id": "",
"scope_name": "x_dnf_table"
}
Notice the first "section" has a caption, but ServiceNow treats the split section as its own separate section with no caption at all.
I want to change the spModel directive to produce only containers with captions as their own tab and if a container does NOT have a caption, to append it to the previous container that does have a caption.
I don't think you can edit this function as this is hosted as a file on Servicenow. See https://hi.service-now.com/scripts/app.$sp/directive.spModel.js then just control-f for the getNestedFields.
Per this thread; https://community.servicenow.com/thread/247907#1059129 I believe spModal is just a wrapper for $uibModal.
What you can do is make your own directive on sp_angular_provider.

Angular schema form destroyStrategy works only on siblings that are array of objects. It does not work on other siblings

I have not been successful in getting the destroy strategy to work on any sibling properties or sibling objects. It only works on sibling array of objects. Please check this example:
$scope.schema = {
"type": "object",
"properties": {
"propertyOne": {
"type": "string",
"enum": ["option1", "option2"],
"title": "Property One Select"
},
"propertyTwo": {
"type": "string",
"enum": ["option3", "option4"],
"title": "Property Two Select"
},
"objectOne": {
"type": "object",
"properties": {
"objectOnePropertyThree": {
"type": "string",
"enum": ["option5","option6"],
"title": "Property Three Select"
}
}
},
"arrayOfObjects": {
"type": "array",
"items": {
"type": "object",
"properties": {
"arrayObjectPropertyFour": {
"type": "string",
"enum": ["option7","option8"],
"title": "Property Four Select"
}
}
}
}
},
"required": ["propertyOne"]
};
$scope.form = [{
"key": "propertyOne"
}, {
"key": "propertyTwo",
"condition": "model.propertyOne === \"option1\""
},{
"key": "objectOne.objectOnePropertyThree",
"condition": "model.propertyOne === \"option1\""
},{
"key": "arrayOfObjects",
"condition": "model.propertyOne === \"option1\""
},
{
"type": "submit",
"title": "Save"
}];
http://jsfiddle.net/mutharasus/dp18a70b/
In here if you select the first dropdown to "Option1" then select all the other dropdowns and save. Then go back and switch the first dropdown to "Option2" and save you can see that only the very last array of objects is removed with the destroy strategy.
Am I doing something wrong or is this a bug in angular-schema-form? I looked under the issues that are currently open in the github project and I do not see an open issue about this.
You're correct, it's not currently behaving the way that you expect when an individual field is removed from the view because of a condition.
Here's what's happening: under the "old" bundled decorators for ASF, each field-type decorator is rendered on the page within an outer tag. The contents of the appropriate field template are then processed and rendered. Condition logic applies to everything within the tag, but not to the tag itself. Normally, this would be fine, but the destroyStrategy logic was assigned to the $destroy event of the tag. The end result is that the $destroy event will never fire unless the entire tag would be removed from the DOM. This is why the model values in the array of objects are being cleaned up - the container is removed when the "model.propertyOne === 'option1'" condition fails, which cascades the $destroy event to each object in the array.
I think that this got overlooked with the creation and release of the new builder, because I raised the issue at the end of the PR for the feature (https://github.com/Textalk/angular-schema-form/pull/371).
On the bright side, the new builder approach (which you can use by adding the bootstrap-decorator file from https://github.com/Textalk/angular-schema-form-bootstrap) doesn't have this issue. Instead, the destroyStrategy logic is applied via directive to the form fields because the tag is no longer used. Unless you have a need to stay with the old decorators at this time, I suggest grabbing the new ones and giving them a try.
Let us know how it goes!

Resources