Search code examples
javascriptjquerydjangoajaxmulti.js

AJAX request is not refreshing <select> when <select> is modified by multi.js library


I have a Django form with a multi-select on it. I am using the multi.js library to modify the look & feel of the multi-select. I am also using the Django Bootstrap Modal Forms package to be able to add new options to my select list without leaving/refreshing the page.

Currently, when I add a new item via the modal form, it is added in the back-end, but the multi-select does not get updated from the JsonResponse.

If I remove the code that applies the multi.js functionality to the multi-select, then it is refreshed properly when the modal window closes.

As per this issue in GitHub, I thought I might just have to trigger the change function for the select element, but that doesn't seem to work for me. I've tried adding $select.trigger( 'change' ); to the last line of the createQuestionModalForm function, but even though that is hit in the debugger after submitting the modal, it doesn't seem to do anything.

Here's the relevant JS:

$('#id_questions').multi();
$(function () {
    function createQuestionModalForm() {
        $("#addQuestion").modalForm({
            formURL: "{% url 'question_create' %}",
            asyncUpdate: true,
            asyncSettings: {
                closeOnSubmit: true,
                successMessage: "test",
                dataUrl: "/projects/question_update",
                dataElementId: "#id_questions",
                dataKey: "question_select",
                addModalFormFunction: createQuestionModalForm
            }
        });
    }
    createQuestionModalForm();
});

Are there any other tricks I can be using to force the multi.js plugin to properly display the updated node when the AJAX update is complete?


Solution

  • If you want to add new Entries to the multi form you basically have to add new options to the select and dispatch a new change Event. I have edited the demo and made a working proof of concept.

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title>Demo – multi.js</title>
            <base href="https://fabianlindfors.se/multijs/" target="_blank">
            <meta name="viewport" content="width=device-width, initial-scale=1">
    
            <!-- Include multi.js -->
            <link rel="stylesheet" type="text/css" href="multi.min.css">
            <script src="multi.min.js"></script>
    
            <style>
                /* Basic styling */
                body {
                    font-family: sans-serif;
                }
    
                .container {
                    box-sizing: border-box;;
                    margin: 0 auto;
                    max-width: 500px;
                    padding: 0 20px;
                    width: 100%;
                }
            </style>
        </head>
        <body>
            <div class="container">
                <h1>multi.js</h1>
                <button class="add_test">Add Test-Element</button>
                <p>Demo of select box replacement multi.js. Available on <a href="https://github.com/Fabianlindfors/multi.js">Github</a>.</p>
                <select multiple="multiple" name="favorite_fruits" id="fruit_select">
                    <option>Apple</option>
                    <option>Banana</option>
                    <option>Blueberry</option>
                    <option>Cherry</option>
                    <option>Coconut</option>
                    <option>Grapefruit</option>
                    <option>Kiwi</option>
                    <option>Lemon</option>
                    <option>Lime</option>
                    <option>Mango</option>
                    <option>Orange</option>
                    <option>Papaya</option>
                </select>
            </div>
            
            <script>
                var select = document.getElementById( 'fruit_select' );
                multi( select, {
                    search_placeholder: 'Search fruits...',
                });
            </script>
            <script>
                var counter = 1;
                
                
                var button = document.getElementsByTagName("button");
                button[0].addEventListener('click',function(e){
                    var select = document.getElementById( 'fruit_select' );
                    
                    // Adding new Stuff 
                    var newOption = document.createElement('option');
                    newOption.innerHTML = 'test'+ counter;
                    select.appendChild(newOption);
                    counter++;
                    
                    // Update Multi
                    select.dispatchEvent(new Event('change'));
                });
            </script>
        </body>
    </html>

    EDIT: you also might want to try dispatch the Change-Event in the addModalFormFunction. I've checked the Github-Repo and it seems this is the only place where you can trigger the select after it has been updated.

    $('#id_questions').multi();
    $(function () {
        function createQuestionModalForm() {
            $('#id_questions').trigger('change');
            $("#addQuestion").modalForm({
                formURL: "{% url 'question_create' %}",
                asyncUpdate: true,
                asyncSettings: {
                    closeOnSubmit: true,
                    successMessage: "test",
                    dataUrl: "/projects/question_update",
                    dataElementId: "#id_questions",
                    dataKey: "question_select",
                    addModalFormFunction: createQuestionModalForm
                }
            });
        }
        createQuestionModalForm();
    });