Source: forms/DynamicForm.js

/**
 * Copyright (C) 2005-2016 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * <p>This module extends the standard [form]{@link module:alfresco/forms/Form} to provide the ability
 * to dynamically re-draw a form based on payload published to a 
 * [subscribed topic]{@link module:alfresco/forms/DynamicForm#subscriptionTopic}. The published payload
 * should contain a dot-notation [property]{@link module:alfresco/forms/DynamicForm#formWidgetsProperty}
 * (the default is "value") that contains a form model to render. This model is expected to be a "stringified"
 * JSON array of [form control]{@link module:alfresco/forms/controls/BaseFormControl} that should be
 * rendered. If the [property]{@link module:alfresco/forms/DynamicForm#formWidgetsProperty} is actually
 * a JavaScript object (e.g. it is not "stringified" JSON) then 
 * [formWidetsPropertyStringified]{@link module:alfresco/forms/DynamicForm#formWidetsPropertyStringified}
 * should be configured to be false.</p>
 * <p>The individual form controls can be set with their own value, however if an overall form value
 * needs to be set after rendering then a dot-notation 
 * [property]{@link module:alfresco/forms/DynamicForm#formValueProperty} should be configured that identifies
 * where the form value is expected to be found in the published payload.</p>
 * <p>The form buttons will only be displayed if the form contains any controls initially and the buttons
 * will be hidden if the payload published on the [subscribed topic]{@link module:alfresco/forms/DynamicForm#subscriptionTopic}
 * contains an empty array as the form model.</p>
 *
 * @example <caption>Basic configuration using defaults:</caption>
 * {
 *   name: "alfresco/forms/DynamicForm",
 *   config: {
 *     subscriptionTopic: "UPDATED_FORM_DETAILS",
 *     okButtonPublishTopic: "DYNAMIC_FORM_POST"
 *   }
 * }
 *
 * @example <caption>Configuration for alternative, non-stringified form model with an expected form value:</caption>
 * {
 *   name: "alfresco/forms/DynamicForm",
 *   config: {
 *     subscriptionTopic: "UPDATED_FORM_DETAILS",
 *     okButtonPublishTopic: "DYNAMIC_FORM_POST",
 *     formWidgetsProperty: "widgets",
 *     formWidetsPropertyStringified: false,
 *     formValueProperty: "formValue",
 *   }
 * }
 *  
 * @module alfresco/forms/DynamicForm
 * @extends module:alfresco/forms/Form
 * @author Dave Draper
 */
define(["dojo/_base/declare",
        "alfresco/forms/Form",
        "dojo/_base/lang",
        "dojo/dom-class"], 
        function(declare, Form, lang, domClass) {
   
   return declare([Form], {
      
      /**
       * An array of the CSS files to use with this widget.
       * 
       * @instance
       * @type {object[]}
       * @default [{cssFile:"./css/DynamicForm.css"}]
       */
      cssRequirements: [{cssFile:"./css/DynamicForm.css"}],

      /**
       * The topic that will be subscribed to in the [postCreate]{@link module:alfresco/forms/DynamicForm#postCreate}
       * function to trigger the redrawing of the form. It is expected that this will be configured with a custom value
       * but a default value is provided.
       *
       * @instance
       * @type {string}
       * @default
       */
      subscriptionTopic: "ALF_DYNAMIC_FORM_UPDATE",

      /**
       * 
       * @instance
       */
      postCreate: function alfresco_forms_DynamicForm__postCreate() {
         this.alfSubscribe(this.subscriptionTopic, lang.hitch(this, this.onDynamicFormUpdate));
         this.inherited(arguments);
         domClass.add(this.domNode, "alfresco-forms-DynamicForm");

         if (!this.widgets || this.widgets.length === 0)
         {
            // Hide the buttons when there aren't any form controls to display initially...
            domClass.add(this.buttonsNode, "alfresco-forms-DynamicForm--hidden");
         }
      },

      /**
       * This is the property in the payload published on the 
       * [subscriptionTopic]{@link module:alfresco/forms/DynamicForm#subscriptionTopic}
       * that contains the JSON model to render as form widgets.
       *
       * @instance
       * @type {string}
       * @default
       */
      formWidgetsProperty: "value",

      /**
       * This indicates whether or not the 
       * [formWidgetsProperty]{@link module:alfresco/forms/DynamicForm#formWidgetsProperty} is 
       * expected to be "stringified" (e.g. it is pure JSON that requires parsing). If this is set to
       * true (which is the default) then an attempt will be made to parse any data found.
       *
       * @instance
       * @type {boolean}
       * @default
       */
      formWidetsPropertyStringified: true,

      /**
       * This is the property in the payload published on the 
       * [subscriptionTopic]{@link module:alfresco/forms/DynamicForm#subscriptionTopic}
       * that contains a value to set on the form.
       *
       * @instance
       * @type {string}
       * @default
       */
      formValueProperty: null,

      /**
       * This function is called whenever the [subscriptionTopic]{@link module:alfresco/forms/DynamicForm#subscriptionTopic}
       * is published on. 
       * 
       * @instance
       * @param {object} payload A payload containing a 'value' attribute with the JSON model to render
       */
      onDynamicFormUpdate: function alfresco_forms_DynamicForm__onDynamicFormUpdate(payload) {
         // jshint maxcomplexity:false
         var widgetModel = lang.getObject(this.formWidgetsProperty, false, payload);
         if (!widgetModel)
         {
            this.alfLog("warn", "No '" + this.formWidgetsProperty + "' attribute found in redraw form request payload", payload, this);
         }
         else
         {
            // Destroy all the previous form fields...
            if (this._form)
            {
               this._form.destroyDescendants(false);
            }
            try
            {
               // It's important to clear the payload of the "OK" button to ensure that old form
               // data does not get published
               // TODO: This should also be done for additional buttons, but is harder to do without preserving
               //       the default publishPayload for them.
               if (this.okButton)
               {
                  this.okButton.publishPayload = {};
               }
               if (this.formWidetsPropertyStringified)
               {
                  try
                  {
                     var widgets = JSON.parse(widgetModel);
                     this.processWidgets(widgets, this._form.domNode);
                     if (widgets.length > 0)
                     {
                        domClass.remove(this.buttonsNode, "alfresco-forms-DynamicForm--hidden");
                     }
                  }
                  catch(e)
                  {
                     this.alfLog("error", "The following error occurred attempting to parse a DynamicForm widget model", e, this, widgetModel);
                  }
               }
               else
               {
                  this.processWidgets(widgetModel, this._form.domNode);
                  if (widgetModel.length > 0)
                  {
                     domClass.remove(this.buttonsNode, "alfresco-forms-DynamicForm--hidden");
                  }
               }

               if (this.formValueProperty)
               {
                  var value = lang.getObject(this.formValueProperty, false, payload);
                  if (value)
                  {
                     this.setValue(value);
                  }
               }

               if (payload.formSubmissionTopic && this.okButton)
               {
                  this.okButton.publishTopic = payload.formSubmissionTopic;
               }
            }
            catch (e)
            {
               this.alfLog("error", "An error occurred redrawing the form", e, this);
            }
         }
      }

   });
});