Reputation: 960
Hello im new to wagtail and its been really awesome so far. However im facing an issue trying to create a modal version of the formbuilder. My intentions is to create an action button within the base.html of which the user can click at any point in time and enter a modal pop up form to leave a feed back . Is there a way of accomplishing this?
Upvotes: 1
Views: 699
Reputation: 5176
This is very doable, you will need to work out how you want your modals to look and work by finding a suitable modal library.
Once you have that, you will need to determine how your admin interface will provide the setting of which FormPage
will be used on the base template to render the modal. Wagtail's site settings is a good option.
From here, you then need to get the linked form and use it's get_form()
method (all pages that extend AbstractForm
or AbstractEmailForm
have this). If you want to understand how the form is processed you can see the code here.
The simplest way to handle form submissions is to POST to the original form, this way there does not need to be any additional handling elsewhere. You also get the 'success' page as per normal without having to render that inside the modal.
Below is a basic code example that should get you started.
# settings.py
INSTALLED_APPS += [
'wagtail.contrib.settings',
]
FormPage
, you will need to create the form page separately.makemigrations
& migrate
from django.db import models
from wagtail.contrib.settings.models import BaseSetting, register_setting
from wagtail.admin.edit_handlers PageChooserPanel
# ... other models & imports
@register_setting
class MyAppSettings(BaseSetting):
# relationship to a single form page (one per site)
modal_form_page = models.ForeignKey(
'wagtailcore.Page',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
verbose_name='Modal Form'
)
panels = [
# note the kwarg - this will only allow form pages to be selected (replace base with your app)
PageChooserPanel('modal_form_page', page_type='base.FormPage')
]
3a. Add the css in your static folder (e.g. my-app/static/css/micromodal.css) & then import it in your central header or layout template.
<head>
<!-- ALL OTHER ITEMS -->
<!-- Modal CSS -->
<link href="{% static 'css/micromodal.css' %}" rel="stylesheet" type="text/css">
</head>
3b. Add the JS & init call in your base template, best to do this as the last item before the closing body
tag
<!-- Modal JS -->
<script src="https://unpkg.com/micromodal/dist/micromodal.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
MicroModal.init();
});
</script>
base.html
template to have a convenient way to place the modal & modal trigger. Django's template tags
are a great way to do this.source-page-id
so we can redirect the user to their source page and request.session reading to show a success message.# my-app/templatetags/modal_tags.py
from django import template
from models import MyAppSettings
register = template.Library()
# reminder - you will need to restart Django when adding a template tag
@register.inclusion_tag('tags/form_modal.html', takes_context=True)
def form_modal(context):
request = context['request'] # important - you must have the request in context
settings = MyAppSettings.for_request(request)
form_page = settings.modal_form_page
if not form_page:
return context
form_page = form_page.specific
# this will provide the parts needed to render the form
# this does NOT handle the submission of the form - that still goes to the form page
# this does NOT handle anything to do with rendering the 'thank you' message
context['form_page'] = form_page
context['form'] = form_page.get_form(page=form_page, user=request.user)
return context
{% comment %} e.g. my-app/templates/tags/form_modal.html {% endcomment %}
{% load wagtailcore_tags %}
{% if request.session.form_page_success %}
Thanks for submitting the form!
{% endif %}
{% if form %}
<button data-micromodal-trigger="modal-1">Open {{ form_page.title }} Modal</button>
<div class="modal micromodal-slide" id="modal-1" aria-hidden="true">
<div class="modal__overlay" tabindex="-1" data-micromodal-close>
<div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="modal-1-title">
<header class="modal__header">
<h2 class="modal__title" id="modal-1-title">
{{ form_page.title }}
</h2>
<button class="modal__close" aria-label="Close modal" data-micromodal-close></button>
</header>
<form action="{% pageurl form_page %}" method="POST" role="form">
<main class="modal__content" id="modal-1-content">
{% csrf_token %}
{{ form.as_p }}
{% if page.pk != form_page.pk %}
{% comment %} only provide the source page if not on the actual form page {% endcomment %}
<input name="source-page-id" type="hidden" value="{{ page.pk }}">
{% endif %}
</main>
<footer class="modal__footer">
<input class="modal__btn modal__btn-primary" type="submit">
<button class="modal__btn" data-micromodal-close aria-label="Close this dialog window">Close</button>
</footer>
</form>
</div>
</div>
</div>
{% endif %}
<!-- Footer -->
<footer>
{% form_modal %}
{% include "includes/footer.html" %}
</footer>
source-page-id
from the request.POST
.redirect
shortcut.request.session
with some data so we can show a success message.render_landing_page
on your FormPage
# models.py
class FormPage(AbstractEmailForm):
# .. fields etc
def render_landing_page(self, request, form_submission=None, *args, **kwargs):
source_page_id = request.POST.get('source-page-id')
source_page = Page.objects.get(pk=source_page_id)
if source_page:
request.session['form_page_success'] = True
return redirect(source_page.url, permanent=False)
# if no source_page is set, render default landing page
return super().render_landing_page(request, form_submission, *args, **kwargs)
Upvotes: 3