Skip to content

Port AJAX form submission from premium to core#836

Open
faisalahammad wants to merge 2 commits into
ibericode:mainfrom
faisalahammad:feature/835-ajax-forms
Open

Port AJAX form submission from premium to core#836
faisalahammad wants to merge 2 commits into
ibericode:mainfrom
faisalahammad:feature/835-ajax-forms

Conversation

@faisalahammad
Copy link
Copy Markdown
Contributor

@faisalahammad faisalahammad commented Apr 25, 2026

Summary

Ports the AJAX form submission feature from mc4wp-premium into the free plugin, using the existing REST API endpoint instead of admin-ajax.php. AJAX is enabled by default with a per-form opt-out toggle.

Fixes #835

Problem

AJAX form submission is only available as a premium add-on. The free plugin always does a full page reload on submit.

Solution

Reused the existing mc4wp/v1/form REST endpoint (already handles form processing) and enriched its JSON response with the data needed for client-side updates. The JS loader and submission handler are ported from premium with minimal changes — same event structure, same loading indicator, same CSS class hook (mc4wp-ajax).

When the premium AJAX module is active, the free plugin's AJAX settings and scripts are automatically suppressed via class_exists('MC4WP_AJAX_Forms') guards to avoid duplicates.

Changes

REST endpoint — class-form-manager.php

Before: Returns { "success": true/false }

After:

return [
    'success'     => ! $form->has_errors(),
    'data'        => [
        'event'       => $form->last_event,
        'message'     => $form->get_response_html(),
        'redirect_to' => $form->settings['redirect'],
        'hide_fields' => (bool) $form->settings['hide_after_success'],
    ],
    'error'       => $form->has_errors() ? ['message' => $form->get_response_html(), 'errors' => $form->errors] : null,
];

Why: The JS handler needs event names, messages, and redirect URLs to replicate server-side behavior client-side. This matches the response shape from the premium's respond_to_request().


Asset management — class-asset-manager.php

Registers mc4wp-ajax-forms script, localizes it with REST URL/nonce and loading character, and enqueues it when an AJAX-enabled form is on the page. Guarded with ! class_exists('MC4WP_AJAX_Forms') so it won't conflict with premium.

Uses • as the default loading character with the same animated dot pattern as premium (• → • • → • • •). Filterable via mc4wp_forms_ajax_loading_character.


Form element — class-form-element.php

Adds mc4wp-ajax CSS class when the form's ajax setting is enabled — same hook the JS uses to identify AJAX-enabled forms.


Admin UI — form-settings.php

Adds "Enable AJAX form submission?" radio toggle below the redirect URL field (same position as premium). Wrapped in ! class_exists('MC4WP_AJAX_Forms') so it doesn't duplicate when premium is active.


JavaScript — ajax-forms.js + ajax-form-loader.js

Ported from premium. Intercepts form submit, POSTs to REST endpoint, handles JSON response (success/error messages, hide fields, redirect, events). The loader shows an animated bullet indicator matching premium's UX. Override via data-loading-text attribute on the submit button.


Config — default-form-settings.php + webpack.config.js

Added 'ajax' => 1 default. Added ajax-forms webpack entry point.

Testing

AJAX submit (default):

  • Create form → embed via shortcode → submit with valid email
  • Result: No page reload, success message displays inline ✓

Error handling:

  • Submit invalid email → error message inline, no reload ✓
  • Submit with bad API config → API error inline ✓

Opt-out:

  • Form Settings → AJAX → No → submit
  • Result: Full page reload (classic behavior) ✓

Premium compatibility:

  • Activate premium with ajax-forms module → only one AJAX toggle visible, premium handles submission ✓
  • Deactivate premium → free AJAX settings appear, free script handles submission ✓

Plugin Build

mailchimp-for-wp-fix-835.zip

@dannyvankooten
Copy link
Copy Markdown
Member

@faisalahammad Nice, will look in more detail once I get back from my vacation!

One thought that occurred to me is that we should probably move the JS inside the core forms JavaScript instead of an additional script. In a way that does not break AJAX forms for users on older versions of Premium. Agree?

