From 2a119a546411297b3f7376df2c5a84f00affe874 Mon Sep 17 00:00:00 2001 From: Bruno Alves de Faria Date: Tue, 30 Aug 2016 14:04:32 -0300 Subject: [PATCH] LT-67: Refactoring schema validation --- .../serverInformationDicomWeb.html | 12 +- .../serverInformationForm.js | 2 +- Packages/lesiontracker/client/utils/form.js | 105 ------------------ Packages/lesiontracker/package.js | 3 - .../client/components/base/mixins/form.js | 59 +--------- .../client/components/base/mixins/formItem.js | 17 ++- .../client/components/base/mixins/group.js | 56 ++++++++++ .../components/base/mixins/schemaData.js | 20 +++- Packages/ohif-core/client/lib/blaze.js | 2 +- Packages/ohif-core/client/lib/index.js | 1 + Packages/ohif-core/client/lib/object.js | 50 +++++++++ .../client/compatibility/dialogPolyfill.js | 5 +- 12 files changed, 156 insertions(+), 176 deletions(-) delete mode 100644 Packages/lesiontracker/client/utils/form.js create mode 100644 Packages/ohif-core/client/lib/object.js diff --git a/Packages/lesiontracker/client/components/serverInformation/serverInformationDicomWeb/serverInformationDicomWeb.html b/Packages/lesiontracker/client/components/serverInformation/serverInformationDicomWeb/serverInformationDicomWeb.html index 478aa1b0b..49db1b73c 100644 --- a/Packages/lesiontracker/client/components/serverInformation/serverInformationDicomWeb/serverInformationDicomWeb.html +++ b/Packages/lesiontracker/client/components/serverInformation/serverInformationDicomWeb/serverInformationDicomWeb.html @@ -29,25 +29,25 @@

Request options

