cancel
Showing results for 
Search instead for 
Did you mean: 

[UTIL] Forms AJAX rendering component

alejandrogarcia
Champ in-the-making
Champ in-the-making
Hi all,

This is something I needed for some customization I'm performing in Share.It is in a very premature state, but perhaps is useful for others. It is a client-side utility component for dynamically render forms retrieved to the forms component (/share/service/components/form) via AJAX requests. I'm not a JavaScrip guru so I'm open to any possible improvement/suggestion/bug fix. There you go:


/**
* Client-side JavaScript library.
*/

// Defines root namespace
if (typeof MyCustom == undefined || !MyCustom) {
   var MyCustom = {};
}

// Defines top-level "custom" namespace, which will contain customization or extensions of existing components or objects in Alfresco
MyCustom.custom = MyCustom.custom || {};

// Defines top-level "util" namespace
MyCustom.util = MyCustom.util || {};

/**
* Renderization and submission of forms generated by Forms Engine via Ajax requests.
*
* To know more about forms engine, invocation parameters and behavior: http://wiki.alfresco.com/wiki/Forms_Developer_Guide
*
* @class MyCustom.util.AjaxFormUI
*/
(function()
{
   var Dom = YAHOO.util.Dom,
      Event = YAHOO.util.Event;
  
   MyCustom.util.AjaxFormUI = function(htmlId, components)
   {
      components = YAHOO.lang.isArray(components) ? components : [];
     
      return MyCustom.util.AjaxFormUI.superclass.constructor.call(
         this,
         "MyCustom.util.AjaxFormUI",
         htmlId,
         components);
   };

   YAHOO.extend(MyCustom.util.AjaxFormUI, Alfresco.component.Base,
   {
      /**
       * The default render config used by method render()
       *
       * @property options
       * @type object
       */
      options:
      {
         itemId : null,
         mode : null,
         htmlid : null,
         container : null,
         successCallback : null,
         successMessage : null,
         failureMessage : null,
         cancelCallback : null
      },
     
      /**
       * Reference to the DOM element which wraps up the form
       *
       * @property formWrapper
       * @type HTMLElement
       */
      formWrapper: null,
     
      /**
       * Retrieves and renders the form by performing a request to the forms engine according
       * to the configuration object provided.
       *
       * @method renderForm
       * @param config {object} Description of the form to be generated
       * The config object has the following form:
       * {
       *    itemId : {string},              // The identifier of the item the form is for, this will be different for each "kind" of item, for "node" it will be a NodeRef.
       *    mode : {string},                // The mode the form will be rendered in, valid values are "view", "edit" and "create".
       *    htmlid : {string},              // The htmlid of the current Surf component
       *    container : {HTMLElement|string},  // Where the form will be allocated
       *    successCallback : {Object}      // A callback object literal used on form submission success
       *    successMessage : {String}       // A submit success message
       *    failureMessage : {String}       // A submit failure message
       *    cancelCallback : {String}       // A callback object literal used when form's cancel button is pressed
       * }
       */
      render: function() {
         // TODO: to check mandatory options/parameters
        
         // Perform AJAX request to get form content from Forms Engine
         Alfresco.util.Ajax.request({
            url: Alfresco.constants.URL_SERVICECONTEXT + "components/form",
            dataObj:
            {
               htmlid: this.options.htmlid, // Ensure unique IDs
               itemKind : "node", // It is fixed at the moment but could be dynamic in the future
               itemId : this.options.itemId,
               mode: this.options.mode,
               submitType: "json",
               formUI: true,
               showCancelButton: true
            },
            successCallback : {
               fn : function(response)
               {
                  // Create form wrapper where the form content is going to be inserted
                  this.formWrapper = document.createElement("div");
                  this.formWrapper.setAttribute("id", this.options.htmlid + "-form-wrapper"); // Set appropriate id attribute
                  this.formWrapper.setAttribute("class", "share-form"); // Set appropriate class
                 
                  // Fill the wrapper with the form component returned by the XHR request into
                  this.formWrapper.innerHTML = response.serverResponse.responseText;
                 
                  // Append form wrapper to container element
                  Dom.get(this.options.container).appendChild(this.formWrapper);
               },
               scope : this
            },
            failureMessage : Alfresco.util.message("message.failure"), // Generic failure message
            scope: this,
            execScripts: true // Automatically executes script tags (<script>) returned in the forms engine's response
         });
        
         // Adjust form to work in dialog
         YAHOO.Bubbling.on("formContentReady", this._onFormContentReady, this);
        
         // When form is submitted make sure we hide the dialog after a successful submit and display a message when it fails.
         YAHOO.Bubbling.on("beforeFormRuntimeInit", this._onBeforeFormRunTimeInit, this);
      },
     
      /**
       *
       *
       * @method _onFormContentReady
       * @param layer
       * @param args
       * @private
       */
      _onFormContentReady: function(layer, args)
      {
         if (Alfresco.util.hasEventInterest(this.options.htmlid + "-form", args))
         {
            // Close dialog when cancel button is clicked
            var cancelButton = args[1].buttons.cancel;
           
            // Destroy the form when cancel button is clicked, by default
            cancelButton.addListener("click", this._destroy, this, true);
           
            // Execute custom function on button click if the object that describes it exists
            if (this.options.cancelCallback && YAHOO.lang.isFunction(this.options.cancelCallback.fn))
            {
               cancelButton.addListener(
                  "click", // Event triggered
                  this.options.cancelCallback.fn, // Function executed
                  this.options.cancelCallback.scope || {}, // Scope
                  true); // The custom object that is passed is used for the execution scope (so it becomes "this" in the callback)
            }
         }
      },
     
      /**
       *
       *
       * @method _onBeforeFormRunTimeInit
       * @param layer
       * @param args
       * @private
       */
      _onBeforeFormRunTimeInit: function(layer, args)
      {
         if (Alfresco.util.hasEventInterest(this.options.htmlid + "-form", args))
         {
            args[1].runtime.setAJAXSubmit(true,
            {
               successCallback :
               {
                  fn : function PopupMananger_displayForm_formSuccess(response)
                  {
                     // Destroy the form wrapper and its content
                     this.options.container.removeChild(this.formWrapper);
                    
                     // Invoke success
                     if (this.options.successCallback && YAHOO.lang.isFunction(this.options.successCallback.fn))
                     {
                        // TODO: does the callback function really needs to receive arguments??
                        this.options.successCallback.fn.call(this.options.successCallback.scope || {}, response, this.options.successCallback.obj)
                     }
                  },
                  scope : this
               },
               successMessage : this.options.successMessage,
               failureMessage : this.options.failureMessage
            });
         }
      },
     
      /**
       * Implements self-destruction of the component
       *
       * @method _destroy
       * @param event
       * @param obj
       * @private
       */
      _destroy: function(event, obj)
      {
         // Remove the form wrapper from the DOM
         obj.options.container.removeChild(Dom.get(obj.formWrapper));
        
         // Form destruction signal
         YAHOO.Bubbling.fire("formContainerDestroyed");
        
         // Unsubscribe events
         YAHOO.Bubbling.unsubscribe("beforeFormRuntimeInit", this._onBeforeFormRunTimeInit, obj);
         YAHOO.Bubbling.unsubscribe("formContentReady", this._onFormContentReady, obj);
        
         // Clean instance attributes
         delete this.formWrapper;
        
         // Invoke super (Alfresco.component.Base) destroy method
         this.destroy();
      },
   });
})();


