Search code examples
javascriptphpjquerywordpressninja-forms

Using dynamically injected Ninja Forms on an AJAX website


I am building a Wordpress site, and using AJAX to load in subsequent pages when the user navigates, so that page transitions are nicely animated.

By default, if you load and inject a page with an embedded Ninja Form, the form simply does not display. There is very little information out there on how to achieve this. I hoped there would be an official out of the box way to get this working, but there doesn't appear to be.

What steps need to be taken in order to get the form to display on the page, when the form has been dynamically loaded with AJAX?


Solution

  • I have experimented with some completely undocumented code samples, and managed to figure it out, along with a few necessary tweaks and additions. I thought I'd share here in case anyone else has difficulty with this.

    Step 1. Enqueue necessary JS and CSS

    Add the following to your functions.php, because Ninja Forms relies on backbone js, and needs css, which won't be loaded if your initial landing page does not already have a form on it.

    add_action('wp_enqueue_scripts', function () {
      // enqueue ninja forms css, including for any ninja forms addons
      wp_enqueue_style('nf-display', content_url('/plugins/ninja-forms/assets/css/display-structure.css'), ['dashicons'], core_dev_version(''));
      wp_enqueue_style('nf-layout-front-end', content_url('/plugins/ninja-forms-style/layouts/assets/css/display-structure.css'), ['nf-display'], core_dev_version(''));
    
      // make sure that backbone is enqueued on the page, as ninja forms relies on this
      wp_enqueue_script('backbone');
    }, 100);
    

    .

    Step 2. Add an AJAX function that returns the form data

    You shouldn't need to edit this, just add it to your functions.php. The javascript we use in step 3 will make its own AJAX call to request some form data in a very specific format.

    add_action('init', function () {
      // if this is not an AJAX form request, return
      if (! isset($_REQUEST[ 'async_form' ])) {
        return;
      }
    
      // clear default loaded scripts.
      global $wp_scripts;
      unset($wp_scripts->registered);
    
      // get the requested form id
      $form_id = absint($_REQUEST['async_form']);
    
      // retrieve the requested form
      ob_start();
      $form = do_shortcode("[ninja_forms id='{$form_id}']");
      ob_get_clean();
    
      // output the requested form on the page
      ob_start();
      NF_Display_Render::output_templates();
      $templates = ob_get_clean();
      $response = [
        'form' => $form,
        'scripts' => $wp_scripts->registered,
        'templates' => $templates
      ];
      echo json_encode($response);
    
      // die, because we don't want anything else to be returned
      die();
    });
    

    .

    Step 3. Add JS helper functions to your landing page

    This simply adds some helpful JS code into your site. You can add this to functions.php as is, or include it in a separate JS file. This is what we will use to load and initialise the form we want.

    add_action('wp_footer', function () {
      // adds a script to the footer
      ?>
      <script type="text/javascript">
    
      var NinjaFormsAsyncForm = function(formID, targetContainer) {
    
        this.formID = formID;
        this.targetContainer = targetContainer;
    
        this.formHTML;
        this.formTemplates;
        this.formScripts;
    
        this.fetch = function(callback) {
          jQuery.post('/', { async_form: this.formID }, this.fetchHandler.bind(this))
          .then(callback);
        }
        this.fetchHandler = function(response) {
          response = JSON.parse( response );
    
          window.nfFrontEnd = window.nfFrontEnd || response.nfFrontEnd;
          window.nfi18n = window.nfi18n || response.nfi18n || {};
    
          this.formHTML = response.form;
          this.formTemplates = response.templates;
          this.formScripts = response.scripts;
        }
    
        this.load = function() {
          this.loadFormHTML(this.formHTML, this.targetContainer);
          this.loadTemplates(this.formTemplates);
          this.loadScripts(this.formScripts);
        }
    
        this.loadFormHTML = function(form, targetContainer) {
          jQuery(targetContainer).append( form );
        }
        this.loadTemplates = function(templates) {
          document.body.innerHTML += templates;
        }
        this.loadScripts = function(scripts) {
          jQuery.each( scripts, function( nfScript ){
            var script = document.createElement('script');
            // note that eval() can be dangerous to use - do your research
            eval( scripts[nfScript].extra.data );
            window.nfFrontEnd = window.nfFrontEnd || nfFrontEnd;
            script.setAttribute('src',scripts[nfScript].src);
            var appendChild = document.head.appendChild(script);
          });
        }
    
        this.remove = function() {
          jQuery(this.targetContainer).empty();
        }
      }
    
      </script>
      <?php
    }, 100);
    

    .

    Step 4. Initialise the form after you AJAX in your page

    I can't tell you how to AJAX in your pages - that's a whole other topic for you to figure out. But, once you have loaded your page content with included Ninja Form, and once that has successfully been on the page, now you need to initialise the form.

    This uses the javascript helper (step 3), which in turn calls the php helper (step 2), and finally displays the form on your page!

    You'll need to know the ID of the form that's been injected. My tactic was to include this as a data-attribute in my page markup.

    var form_id = 1;
    var asyncForm = new NinjaFormsAsyncForm(form_id, '.ninja-forms-dynamic');
    asyncForm.fetch(function () {
      asyncForm.load();
    });
    

    And that's about all there is to it!

    Hopefully this may save others the time and effort of figuring all of this out.