-
+ {{#group class='row' key='requestOptions'}}
- {{>inputText labelClass='form-group' key='requestOptions.auth'}} + {{>inputText labelClass='form-group' key='auth'}}
Logging options
- {{>inputCheckbox key='requestOptions.logRequests'}} + {{>inputCheckbox key='logRequests'}}
- {{>inputCheckbox key='requestOptions.logResponses'}} + {{>inputCheckbox key='logResponses'}}
- {{>inputCheckbox key='requestOptions.logTiming'}} + {{>inputCheckbox key='logTiming'}}
-
+ {{/group}} diff --git a/Packages/lesiontracker/client/components/serverInformation/serverInformationForm/serverInformationForm.js b/Packages/lesiontracker/client/components/serverInformation/serverInformationForm/serverInformationForm.js index 699c2d847..41cfd26c6 100644 --- a/Packages/lesiontracker/client/components/serverInformation/serverInformationForm/serverInformationForm.js +++ b/Packages/lesiontracker/client/components/serverInformation/serverInformationForm/serverInformationForm.js @@ -53,7 +53,7 @@ Template.serverInformationForm.onRendered(() => { Template.serverInformationForm.events({ submit(event, instance) { event.preventDefault(); - var formData = FormUtils.getFormData(instance.data.$form); + var formData = instance.data.form.value(); Meteor.call('serverSave', formData, function(error) { if (error) { // TODO: check for errors: not-authorized, data-write diff --git a/Packages/lesiontracker/client/utils/form.js b/Packages/lesiontracker/client/utils/form.js deleted file mode 100644 index 80a790594..000000000 --- a/Packages/lesiontracker/client/utils/form.js +++ /dev/null @@ -1,105 +0,0 @@ -class Form { - - // Identify the element's type and get its value - static getElementValue($element) { - var type = $element.attr('type'); - var value; - switch (type) { - case 'checkbox': - value = $element.is(':checked'); - break; - default: - value = $element.val(); - } - return value; - }; - - // Identify the element's type and get its value - static setElementValue($element, value) { - if (!$element.length) { - return; - } - - var type = $element.attr('type'); - switch (type) { - case 'checkbox': - $element.prop('checked', !!value); - break; - default: - $element.val(value); - } - $element.trigger('change'); - }; - - // Transforms a shallow object with keys separated by "." into a nested object - static getNestedObject(shallowObject) { - var nestedObject = {}; - for (var key in shallowObject) { - var value = shallowObject[key]; - var propertyArray = key.split('.'); - var currentObject = nestedObject; - while (propertyArray.length) { - var currentProperty = propertyArray.shift(); - if (!propertyArray.length) { - currentObject[currentProperty] = value; - } else { - if (!currentObject[currentProperty]) { - currentObject[currentProperty] = {}; - } - - currentObject = currentObject[currentProperty]; - } - } - } - - return nestedObject; - }; - - // Transforms a nested object into a shallowObject merging its keys with "." character - static getShallowObject(nestedObject) { - var shallowObject = {}; - var putValues = function(baseKey, nestedObject, resultObject) { - for (var key in nestedObject) { - var currentKey = baseKey ? baseKey + '.' + key : key; - var currentValue = nestedObject[key]; - if (typeof currentValue === 'object') { - if (currentValue instanceof Array) { - currentKey += '[]'; - } - - putValues(currentKey, currentValue, resultObject); - } else { - resultObject[currentKey] = currentValue; - } - } - }; - - putValues('', nestedObject, shallowObject); - return shallowObject; - } - - // Gets the nested data for the given form - static getFormData($form) { - var data = {}; - $form.find(':input[name]').each((index, element) => { - var $element = $(element); - var value = this.getElementValue($element); - var name = $element.attr('name'); - data[name] = value; - }); - return this.getNestedObject(data); - } - - // Sets the nested data in the given form - static setFormData($form, data) { - var shallowData = this.getShallowObject(data); - for (var key in shallowData) { - var value = shallowData[key]; - var $element = $form.find(':input[name="' + key + '"]'); - this.setElementValue($element, value); - } - } - -} - -FormUtils = Form; diff --git a/Packages/lesiontracker/package.js b/Packages/lesiontracker/package.js index 5461f5700..8fc480663 100644 --- a/Packages/lesiontracker/package.js +++ b/Packages/lesiontracker/package.js @@ -58,9 +58,6 @@ Package.onUse(function(api) { api.addFiles('client/tools.js', 'client'); - // Utility classes - api.addFiles('client/utils/form.js', 'client'); - // UI Components api.addFiles('client/components/viewer/viewer.html', 'client'); api.addFiles('client/components/viewer/viewer.styl', 'client'); diff --git a/Packages/ohif-core/client/components/base/mixins/form.js b/Packages/ohif-core/client/components/base/mixins/form.js index cc2061faf..28d5eb64c 100644 --- a/Packages/ohif-core/client/components/base/mixins/form.js +++ b/Packages/ohif-core/client/components/base/mixins/form.js @@ -12,62 +12,13 @@ OHIF.mixins.form = new OHIF.Mixin({ const instance = Template.instance(); const component = instance.component; - // Run this computation every time the schema property is changed - instance.autorun(() => { - let schema; + // Set the form identifier flag + component.isForm = true; - // Check if the schema is reactive - if (instance.data.schema instanceof ReactiveVar) { - // Register a dependency on schema property - schema = instance.data.schema.get(); - } - - // Set the form's data schema - component.schema = schema && schema.newContext(); - }); - - // Get a registered item in form by its key - component.item = itemKey => { - let found; - - // Iterate over each registered form item - component.registeredItems.forEach(child => { - const key = child.templateInstance.data.key; - - // Change the found item if current key is the same as given - if (key === itemKey) { - found = child; - } - }); - - // Return the found item or undefined if it was not found - return found; - }; - - // Check if the form data is valid in its schema - component.validate = () => { - // Assume validation result as true - let result = true; - - // Return true if there's no data schema defined - if (!component.schema) { - return result; - } - - // Iterate over each registered form item and validate it - component.registeredItems.forEach(child => { - const key = child.templateInstance.data.key; - - // Change result to false if any form item is invalid - if (key && !child.validate()) { - result = false; - } - }); - - // Return the validation result - return result; - }; + // Reset the pathKey + instance.data.pathKey = ''; }, + onRendered() { const instance = Template.instance(); const component = instance.component; diff --git a/Packages/ohif-core/client/components/base/mixins/formItem.js b/Packages/ohif-core/client/components/base/mixins/formItem.js index fd1f4c76e..12f9fc308 100644 --- a/Packages/ohif-core/client/components/base/mixins/formItem.js +++ b/Packages/ohif-core/client/components/base/mixins/formItem.js @@ -73,16 +73,27 @@ OHIF.mixins.formItem = new OHIF.Mixin({ } }; + // Search for the parent form component + component.getForm = () => { + let currentComponent = component; + while (currentComponent) { + currentComponent = currentComponent.parent; + if (currentComponent && currentComponent.isForm) { + return currentComponent; + } + } + }; + // Check if the component value is valid in its form's schema component.validate = () => { // Get the component's form - const form = component.parent; + const form = component.getForm(); // Get the form's data schema const schema = form && form.schema; // Get the current component's key - const key = instance.data.key; + const key = instance.data.pathKey; // Return true if validation is not needed if (!key || !schema || !component.$wrapper.is(':visible')) { @@ -156,11 +167,13 @@ OHIF.mixins.formItem = new OHIF.Mixin({ // TODO: [design] remove log, show error box/hint over the wrapper errorin(event, instance) { + event.stopPropagation(); console.log('ERROR when validating component', instance.component); }, // TODO: [design] hide error box/hint errorout(event, instance) { + event.stopPropagation(); } } diff --git a/Packages/ohif-core/client/components/base/mixins/group.js b/Packages/ohif-core/client/components/base/mixins/group.js index 4e212cd2c..ffb1235cf 100644 --- a/Packages/ohif-core/client/components/base/mixins/group.js +++ b/Packages/ohif-core/client/components/base/mixins/group.js @@ -12,6 +12,20 @@ OHIF.mixins.group = new OHIF.Mixin({ const instance = Template.instance(); const component = instance.component; + // Run this computation every time the schema property is changed + instance.autorun(() => { + let schema = instance.data.schema; + + // Check if the schema is reactive + if (schema instanceof ReactiveVar) { + // Register a dependency on schema property + schema = schema.get(); + } + + // Set the form's data schema + component.schema = schema && schema.newContext(); + }); + // Get or set the child components values component.value = value => { const isGet = _.isUndefined(value); @@ -35,6 +49,48 @@ OHIF.mixins.group = new OHIF.Mixin({ component.$element.trigger('change'); }; + // Get a registered item in form by its key + component.item = itemKey => { + let found; + + // Iterate over each registered form item + component.registeredItems.forEach(child => { + const key = child.templateInstance.data.key; + + // Change the found item if current key is the same as given + if (key === itemKey) { + found = child; + } + }); + + // Return the found item or undefined if it was not found + return found; + }; + + // Check if the form data is valid in its schema + component.validate = () => { + // Assume validation result as true + let result = true; + + // Return true if there's no data schema defined + if (!component.schema) { + return result; + } + + // Iterate over each registered form item and validate it + component.registeredItems.forEach(child => { + const key = child.templateInstance.data.key; + + // Change result to false if any form item is invalid + if (key && !child.validate()) { + result = false; + } + }); + + // Return the validation result + return result; + }; + // Disable or enable the component component.disable = isDisable => { component.registeredItems.forEach(child => child.disable(isDisable)); diff --git a/Packages/ohif-core/client/components/base/mixins/schemaData.js b/Packages/ohif-core/client/components/base/mixins/schemaData.js index 6bddc2012..15f9de7dc 100644 --- a/Packages/ohif-core/client/components/base/mixins/schemaData.js +++ b/Packages/ohif-core/client/components/base/mixins/schemaData.js @@ -7,7 +7,13 @@ import { _ } from 'meteor/underscore'; // Helper function to get the component's current schema const getCurrentSchema = (parentComponent, key) => { // Get the parent component schema - const schema = parentComponent && parentComponent.schema; + let schema = parentComponent && parentComponent.schema; + + // Try to get the form schema if it was not found + if (parentComponent && !schema) { + const form = parentComponent.getForm(); + schema = form && form.schema; + } // Stop here if there's no key or schema defined if (!key || !schema) { @@ -45,8 +51,18 @@ OHIF.mixins.schemaData = new OHIF.Mixin({ // Get the parent component const parent = OHIF.blaze.getParentComponent(Blaze.currentView); + // Get he parent component key + let parentKey = parent && parent.templateInstance.data.pathKey; + + // Set the path key for this component + data.pathKey = data.key || ''; + if (data.pathKey && typeof parentKey === 'string') { + const prefix = parentKey ? `${parentKey}.` : ''; + data.pathKey = `${prefix}${data.pathKey}`; + } + // Get the current schema data using component's key - const currentSchema = getCurrentSchema(parent, data.key); + const currentSchema = getCurrentSchema(parent, data.pathKey); // Stop here if there's no schema data for current key if (!currentSchema) { diff --git a/Packages/ohif-core/client/lib/blaze.js b/Packages/ohif-core/client/lib/blaze.js index beeaf083f..a8a03763a 100644 --- a/Packages/ohif-core/client/lib/blaze.js +++ b/Packages/ohif-core/client/lib/blaze.js @@ -31,7 +31,7 @@ OHIF.blaze.getParentView = (view, parentViewName) => { }; // Search for the parent component of the given view -OHIF.blaze.getParentComponent = (view) => { +OHIF.blaze.getParentComponent = view => { let currentView = view; while (currentView) { currentView = currentView.originalParentView || currentView.parentView; diff --git a/Packages/ohif-core/client/lib/index.js b/Packages/ohif-core/client/lib/index.js index 1699c91cd..6588528d9 100644 --- a/Packages/ohif-core/client/lib/index.js +++ b/Packages/ohif-core/client/lib/index.js @@ -1,3 +1,4 @@ import './blaze.js'; +import './object.js'; import './string.js'; import './user.js'; diff --git a/Packages/ohif-core/client/lib/object.js b/Packages/ohif-core/client/lib/object.js new file mode 100644 index 000000000..4cef24679 --- /dev/null +++ b/Packages/ohif-core/client/lib/object.js @@ -0,0 +1,50 @@ +import { OHIF } from 'meteor/ohif:core'; + +OHIF.object = {}; + +// Transforms a shallow object with keys separated by "." into a nested object +OHIF.blaze.getNestedObject = shallowObject => { + var nestedObject = {}; + for (var key in shallowObject) { + var value = shallowObject[key]; + var propertyArray = key.split('.'); + var currentObject = nestedObject; + while (propertyArray.length) { + var currentProperty = propertyArray.shift(); + if (!propertyArray.length) { + currentObject[currentProperty] = value; + } else { + if (!currentObject[currentProperty]) { + currentObject[currentProperty] = {}; + } + + currentObject = currentObject[currentProperty]; + } + } + } + + return nestedObject; +}; + +// Transforms a nested object into a shallowObject merging its keys with "." character +OHIF.blaze.getShallowObject = nestedObject => { + var shallowObject = {}; + var putValues = function(baseKey, nestedObject, resultObject) { + for (var key in nestedObject) { + var currentKey = baseKey ? baseKey + '.' + key : key; + var currentValue = nestedObject[key]; + if (typeof currentValue === 'object') { + if (currentValue instanceof Array) { + currentKey += '[]'; + } + + putValues(currentKey, currentValue, resultObject); + } else { + resultObject[currentKey] = currentValue; + } + } + }; + + putValues('', nestedObject, shallowObject); + return shallowObject; +}; diff --git a/Packages/viewerbase/client/compatibility/dialogPolyfill.js b/Packages/viewerbase/client/compatibility/dialogPolyfill.js index 1ff46a6fc..77a4ceea6 100644 --- a/Packages/viewerbase/client/compatibility/dialogPolyfill.js +++ b/Packages/viewerbase/client/compatibility/dialogPolyfill.js @@ -230,7 +230,8 @@ var dialogPolyfill = (function() { // TODO: Only install when any dialogs are open. document.addEventListener('submit', function(ev) { - var method = ev.target.getAttribute('method').toLowerCase(); + var method = ev.target.getAttribute('method'); + method = method ? method.toLowerCase() : ''; if (method != 'dialog') { return; } ev.preventDefault(); @@ -408,4 +409,4 @@ var dialogPolyfill = (function() { dialogPolyfill.dm.handleKey.bind(dialogPolyfill.dm)); return dialogPolyfill; -})(); \ No newline at end of file +})();