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

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!

Related

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 custom synchronous validation not working at the very onset

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?

minItems doesn't seem to validate correctly in JSON schema

I'm writing a simple JSON schema and using minItems to validate the number of items in a given array. My schema is as follows:
{
"title": "My Schema",
"type": "object",
"properties": {
"root": {
"type": "array",
"properties": {
"id": {
"type": "string"
},
"myarray": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 4,
"uniqueItems": true
},
"boolean": {
"type": "boolean"
}
},
"required": ["id","myarray","boolean"]
}
},
"required": [
"root"
],
"additionalProperties": false
}
Now I would expect the following JSON to fail validation given the element myarray has nothing in it. But when using this online validator, it passes. Have I done something wrong or is the schema validator I'm using faulty?
{
"root":[
{
"id":"1234567890",
"myarray":[],
"boolean":true
}
]
}
I am not sure why or what it is called, but the correct schema definition for your requirement should be as shown further down.
From what I understand from the JSON Schema definitions, you should declare the properties of an array inside the items declaration. In your schema you where defining properties outside of the array item declaration.
In your schema you have the two different types of array declaration:
Once with just a single object (a string for the "myarray" object)
Once with a complex object (the object name "myComplexType" in the code below)
Have a look at the definitions of both, how they are structured and how they would be interpreted.
The corrected schema:
{
"title": "My Schema",
"type": "object",
"properties": {
"root": {
"type": "array",
"items": { <-- Difference here - "items" instead of "properties"
"type": "object", <-- here - define the array items as a complex object
"title": "myComplexType", <-- here - named for easier referencing
"properties": { <-- and here - now we can define the actual properties of the object
"id": {
"type": "string"
},
"myarray": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 4,
"uniqueItems": true
},
"boolean": {
"type": "boolean"
}
}
},
"required": [
"id",
"myarray",
"boolean"
]
}
},
"required": [
"root"
],
"additionalProperties": false
}
Remove the comments I added with <-- when copying over to your code, added for pointing where there changes are.
As a note, I do however don't understand why the validator didn't give an error for the 'malformed' schema, but might just be that it saw the definition as you had it as additional properties, not entirely sure.
The only thing wrong with your schema is that the root property should have type object instead of array. Because the properties keyword is not defined for arrays, it is ignored. Therefore, the part of the schema you were trying to test was completely ignored even though it was correct.
Here is the relevant passage from the specification
Some validation keywords only apply to one or more primitive types. When the primitive type of the instance cannot be validated by a given keyword, validation for this keyword and instance SHOULD succeed.
http://json-schema.org/latest/json-schema-validation.html#rfc.section.4.1

AngularJs - Filtering out duplicates in select control

I have a select list of axleTypes, each having a Type property of either a Front Axle or a Rear Axle. I can not seem to filter out duplicate words of 'Front' and 'Rear'.
Update:
Html:
<select ng-model="axleType.Type" ng-options="type for type in uniqueTypes">
Controller:
$scope.axleTypes = API.GetAxleTypes();
$scope.fixedAxleTypes = [
{ "$id": "1", "Description": "I beam, telescopic type shock absorbers", "Type": "Front", "Id": 1 },
{ "$id": "2", "Description": "Full-floating banjo housing", "Type": "Rear", "Id": 2 },
{ "$id": "3", "Description": "Something Else", "Type": "Rear", "Id": 2 },
{ "$id": "4", "Description": "I beam, telescopic type shock absorbers", "Type": "Front", "Id": 4 }
];
// This Works
$scope.uniqueTypes = _.uniq(_.pluck($scope.fixedAxleTypes, 'Type'));
// This does not
//$scope.uniqueTypes = _.uniq(_.pluck($scope.axleTypes, 'Type'));
// This does not
//$scope.uniqueTypes = _.uniq(_.pluck(API.GetAxleTypes(), 'Type'));
I am thoroughly confused. And yes, the API works, I copy pasted above data from
Chrome > Network>Response window
Seeing the error that you added in your edit, I'm sure that the reason is what I described in my comment, which is the use of the (axleType.Type) expression for the filter in a context where that expression cannot be evaluated. Since you're not using the expression in the filter implementation, you could just omit it altogether, I believe.
What i guess is API.GetAxleTypes(); should be doing some asynch tasks like calling $http.
if that is the case then $scope.axleTypes wont be of the array type you are looking for.
The the GetAxleTypes can look something similar to this.
Service definition:
{
uniqueaxleTypes:[],
GetAxleTypes = function($http,..){
var promise = $http({
//config
})
promise.then(function(response){
this.uniqueaxleTypes = _.uniq(_.pluck(response.data, 'Type'));
})
}
}
Then if you bind a scope variable to this uniqueaxleTypes it will always reflect the unique values.
Since $scope.fixedAxleTypes is a hard coded value, its working fine.

Resources