The short answer is that there is no documented way to use the existing Wagtail admin modals.
However, with a bit of looking at the source code it is possible to leverage the modal workflow to implement your own modals. The approach in Wagtail is to have a server side template response supplied by render_modal_workflow
.
On the client, a function is available ModalWorkflow
. That will call a URL async and render the html content inside the modal on response, it expects a response formed by the above render_modal_workflow
helper.
From these basics it is possible to add open behaviour by a button trigger, error handling, render callbacks and callbacks based on value from inside the modal.
Below is a bare minimum example of a way to render a modal in the admin using this approach.
Example
1. Render some html content that has a button as a trigger
- For the sake of example, we will render a modal on the Wagtail home (dashboard) page.
- Using the
construct_homepage_panels
we can add some html to a panel part way down the page.
wagtail_hooks.py
from django.utils.safestring import mark_safe
from wagtail.core import hooks
class WelcomePanel:
order = 110
def render(self):
return mark_safe("""
<section class="panel summary nice-padding">
<h3>Dashboard Panel Section Title</h3>
<button data-modal-trigger="some-param">Open Modal</button>
</section>
""")
@hooks.register('construct_homepage_panels')
def add_another_welcome_panel(request, panels):
panels.append(WelcomePanel())
2. Ensure the modal-workflow JS script is loaded
- By default, only pages that handle editing have the modal-workflow script loaded
- To add it to this specific page we need to override the
wagtailadmin/home.html
template template.
- We will also add a bit of jquery to find any elements that have the
data-modal-trigger
attribute and add an onClick
listener which will call our ModalWorkflow
function. This data can be passed back to the modal view, along with any other specific data.
templates/wagtailadmin/home.html
{% extends "wagtailadmin/home.html" %}
{% load wagtailadmin_tags %}
{% comment %}
Javascript declaration added to bring in the modal loader, by default it is only available on edit pages
example of usage - wagtail/search/templates/wagtailsearch/queries/chooser_field.js
{% endcomment %}
{% block extra_js %}
{{ block.super }}
<script src="{% versioned_static 'wagtailadmin/js/modal-workflow.js' %}"></script>
<script type="text/javascript">
$(function() {
$('[data-modal-trigger]').on('click', function(element) {
/* options passed in 'opts':
'url' (required): initial
'responses' (optional): dict of callbacks to be called when the modal content
calls modal.respond(callbackName, params)
'onload' (optional): dict of callbacks to be called when loading a step of the workflow.
The 'step' field in the response identifies the callback to call, passing it the
modal object and response data as arguments
*/
ModalWorkflow({
onError: function(error) { console.log('error', error); },
url: '/admin/modal/?trigger=' + element.target.dataset.modalTrigger
});
});
});
</script>
{% endblock %}
3. Create a view and url to handle the modal requests
- Ensure there is an
admin/...
url that we can request the modal content from
- This url must go to a view that returns a response based on
render_modal_workflow
- It is possible to initiate data on the client side along with using a normal Django template response for the server side rendered modal content
views.py
from django.template.response import TemplateResponse
from wagtail.admin.modal_workflow import render_modal_workflow
def modal_view(request):
return render_modal_workflow(
request,
'base/modal.html', # html template
None, # js template
{'trigger': request.GET.get('trigger')}, # html template vars
json_data={'some': 'data'} # js template data
)
urls.py
from django.conf.urls import url
from .views import modal_view
urlpatterns = [
url(r'^admin/modal/', modal_view, name='modal'),
url(r'^admin/', include(wagtailadmin_urls)),
# ...
]
4. Set up your template to render the modal content
- Modals all use the same shared header template, which gives a nice way to make it feel consistent.
templates/base/modal.html
{% include "wagtailadmin/shared/header.html" with title="Modal Title" icon="no-view" %}
<div class="nice-padding">
<p>Modal Triggered by {{ trigger }}</p>
</div>