What is the correct method for launching a modal window in wagtail-admin?

I am currently working on enhancing my wagtail-admin interface, but I have hit a roadblock when trying to open a modal window. While I could create a div with a close button, I believe there must be a more appropriate method for achieving this. It seems that there is a specific function or object within wagtail-admin that handles the opening of modal windows.

Unfortunately, I have not been able to locate any documentation outlining the structure of these javascript objects within wagtail-admin. Does anyone have insights on how to accomplish this task? Or should I abandon the idea and build my modal window using plain vanilla javascript instead?

Answer №1

It's important to note that there is no documented method for utilizing Wagtail admin modals as they are.

However, by delving into the source code, you can harness the modal workflow to create your own custom modals. In Wagtail, this involves providing a server-side template response with render_modal_workflow.

On the client side, there is a function available called ModalWorkflow. This function calls a URL asynchronously and renders the HTML content within the modal upon response, expecting a response formatted by the aforementioned render_modal_workflow helper.

With these fundamentals in place, you can incorporate open behavior via a button trigger, handle errors, execute render callbacks, and define callbacks based on values retrieved from within the modal.

Below is a basic example demonstrating how to render a modal in the admin using this approach.

Example

1. Render some HTML content with a button trigger

  • For demonstration purposes, let's render a modal on the Wagtail home (dashboard) page.
  • Using the construct_homepage_panels, we can add HTML to a panel partway 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 handling editing have the modal-workflow script loaded.
  • To include it on this specific page, override the wagtailadmin/home.html template.
  • Add jQuery to identify elements with the data-modal-trigger attribute and attach an onClick listener to call our ModalWorkflow function. Data passed back to the modal view can include any specific information.
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 for when the modal content
              calls modal.respond(callbackName, params)
          'onload' (optional): dict of callbacks for loading steps of the workflow.
              The 'step' field in the response identifies the callback to call, passing 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 manage the modal requests

  • Ensure there is an admin/... URL for requesting the modal content.
  • This URL should lead to a view returning a response based on render_modal_workflow.
  • You can initialize data on the client side alongside using a typical 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 display the modal content

  • All modals utilize the same shared header template to ensure consistency.
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>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Leveraging AngularJS for a Windows store app

After attempting to integrate AngularJS into my Windows store application, I came across a few recommended solutions: Unfortunately, these solutions did not work as expected. While I didn't encounter the Unable to add dynamic content error, AngularJS ...

What is the best way to create an image carousel that continuously loops instead of resetting?

Currently tackling an issue with my image carousel project. The automatic function of the slider is not functioning as desired. Instead of looping back to the first image when it reaches the last one, it moves all the way back to the beginning, displaying ...

Is it possible to use the Three.js LoadingManager to load videos?

I am currently using THREE.LoadingManager in conjunction with various loaders to efficiently handle the loading of textures, models, images, and cubeTextures before my app is displayed. This setup enables me to present a loading bar that shows the overall ...

Guide on bringing in Javascript file into your Ionic/Angular application

Within my Ionic 2 application, I have incorporated three.js along with a PLYLoader extension for three.js (accessible here: https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/PLYLoader.js) Integrating three.js is straightforward by includi ...

An easy way to create an input field after clicking a button

When I try to add a field on Button Click, the default field is not showing and does not get added upon button click I have put in my best effort but I cannot figure out what the problem is. I have added functions and used Math to generate a unique id. Th ...

Nodemailer is functioning properly in a local environment, however, it is encountering issues when

I am facing an issue with Nodemailer where it is sending emails successfully on my local machine but not on Lambda. I have tried various solutions from Stack Overflow, but none of them seem to be working for me. One suggestion was to make the sendEmail fun ...

Retrieve the corresponding value from an object received from express and display it on a jade template

I'm currently working on a solution to display the correct user information on a page when users navigate to "/users/:name". For example, I want to show "welcome user2" if user2 is logged in. My approach involves passing along the parameter from "/use ...

Using an AngularJS ng-repeat alias expression with multiple filters

As stated in the Angular ngRepeat documentation, the alias expression can only be used at the end of the ngRepeat: It's important to note that `as [variable name]` is not an operator, but rather a part of the ngRepeat micro-syntax and must be place ...

Tips for Organizing an Array: Grouping Categories and Products

I have a single array and I am looking to separate categories from the products listed within it. const product = [{ id: 1, name: 'Cloth', cat: ['fashion', 'man', 'women'] }, { id: 2, name: &apos ...

Plane is constantly cloaked in darkness

I'm having trouble adding a texture to my plane that repeats both horizontally and vertically. Every time I try to apply the texture, it shows up as black. I've attempted to add some lights to the scene, but the issue persists without any errors. ...

A Vue.js component modifies one store state variable but leaves another unchanged

Currently developing a Vue 3 application that utilizes Vuex and the Composition API. Encountered an issue while working on it. A component is supposed to display elements based on the state of two store variables. One is an array, and the other is a bool ...

Skipping certain key-value pairs during the conversion from JSON to Excel Worksheet using the XLSX library in JavaScript

I have a set of objects in JSON format within my JavaScript code and I am looking to transform this data into an Excel worksheet. Within the JSON structure, there are certain key-value pairs that I do not wish to include in the Excel output. For instance, ...

Utilizing Bootstrap Plugins with Nuxt.js: A Step-by-Step Guide

"dependencies": { "ant-design-vue": "^1.7.2", "bootstrap": "^4.6.0", "core-js": "^3.8.3", "nuxt": "^2.14.12", "popper.js": "^1.16.1" } ...

Navigating through the Table using AngularJS

I was able to successfully loop through a table using AngularJS to retrieve values from a specified scope named {{rec}} as shown below. HTML <div id="AngularContainer" data-ng-app="myApp" data-ng-controller="myCtrl"> < ...

Tips for adding text to your d3 force layout

Looking to incorporate text into a force layout using SVG. I've created an svg group and added a circle successfully, but having trouble with the text. Here's the code snippet: var node = svg.selectAll("g") .data(measures.nod ...

implementing a like button next to every item on a list using ajax

Help needed: How can I add a like button next to each item on my 'destinations' list? I'm struggling to figure out the placement. Any suggestions? Below is the content of my dashboard.html.erb file: <div class="destination-list"> ...

Creating a jQuery AJAX form that allows users to upload an image, submit the form, and store the entered values in a MySQL database

I am struggling with an HTML form that I am trying to submit using jQuery's $.ajax(); The form needs to: 1. Upload an image to a directory with error checks 2. Save the image path to a MySQL database 3. Insert two other form values (input and select) ...

Tips for inserting items into an array of objects?

I have an array of objects with categories and corresponding points, and I need to calculate the total points for each category. { category: A, points:2 }, { category: A points: 3 }, { category: B, points: ...

Limiting zero is ineffective when it comes to pop-up issues

Hey there, I'm looking to prevent users from inputting zero and dot in a specific field, which is currently working fine. However, when the field is within a pop-up, the code below doesn't seem to work. <script> $('#name').keyp ...

Showing canvas lines while dragging (using only plain JavaScript, with React.JS if needed)

Is there a way to add lines with two clicks and have them visible while moving the mouse? The line should only be drawn when clicking the left mouse button. Any suggestions on how I can modify my code to achieve this functionality? Currently, the lines are ...