/django-bootstrap-modal-forms

A Django plugin for creating AJAX driven forms in Bootstrap modal.

Primary LanguagePythonMIT LicenseMIT

Django Bootstrap Modal Forms

A Django plugin for creating AJAX driven forms in Bootstrap modal.

Live Demo

Demo

Installation

  1. Install django-bootstrap-modal-forms:

    $ pip install django-bootstrap-modal-forms
  2. Add bootstrap_modal_forms to your INSTALLED_APPS in settings.py:

    INSTALLED_APPS = [
        ...
        'bootstrap_modal_forms',
        ...
    ]
  3. Include Bootstrap, jQuery and jquery.bootstrap.modal.forms.js on every page where you would like to set up the AJAX driven Django forms in Bootstrap modal.

IMPORTANT: Adjust Bootstrap and jQuery file paths to match yours, but include jquery.bootstrap.modal.forms.js exactly as in code bellow.

<head>
    <link rel="stylesheet" href="{% static 'assets/css/bootstrap.css' %}">
</head>

<body>
    <script src="{% static 'assets/js/bootstrap.js' %}"></script>
    <script src="{% static 'assets/js/jquery.js' %}"></script>
    <script src="{% static 'js/jquery.bootstrap.modal.forms.js' %}"></script>
    <!-- You can alternatively load the minified version -->
    <script src="{% static 'js/jquery.bootstrap.modal.forms.min.js' %}"></script>
</body>

How it works?

  1. Click event on html element instantiated with modalForm opens modal
  2. Form at formURL is appended to the modal
  3. On submit the form is POSTed via AJAX request to formURL
  4. Unsuccessful POST request returns errors, which are shown in modal
  5. Successful POST request submits the form and redirects to success_url and shows success_message, which are both defined in related Django view

Usage

1. Form

Define BookModelForm and inherit built-in form BSModalModelForm.

2. Form's html

Define form's html and save it as Django template.

  • Bootstrap 4 modal elements are used in this example.
  • Button triggering the submission should have type attribute set to "button" and not "submit".
  • Add class="submit-btn" or custom submitBtn class (see paragraph Options) to this button.
  • Form will POST to formURL defined in #6.
  • Add class="invalid" or custom errorClass (see paragraph Options) to the elements that wrap the fields.
  • class="invalid" acts as a flag for the fields having errors after the form has been POSTed.

3. Class-based view

Define a class-based view BookCreateView and inherit from built-in generic view BSModalCreateView. BookCreateView processes the form defined in #1, uses the template defined in #2 and redirects to success_url showing success_message.

4. URL for the view

Define URL for the view in #3.

5. Bootstrap modal and trigger element

