ReStructure makes it simple to add AJAX functionality using standard Rails helpers with remote: true. This approach allows you to create interactive features without writing custom JavaScript code.
A complete AJAX implementation requires three components:
- View with AJAX link/button - Triggers the request
- Controller action - Processes the request and renders a partial
- Response partials - Rendered and inserted into the page
- User clicks the AJAX link - The
remote: trueattribute prevents default navigation - Rails sends AJAX request - POST to
/admin/dynamic_models/:id/run_batch_now - Controller processes request - Performs business logic and prepares response data
- Controller renders partial - Returns HTML fragment instead of full page
- ReStructure updates DOM - Finds container with matching
data-subscriptionand injects content - User sees result - Success or error message appears in the container
Follow this pattern for any AJAX interaction:
# config/routes.rb
resources :my_resources do
member do
post :my_action # or get, put, patch, delete
end
end<!-- app/views/my_resources/_form.html.erb -->
<%= link_to my_action_my_resource_path(@resource),
remote: true,
class: 'btn btn-primary',
method: :post,
data: { disable_with: "Processing..." } do %>
<span class="glyphicon glyphicon-cog"></span> Perform Action
<% end %>
<div class="my-action-result-container"
id="my-action-result-container"
data-subscription="my-action-result">
</div>remote: true- Makes the link submit via AJAXmethod: :post- Specifies HTTP methoddata: { disable_with: "..." }- Shows loading state during request- Container div - Receives the response content via
data-subscriptionattribute
# app/controllers/my_resources_controller.rb
def my_action
@resource = MyResource.find(params[:id])
begin
# Perform your business logic
result = @resource.do_something
@success_message = "Action completed successfully: #{result}"
render partial: 'my_resources/my_action_success'
rescue StandardError => e
Rails.logger.error "Action failed: #{e.message}"
@error_message = "Action failed: #{e.message}"
render partial: 'my_resources/my_action_error'
end
endKey Points:
- Renders different partials based on success or error (or anything else you want)
- No
respond_toblock needed - Rails handles AJAX automatically
<!-- app/views/my_resources/_my_action_success.html.erb -->
<div class="data-results">
<div data-result="my-action-result" class="alert alert-success">
<%= @success_message %>
</div>
</div><!-- app/views/my_resources/_my_action_error.html.erb -->
<div class="data-results">
<div data-result="my-action-result" class="alert alert-danger">
<%= @error_message %>
</div>
</div>Key Points:
- Both partials use
data-result="dynamic-model--run-batch-result"attribute - This matches the
data-subscription="dynamic-model--run-batch-result"in the container - ReStructure automatically injects the rendered content into the matching container
The data-result and data-subscription attributes must match:
- Container:
data-subscription="my-action-result" - Partial:
data-result="my-action-result"
Use data: { disable_with: "..." } to prevent double-clicking:
<%= link_to path, remote: true,
data: { disable_with: "<span class='glyphicon glyphicon-refresh spinning'></span> Processing..." } do %>
Click Me
<% end %>Specify the appropriate HTTP method:
- GET - For retrieving data (default)
- POST - For creating or triggering actions
- PUT/PATCH - For updates
- DELETE - For deletions
<%= link_to path, remote: true, method: :delete do %>
Delete
<% end %>You can also use forms with AJAX:
<%= form_with model: @resource, remote: true do |f| %>
<%= f.text_field :name %>
<%= f.submit "Save", data: { disable_with: "Saving..." } %>
<% end %>
<div data-subscription="form-result"></div>You can render different partials for different scenarios:
def my_action
if condition_a
render partial: 'response_a'
elsif condition_b
render partial: 'response_b'
else
render partial: 'response_default'
end
endIn system specs, helper methods handle AJAX automatically:
# The helper waits for AJAX to complete
click_link 'Run Batch Now'
finish_page_loading
expect(page).to have_content('Batch processing completed')Start with a regular link, then add AJAX:
<!-- Before: regular link -->
<%= link_to "Do Something", my_action_path(@resource), method: :post %>
<!-- After: AJAX link -->
<%= link_to "Do Something", my_action_path(@resource),
remote: true, method: :post %>Combine with modal for confirmation dialogs:
<a class="btn btn-primary show-in-modal"
data-content-el="#confirm-action"
data-title="Confirm Action">
Do Something
</a>
<div id="confirm-action" class="hidden">
<h2>Are you sure?</h2>
<%= link_to "Yes, do it", my_action_path(@resource),
remote: true, method: :post,
data: { disable_with: "Processing..." } %>
<div data-subscription="action-result"></div>
</div>For long-running tasks, you can poll for updates:
// In your view
<script>
setInterval(function() {
$.get('/my_resources/<%= @resource.id %>/status', function(data) {
$('#status-container').html(data);
});
}, 5000); // Poll every 5 seconds
</script>- Check that
data-subscriptionmatchesdata-result - Verify the container exists in the DOM when link is clicked
- Check browser console for JavaScript errors
- Verify controller is rendering the partial
Add data: { disable_with: "..." } to prevent multiple clicks
- Verify
remote: trueis present - Check that
jquery-ujsis loaded (Rails default) - Check browser console for errors
- Verify route exists:
rails routes | grep my_action