A form can be easily initialized as follows:


                        var form = new MyCustom.util.AjaxFormUI();
                        form.setOptions(
                        {
                           itemId : "workspace://SpacesStore/73ddea6e-c937-4db3-af65-44c82150a72f",
                           mode : "edit",
                           htmlid : parent.id,
                           container : Dom.get(parent.id + "-container"),
                           successCallback:
                           {
                              fn: parent.onCreateUserFormCancelClick,
                              scope: parent
                           },
                           successMessage : parent._msg("message.update-success"),
                           cancelCallback:
                           {
                              fn: parent.onCreateUserFormCancelClick,
                              scope: parent
                           }
                        });
                        form.render();


Hope it helps.

Regards.
6 REPLIES 6

alejandrogarcia
Champ in-the-making
Champ in-the-making
Forgot to post a usage example Smiley Happy

zladuric
Champ on-the-rise
Champ on-the-rise
This looks excellent. I stumbled across your post looking for something else, found my answer in your code. Although I won't be using the code myself, this looks very neat for the future uses Smiley Happy

I'm glad you found it useful Smiley Very Happy

The component has changed slightly in the mean time, let me know if you want me to post it. Some feedback in terms of improvements would be appreciated as well.

Ragards.

Useful post!!! helped me… Thanks!

alejandrogarcia
Champ in-the-making
Champ in-the-making
Glad to know it Paiyyavj13. If you come up with any improvement or you find issues, please share with us!

Cheers.

kriti1602
Champ in-the-making
Champ in-the-making
thank you for this post !
It helped me solve many issues in my code Smiley Happy