/* https://github.com/nikos/cmskern/blob/master/playapp/public/javascripts/widgets.js*/ /** * Widget for displaying a complete form as specified by the given schema. */ angular.widget('my:form', function(element) { this.descend(true); // compiler will process children elements this.directives(true); // compiler will process directives return function(element) { function processField(field, fieldKey) { var qualifiedName = this.parentName + '.' + fieldKey, fullyQualifiedName = this.fqName + '.' + fieldKey, fieldElStr; console.log("----> field: " + fullyQualifiedName + ", relative: " + qualifiedName); // has hierarchical subforms? Must be declared in a type struct (single) or map (multi-typed) if (field.type == 'array' && field.items && field.ui_class != 'compact') { var childElem = fieldKey + 'Elem'; var multiTyped = true; // if items is a singular value set it to an array to make the rest work // expect either object with properties or type with map of different sub-types if (field.items.type == 'object') { multiTyped = false; field.items.type = [ field.items ]; } // ~~~~ FIXME start (init array top-level) var contentChilds = scope.$eval(qualifiedName); if (!contentChilds) { var propName = fullyQualifiedName.substr('contentNode'.length + 1); console.log("No content childs yet for " + propName); var propNameArr = propName.split('.'); if (propNameArr.length == 1) { console.log("... init array: " + propName); globalContentNode[propNameArr[0]] = [{"_type":field.items.type[0].id}]; // TODO: really use first type as default? } else { console.log("============= WARN cannot initialize arrays: " + propNameArr); } } // ~~~~ FIXME end (init array top-level) // (A) subform header (with move up/down button) var subform = angular.element(''); var repeater = angular.element('
  • '); var subfieldset = angular.element('
    '); var legendChild = angular.element('' + field.title + '{{$index}}'); // ~~ remove (per individual child group) var removeButton = angular.element(''); /* var removeButton = angular.element(''); */ legendChild.append(removeButton); subfieldset.append(legendChild); // (B) render individual fields of subform jQuery.each(field.items.type, function (subIdx, subfield) { if (typeof subfield.id == 'undefined') { subfield.id = subfield.title; } console.log("Add sub element for type: " + subfield.id); var elGroup = angular.element('
    ' + subfield.title + '
    '); var arraySuffix = ""; console.log("scope idx: " + scope.$index); var arr_level = scope.$get('arr_level'); if (arr_level != undefined) { arraySuffix = '.' + arr_level; console.log("array suffix: " + arraySuffix); } angular.forEach(subfield.properties, processField, {parentName: childElem, fqName: fullyQualifiedName + arraySuffix, curDOMParent: elGroup, childtype: subfield.id}); subfieldset.append(elGroup); }); repeater.append(subfieldset); subform.append(repeater); this.curDOMParent.append(subform); // (C) bottom: place add button for all available sibling types TODO: transform into drop-downlist var localScope = this; var subfieldTypes = []; jQuery.each(field.items.type, function (subIdx, subfield) { subfieldTypes.push(subfield.id); }); jQuery.each(field.items.type, function (subIdx, subfield) { // ~~ add sub-entity button (available no matter how many already exist) var addButton = angular.element('
    ' + '' + subfield.title + '
    '); localScope.curDOMParent.append(addButton); }); return; } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Start Render Field Type if (field.ui_class != 'hidden') { var controlGroup = angular.element('
    '); // ~~ Label for input element controlGroup.append(angular.element('')); var controlElem = angular.element('
    '); var typeLength = "medium"; if (field.ui_width) { typeLength = field.ui_width; } var lengthCssClassName = 'input-' + typeLength; if (field.type == 'array' && field.ui_class == 'compact') { fieldElStr = ''; fieldElStr += '
    {{i}}
    '; fieldElStr += ' '; fieldElStr += ''; // das ist provisorisch reingenommen, damit ich produktive CTs entwickeln kann und // selectable in sortable nicht funktioniert. for (var idx in field.enum) { fieldElStr += '' + field.enum[idx]; } } else if (field.ui_callout) { fieldElStr = '
    '; fieldElStr += ' '; if (field.ui_callout.target_properties) { var targetProperties = ""; for (i in field.ui_callout.target_properties) { targetProperties += field.ui_callout.target_properties[i] + "#"; } console.log("fields to update: " + targetProperties); var srcPropNames = field.ui_callout.src_properties.join('#'); fieldElStr += ' '; fieldElStr += '
    '; } else if (field.format == 'date') { fieldElStr = '
    '; fieldElStr += '
    '); //console.log("**** Include sub-object structure for " + fieldKey); angular.forEach(field.properties, processField, {parentName: fullyQualifiedName, fqName: fullyQualifiedName, curDOMParent: fieldElStr}); } else if (field.type == 'boolean') { fieldElStr = ''; */ controlElem.append(fieldElStr); // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ End Render Field Type // ~~ optionally add help hint if (field.description) { controlElem.append('

    ' + field.description + '

    '); } controlGroup.append(controlElem); this.curDOMParent.append(controlGroup); } // skip hidden field } var scope = this, schema = scope.$eval(element.attr('schema')), data = element.attr('data'), fieldset = angular.element('
    '); // process every field as specified in the JSON schema definition // context object: {parentName, fqName, curDOMParent, childtype} angular.forEach(schema, processField, {parentName: data, fqName: data, curDOMParent: fieldset}); angular.compile(fieldset)(scope); element.append(fieldset); }; });