@faisalahammad
Copy link
Copy Markdown
Contributor Author

faisalahammad commented Apr 25, 2026

@dannyvankooten I've gone ahead and pushed these changes:

  1. Merged AJAX into forms.js: ajax-forms.js is gone. The logic is now part of the core mc4wp-forms-api bundle (adds ~2KB minified).
  2. Premium Compatibility: The AJAX initialization is guarded by checking if mc4wp_ajax_vars exists and hasn't been initialized (inited === true). If an older Premium version is active, the PHP class_exists('MC4WP_AJAX_Forms') guard skips localizing the free vars. Premium localizes its own vars and sets the inited flag, keeping the core AJAX block dormant.
  3. REST API Fixes: Removed the X-WP-Nonce header from the AJAX request to prevent 403 cookie validation errors on cached pages since the endpoint is public. Also added handling for native WP_Error JSON responses so they display correctly inside the form.

Enjoy the rest of your vacation!

@faisalahammad faisalahammad force-pushed the feature/835-ajax-forms branch 2 times, most recently from 7871033 to 401fb25 Compare April 25, 2026 07:49
Copy link
Copy Markdown
Member

@dannyvankooten dannyvankooten left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@faisalahammad Two minor things. Also, with these changes, what does the upgrade path for existing users of MC4WP Premium look like? We should plan for them to be on this same logic so that we can remove MC4WP_AJAX_Forms from the premium plugin, but does this re-use any existing setting keys or do we need additional logic?

What I want is:

  • Users on older Premium plugin with latest Mailchimp for WordPress: AJAX forms via older Premium plugin
  • Users on latest Premium plugin with latest Mailchimp for WordPress: AJAX forms via free plugin

What we should be absolutely certain of:

  • AJAX forms not breaking under Premium users after they update either plugin.

Comment thread includes/forms/class-asset-manager.php Outdated
Comment thread assets/src/js/forms.js
@faisalahammad faisalahammad force-pushed the feature/835-ajax-forms branch from 401fb25 to d027643 Compare May 4, 2026 17:56
- Migrates AJAX form submission from premium to core via REST API.
- Replaces static 'Loading button text' with animated dots.
- Merges AJAX JS logic directly into core forms.js bundle.
- Includes backwards compatibility checks to prevent conflicts with older premium versions.
@faisalahammad faisalahammad force-pushed the feature/835-ajax-forms branch from d027643 to 5473b6c Compare May 4, 2026 18:04
@dannyvankooten
Copy link
Copy Markdown
Member

Hi @faisalahammad,

Can you clarify what happens in the following scenario's and confirm that everything keeps working as expected:

Do we need to take any special steps to ensure Premium users are automatically on the code path from the free plugin once we remove MC4WP_Ajax_Forms from Premium?

- JS: bail early if window.mc4wp_ajax_vars.inited is set (Premium's own
  'already running' flag), preventing any edge-case double-initialization
  if plugin load order ever shifts
- PHP: expand comment on class_exists guard to document the migration
  contract — when Premium drops MC4WP_AJAX_Forms it must declare
  Requires Plugins: mailchimp-for-wp (>=4.13.0) to avoid version-gap
  breakage for users who update Premium but not the free plugin
@faisalahammad
Copy link
Copy Markdown
Contributor Author

Hi @dannyvankooten, I checked the three scenarios. Everything looks safe. Here is the summary.

For scenario 1 and 2, the old premium is active. The code checks if the class MC4WP_AJAX_Forms exists. If it does, the free plugin stops and premium handles everything. I also added a JS check today to be extra safe. They use different variables and URLs so they do not fight.

For scenario 3, when you remove the class from premium, the free plugin will see it and start doing the AJAX automatically via REST API. No extra work needed here.

The only risk is if user updates premium but keeps very old free plugin. Then AJAX will break. To prevent this, the new premium must check for free plugin version 4.13 or higher. The current header only checks if the plugin is active, not the version. This is something we need to do in the premium plugin later.

I also added a comment in class-asset-manager.php to remind us about this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Port AJAX enabled forms from MC4WP Premium to free

2 participants