/********************************************* * Copyright (c) 2013 AnsibleWorks, Inc. * * FormGenerator * Pass in a form definition from FormDefinitions and out pops an html template. * See js/form-definitions.js for form example. For now produces a Twitter Bootstrap * horizontal form. * */ angular.module('FormGenerator', ['GeneratorHelpers']) .factory('GenerateForm', [ '$compile', 'SearchWidget', 'PaginateWidget', function($compile, SearchWidget, PaginateWidget) { return { setForm: function(form) { this.form = form; }, attr: function(obj, key) { var result; switch(key) { case 'ngClick': result = "ng-click=\"" + obj[key] + "\" "; break; case 'ngChange': result = "ng-change=\"" + obj[key] + "\" "; break; case 'ngShow': result = "ng-show=\"" + obj[key] + "\" "; break; case 'trueValue': result = "ng-true-value=\"" + obj[key] + "\" "; break; case 'falseValue': result = "ng-false-value=\"" + obj[key] + "\" "; break; default: result = key + "=\"" + obj[key] + "\" "; } return result; }, icon: function(icon) { return " "; }, has: function(key) { return (this.form[key] && this.form[key] != null && this.form[key] != undefined) ? true : false; }, inject: function(form, options) { // // Use to inject the form as html into the view. View MUST have an ng-bind for 'htmlTemplate'. // Returns scope of form. // var element = angular.element(document.getElementById('htmlTemplate')); this.setForm(form); element.html(this.build(options)); // Inject the html this.scope = element.scope(); // Set scope specific to the element we're compiling, avoids circular reference // From here use 'scope' to manipulate the form, as the form is not in '$scope' $compile(element)(this.scope); if (options.related) { this.addListeners(); } if (options.mode == 'add') { this.applyDefaults(); } this.mode = options.mode; return this.scope; }, applyDefaults: function() { for (fld in this.form.fields) { if (this.form.fields[fld].default) { this.scope[fld] = this.form.fields[fld].default; } } }, reset: function() { // The form field values cannot be reset with jQuery. Each field is tied to a model, so to clear the field // value, you have clear the model. this.scope[this.form.name + '_form'].$setPristine(); for (var fld in this.form.fields) { this.scope[fld] = ''; this.scope[fld + '_api_error'] = ''; this.scope[this.form.fields[fld].sourceModel + '_' + this.form.fields[fld].sourceField] = ''; if ( this.form.fields[fld].type == 'lookup' && this.scope[this.form.name + '_form'][this.form.fields[fld].sourceModel + '_' + this.form.fields[fld].sourceField] ) { this.scope[this.form.name + '_form'][this.form.fields[fld].sourceModel + '_' + this.form.fields[fld].sourceField].$setPristine(); } if (this.scope[this.form.name + '_form'][fld]) { this.scope[this.form.name + '_form'][fld].$setPristine(); } } if (this.mode == 'add') { this.applyDefaults(); } }, addListeners: function() { // Listen for accordion collapse events and toggle the header icon $('.collapse') .on('show', function() { var element = $(this).parent().find('.accordion-heading i'); element.removeClass('icon-angle-down'); element.addClass('icon-angle-up'); }) .on('hide', function() { var element = $(this).parent().find('.accordion-heading i'); element.removeClass('icon-angle-up'); element.addClass('icon-angle-down'); }); }, build: function(options) { // // Generate HTML. Do NOT call this function directly. Called by inject(). Returns an HTML // string to be injected into the current view. // var html = ''; //Breadcrumbs html += "
\n"; html += "\n
\n"; // Start the well if ( this.has('well') ) { html += "
\n"; } html += "
' + "\n"; html += "
{{ flashMessage }}
\n"; //fields for (var fld in this.form.fields) { var field = this.form.fields[fld]; //Assuming horizontal form for now. This will need to be more flexible later. //text type fields if (field.type == 'text' || field.type == 'password' || field.type == 'email') { if ( (! field.readonly) || (field.readonly && options.mode == 'edit') ) { html += "
' + field.label + '' + "\n"; html += "
\n"; html += "A value is required!\n"; } if (field.type == "email") { html += "A valid email address is required!\n"; } if (field.awPassMatch) { html += "Must match Password value\n"; } html += "\n"; html += "
\n"; html += "
\n"; } } //checkbox if (field.type == 'checkbox') { if ( (! field.readonly) || (field.readonly && options.mode == 'edit') ) { html += "
\n"; html += "
\n"; html += "
\n"; } } //lookup type fields if (field.type == 'lookup') { html += "
' + field.label + '' + "\n"; html += "
\n"; html += "
\n"; html += "\n"; html += "A value is required!\n"; } html += "\n"; html += "
\n"; html += "
\n"; } } //buttons if (this.has('buttons')) { html += "
\n"; html += "
\n"; } for (var btn in this.form.buttons) { var button = this.form.buttons[btn]; //button html += "
\n"; html += "
\n"; } html += "\n"; if ( this.has('well') ) { html += "
\n"; } if (options.related && this.form.related) { html += this.buildCollections(); } return html; }, buildCollections: function() { // // Create TB accordians with imbedded lists for related collections // Should not be called directly. Called internally by build(). // var idx = 1; var form = this.form; var html = "
\n"; for (var itm in form.related) { if (form.related[itm].type == 'collection') { // Start the accordion group html += "
\n"; html += "\n"; html += "
\n"; html += "
\n"; html += "
\n"; html += SearchWidget({ iterator: form.related[itm].iterator, template: form.related[itm], mini: true }); // Add actions(s) html += "
\n"; for (var action in form.related[itm].actions) { html += "\n"; } html += "
\n"; // Start the list html += "
\n"; html += "\n"; html += "\n"; html += "\n"; html += "\n"; for (var fld in form.related[itm].fields) { html += "\n"; } html += "\n"; html += "\n"; html += ""; html += "\n"; html += "\n"; html += "\n"; var cnt = 1; for (var fld in form.related[itm].fields) { cnt++; html += "\n"; } // Row level actions html += ""; html += "\n"; // Message for when a related collection is empty html += "\n"; html += "\n"; html += "\n"; // End List html += "\n"; html += "
#" + form.related[itm]['fields'][fld].label + "
{{ $index + (" + form.related[itm].iterator + "Page * " + form.related[itm].iterator + "PageSize) + 1 }}.{{ " + form.related[itm].iterator + "." + fld + " }}"; for (action in form.related[itm].fieldActions) { html += " "; } html += "
No records matched your search.
\n"; html += "
\n"; // close well html += "
\n"; // close list div html += PaginateWidget({ set: itm, iterator: form.related[itm].iterator, mini: true }); // End Accordion Group html += "
\n"; // accordion inner html += "
\n"; // accordion body html += "
\n"; // accordion group idx++; } } html += "
\n"; return html; } }}]);