Define the Bootstrap modal window and html element triggering modal opening.

  • Single modal can be used for multiple modalForms in single template (see #6).
  • When using multiple modals on the same page each modal should have unique id and the same value should also be set as modalID option when instantiating modalForm on trigger element.
  • Trigger element (in this example button with id="create-book") is used for instantiation of modalForm in #6.
  • Any element can be trigger element as long as modalForm is bound to it.
  • Click event on trigger element loads form's html from #2 within <div class="modal-content"></div> and sets action attribute of the form to formURL set in #6.
index.html

<div class="modal fade" tabindex="-1" role="dialog" id="modal">
  <div class="modal-dialog" role="document">
    <div class="modal-content"></div>
  </div>
</div>

<!-- Create book button -->
<button id="create-book" class="btn btn-primary" type="button" name="button">Create book</button>

6. modalForm

Add script to the template from #5 and bind the modalForm to the trigger element. Set BookCreateView URL defined in #4 as formURL property of modalForm.

  • If you want to create more modalForms in single template using the single modal window from #5, repeat steps #1 to #4, create new trigger element as in #5 and bind the new modalForm with unique URL to it.
  • Default values for modalID, modalContent, modalForm and errorClass are used in this example, while formURL is customized. If you customize any other option adjust the code of the above examples accordingly.

Async create/update with or without modal closing on submit

Set asyncUpdate and asyncSettings settings to create or update objects without page redirection to successUrl and define whether a modal should close or stay opened after form submission. See comments in example below and paragraph modalForm options for explanation of asyncSettings.

modalForm options

modalID

Sets the custom id of the modal. Default: "#modal"

modalContent

Sets the custom class of the element to which the form's html is appended. If you change modalContent to the custom class, you should also change modalForm accordingly. To keep Bootstrap's modal style you should than copy Bootstrap's style for modal-content and set it to your new modalContent class. Default: ".modal-content"

modalForm

Sets the custom form selector. Default: ".modal-content form"

formURL

Sets the url of the form's view and html. Default: null

errorClass

Sets the custom class for the form fields having errors. Default: ".invalid"

submitBtn

Sets the custom class for the button triggering form submission in modal. Default: ".submit-btn"

asyncUpdate

Sets asynchronous content update after form submission. Default: false

asyncSettings.closeOnSubmit

Sets whether modal closes or not after form submission. Default: false

asyncSettings.successMessage

Sets successMessage shown after succesful for submission. Should be set to string defining message element. See asyncSuccessMessage example above. Default: null

asyncSettings.dataUrl

Sets url of the view returning new queryset = all of the objects plus newly created or updated one after asynchronous update. Default: null

asyncSettings.dataElementId

Sets the id of the element which renders asynchronously updated queryset. Default: null

asyncSettings.dataKey

Sets the key containing asynchronously updated queryset in the data dictionary returned from the view providing updated queryset. Default: null

asyncSettings.addModalFormFunction

Sets the method needed for reinstantiation of event listeners on button after asynchronous update. Default: null

modalForm default settings object and it's structure

Forms

Import forms with from bootstrap_modal_forms.forms import BSModalForm.

BSModalForm

Inherits PopRequestMixin and Django's forms.Form.

BSModalModelForm

Inherits PopRequestMixin, CreateUpdateAjaxMixin and Django's forms.ModelForm.

Mixins

Import mixins with from bootstrap_modal_forms.mixins import PassRequestMixin.

PassRequestMixin

Puts the request into the form's kwargs.

PopRequestMixin

Pops request out of the kwargs and attaches it to the form's instance.

CreateUpdateAjaxMixin

Saves or doesn't save the object based on the request type.

DeleteMessageMixin

Deletes object if request is not ajax request.

LoginAjaxMixin

Authenticates user if request is not ajax request.

Generic views

Import generic views with from bootstrap_modal_forms.generic import BSModalFormView.

BSModalFormView

Inherits PassRequestMixin and Django's generic.FormView.

BSModalCreateView

Inherits PassRequestMixin and Django's SuccessMessageMixin and generic.CreateView.

BSModalUpdateView

Inherits PassRequestMixin and Django's SuccessMessageMixin and generic.UpdateView.

BSModalReadView

Inherits Django's generic.DetailView.

BSModalDeleteView

Inherits DeleteMessageMixin and Django's generic.DeleteView.

Examples

To see django-bootstrap-modal-forms in action clone the repository and run the examples locally:

$ git clone https://github.com/trco/django-bootstrap-modal-forms.git
$ cd django-bootstrap-modal-forms
$ pip install -r requirements.txt
$ python manage.py migrate
$ python manage.py runserver

Tests

Run unit and functional tests inside of project folder:

$ python manage.py test

Example 1: Signup form in Bootstrap modal

For explanation how all the parts of the code work together see paragraph Usage. To test the working solution presented here clone and run Examples.

Example 2: Login form in Bootstrap modal

For explanation how all the parts of the code work together see paragraph Usage. To test the working solution presented here clone and run Examples.

You can set the login redirection by setting the LOGIN_REDIRECT_URL in settings.py.

You can also set the custom login redirection by:

  1. Adding success_url to the extra_context of CustomLoginView
  2. Setting this success_url variable as a value of the hidden input field with name="next" within the Login form html

Example 3: Django's forms.ModelForm (CRUD forms) in Bootstrap modal

For explanation how all the parts of the code work together see paragraph Usage. To test the working solution presented here clone and run Examples.

.html file containing modal, trigger elements and script instantiating modalForms

<!-- Modal 1 with id="create-book"-->
<div class="modal fade" id="create-modal" tabindex="-1" role="dialog" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
    </div>
  </div>
</div>

<!-- Modal 2 with id="modal" -->
<div class="modal fade" tabindex="-1" role="dialog" id="modal">
  <div class="modal-dialog" role="document">
    <div class="modal-content"></div>
  </div>
</div>

<!-- Create book button -->
<button id="create-book" class="btn btn-primary" type="button" name="button">Create book</button>

{% for book in books %}
    <div class="text-center">
      <!-- Read book buttons -->
      <button type="button" id="read-book" class="bs-modal btn btn-sm btn-primary" data-form-url="{% url 'read_book' book.pk %}">
        <span class="fa fa-eye"></span>
      </button>
      <!-- Update book buttons -->
      <button type="button" id="update-book" class="bs-modal btn btn-sm btn-primary" data-form-url="{% url 'update_book' book.pk %}">
        <span class="fa fa-pencil"></span>
      </button>
      <!-- Delete book buttons -->
      <button type="button" id="delete-book" class="bs-modal btn btn-sm btn-danger" data-form-url="{% url 'delete_book' book.pk %}">
        <span class="fa fa-trash"></span>
      </button>
    </div>
{% endfor %}

<script type="text/javascript">
  $(function () {

    // Update, Read and Delete book buttons open modal with id="modal" (default)
    // The formURL is retrieved from the data of the element
    $(".bs-modal").each(function () {
      $(this).modalForm({
          formURL: $(this).data('form-url')
      });
    });

     // Create book button opens form in modal with id="create-modal"
    $("#create-book").modalForm({
        formURL: "{% url 'create_book' %}",
        modalID: "#create-modal"
    });

  });
</script>
  • See the difference between button triggering Create action and buttons triggering Read, Update and Delete actions.
  • Within the for loop in .html file the data-form-url attribute of each Update, Read and Delete button should be set to relevant URL with pk argument of the object to be updated, read or deleted.
  • These data-form-url URLs should than be set as formURLs for modalForms bound to the buttons.

Example 4: Django's forms.Form in Bootstrap modal

For explanation how all the parts of the code work together see paragraph Usage. To test the working solution presented here clone and run Examples.

Contribute

This is an Open Source project and any contribution is appreciated.

License

This project is licensed under the MIT License.