I try to improve my wagtail-admin and now I stack because there is no way to open a modal window. Yes, of course, I could create a div
with a close button, but this would be not the right way. As I've got, there is a special function (or object) which is responsible for opening such a window.
There is no reference for such a structure of js objects of wagtail-admin. May be somebody knows, how to do it? Or maybe I should forget about it and make my modal window just by vanilla javascript?
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.
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())
wagtailadmin/home.html
template template.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 %}
admin/...
url that we can request the modal content fromrender_modal_workflow
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)),
# ...
]
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>