Search code examples
javascriptjquerydjangodatepickerdjango-widget

Using jQuery datepicker with multiple django forms in one page


So I have a create announcement form and then an edit form for each announcement and each form has its own datepicker. My function for controlling the datepicker is:

$(function() {
    $( ".datepicker" ).datepicker({
        changeMonth: true,
        changeYear: true,
    });
});

Initially my issue was that using the datepicker in one of the edit forms would change the field in the create form since the ids were the same. I got around this by adding ids (in my forms.py) like so:

end_date = forms.DateField(label='Expires', widget=forms.TextInput(attrs={'class':'datepicker form-control', 'id':'end_date_create'}))

and

end_date = forms.DateField(label='Expires', widget=forms.TextInput(attrs={'class':'datepicker form-control', 'id':'end_date_edit'}))

to my createAnnouncement and EditAnnouncement forms. But since I have multiple edit forms per page, I still have the same issue where using the datepicker on any edit form changes the field of only the top edit form. I am using django widget tweaks to render my forms so it automatically generates HTML and ids and classes and everything. Each edit form is in a div with a unique id, but the form fields themselves are named the same thing across all the forms. Does anyone know how I could generate unique ids for my form fields with django widget tweaks? Or maybe there is some javascript or something I could add to my datepicker function that tells the datepicker to change the value of the field that is in the same div?

EDIT: My template looks like this:

{% if boardAnnouncements %}
<h3>Announcements</h3>
    <div class="container" style="margin: 0px; padding: 0px;">
        <ul>
            {% for announcement in boardAnnouncements %}
                <div class="row" style="padding-bottom: 10px;">
                <li>
                    <div class="col-md-6">
                        <!-- display announcement content -->
                    </div>
                    <div class="col-md-6">
                        <!-- edit button calls javascript function to hide/unhide div with edit form in it -->
                        <a href="javascript:unhide_announcement('editann-{{announcement.id}}', '{{announcement.id}}')" class="btn btn-primary" style="margin-left: 10px;"><i class="fa fa-pencil" aria-hidden="true"></i> Edit</a>
                    </div>
                </div>
                <!-- each div gets unique id that corresponds to announcemt id -->
                <div id="editann-{{announcement.id}}" class="hidden">
                    <form role="form" action="/editannouncement/{{announcement.id}}/" method="post">
                            <!-- display edit form with django widget tweaks -->
                            {% csrf_token %}
                            {% for field in editAnnouncement %}
                                {% if field.errors %}
                                    <div class="form-group has-error">
                                        <label class="col-sm-2 control-label" for="id_{{ field.name }}">
                                        {{ field.label }}</label>
                                        <div class="col-sm-10">
                                            {{ field }}
                                            <span class="help-block">
                                            {% for error in  field.errors %}
                                                {{ error }}
                                            {% endfor %}
                                            </span>
                                        </div>
                                    </div>
                                {% else %}
                                    <div class="form-group">
                                        <label class="col-sm-2 control-label" for="id_{{ field.name }}">{{ field.label }}</label>
                                        <div class="col-sm-10">
                                            {{ field }}
                                            {% if field.help_text %}
                                                <p class="help-block"><small>{{ field.help_text }}</small></p>
                                            {% endif %}
                                        </div>
                                    </div>
                                {% endif %}
                            {% endfor %}
                                <div class="form-group">
                                    <div class="col-sm-offset-2 col-sm-10">
                                        <button type="submit" name="edit_announcement" class="btn btn-primary">Save</button><br><br>
                                    </div>
                                </div>
                            </form>
                </li>
                </div>
            {% endfor %} 
        </ul>
    </div>  
    {% endif %}
<div>

And the generated HTML for the edit forms is:

<div id="editann-1" class="unhidden">
    <form class="ng-pristine ng-valid" role="form" action="/editannouncement/1/" method="post">
        <input name="csrfmiddlewaretoken" value="AbTEZYmK1RF9yeom1C34IFFCj3EBrOD3" type="hidden">
        <div class="form-group">
            <label class="col-sm-2 control-label" for="id_description">Edit Description</label>
            <div class="col-sm-10">
                <textarea class="form-control" cols="40" id="id_description" name="description" rows="10"></textarea>
            </div>
        </div>
        <div class="form-group">
            <label class="col-sm-2 control-label" for="id_end_date">Expires</label>
            <div class="col-sm-10">
                <input class="datepicker form-control hasDatepicker" id="end_date_edit" name="end_date" type="text">
            </div>
        </div>
        <div class="form-group">
            <div class="col-sm-offset-2 col-sm-10">
                <button type="submit" name="edit_announcement" class="btn btn-primary">Save</button><br><br>
            </div>
        </div>
    </form>
</div>
<div id="editann-2" class="unhidden">
    <form class="ng-pristine ng-valid" role="form" action="/editannouncement/2/" method="post">
        <input name="csrfmiddlewaretoken" value="AbTEZYmK1RF9yeom1C34IFFCj3EBrOD3" type="hidden">
        <div class="form-group">
            <label class="col-sm-2 control-label" for="id_description">Edit Description</label>
            <div class="col-sm-10">
                <textarea class="form-control" cols="40" id="id_description" name="description" rows="10"></textarea>
            </div>
        </div>
        <div class="form-group">
            <label class="col-sm-2 control-label" for="id_end_date">Expires</label>
            <div class="col-sm-10">
                <input class="datepicker form-control hasDatepicker" id="end_date_edit" name="end_date" type="text">
            </div>
        </div>
        <div class="form-group">
            <div class="col-sm-offset-2 col-sm-10">
                <button type="submit" name="edit_announcement" class="btn btn-primary">Save</button><br><br>
            </div>
        </div>
    </form>
</div>

So to clarify each editann-# div has an edit form in it (and each edit form has a datepicker). Right now since the all the edit form "Expire" fields have id id_end_date the datepicker changes the value of the first edit form, no matter which datepicker I am using.


Solution

  • I came up with a solution involving some javascript and jQuery.

    function unhide_announcement(divID, ID) {
    var item = document.getElementById(divID);
    if (item) {
        /* hide/unhide div */
        item.className=(item.className=='hidden')?'unhidden':'hidden';
        /* give each end_date field a unique ID */
        var newID = 'id_end_date' + ID;
        item.querySelector('#id_end_date').id = newID;
        /* datepicker functionality */
        $(function() {                                                    
            $( '#'+newID ).datepicker({
              changeMonth: true,
              changeYear: true,
            });
          })
        }
    }
    

    Since each edit announcement form is in a unique div, I get that div first and then within the div there is only 1 edit announcement form so I change that id from id_end_date to id_end_date1 or whichever announcement id it is associated with. And then I changed the jQuery datepicker function I had to select by id instead of by class and it worked. So when I call it in my template it looks like this:

    <a href="javascript:unhide_announcement('editann-{{announcement.id}}', '{{announcement.id}}')" class="btn btn-primary">Edit</a>
    <div id="editann-{{announcement.id}}" class="hidden">
        <!-- edit announcement form -->
        <form>
            ...
        </form>
    </div>