I have two select2 fields that are working as dynamic search engine inside a form. I need to clone this form so the user can add multiple entries and filtering inside these two fields that are working as search engine.
HTML:
<div id="formContainer">
<div class="row mt-3">
<div class="col col-md-4">
<div class="form-group">
<label>{% trans "Aircraft" %}</label>
<select class="form-control" name="aircraft" id="aircraft">
<option value="" selected="" disabled="">{% trans "Select Aircraft" %}</option>
{% for aircraft in aircrafts %}
<option value="{{aircraft.id}}" {% if aircraft.id == log_entry.aircraft.id %}selected{% endif %}>{{aircraft.registration}}</option>
{% endfor %}
</select>
</div>
</div>
<div class="col col-md-4">
<div class="form-group">
<label>{% trans "Departure Aerodrome" %}</label>
<select class="form-control adep" name="adep" id="adep">
{% if not base_airport %}
<option disable="">{% trans "Choose one Airport" %}</option>
{% else %}
<option value="{{base_airport.icao_code}}">{{base_airport.icao_code}} - {{base_airport.name}} </option>
{% endif %}
</select>
</div>
</div>
<div class="col col-md-4">
<div class="form-group">
<label>{% trans "Arrival Aerodrome" %}</label>
<select class="form-control ades" name="ades" id="ades">
{% if not base_airport %}
<option disable="">{% trans "Choose one Airport" %}</option>
{% else %}
<option value="{{base_airport.icao_code}}">{{base_airport.icao_code}} - {{base_airport.name}} </option>
{% endif %}
</select>
</div>
</div>
</div>
<div>
</div>
</div>
</div>
<div id="newForm"></div>
<div class="row mt-3">
<div class="col">
<button class="btn btn-primary" id="addFlightBtn">{% trans "Add New Flight" %}</button>
</div>
</div>
My form is inside a forloop, so there can be 1 item as many other items. So I initialize the select fields with this function:
$(document).ready(function () {
searchAirports(".adep", "adep-");
searchAirports(".ades", "ades-");
});
function searchAirports(className, idName) {
$(className).each(function (index) {
var id = idName + index;
$(this).attr("id", id);
$("#" + id).select2({
ajax: {
url: "/flight/search_airports",
dataType: "json",
data: function (params) {
return {
search_value: params.term, // search term
};
},
processResults: function (data) {
return {
results: $.map(data, function (obj) {
return {
text: obj.code + " " + obj.name,
id: obj.code,
};
}),
};
},
cache: true,
},
minimumInputLength: 3,
placeholder: gettext("Search for a airport"),
theme: "classic",
tags: true,
templateSelection: function (obj) {
return obj.id;
},
allowClear: true,
});
});
}
Everything works fine. But, if I click on the Add New Flight button, a function is triggered and, basically it clones the form into a new one. The function is this one:
addFlightBtn.addEventListener("click", (e) => {
e.preventDefault();
// creiamo un nuovo form
const newForm = createFlightForm();
// inseriamo il nuovo form nel div "newForm"
const newFormContainer = document.querySelector("#newForm");
newFormContainer.appendChild(newForm);
});
// funzione per creare un nuovo form
function createFlightForm() {
const formContainer = document.querySelector("#formContainer");
// cloniamo il contenuto del div "formContainer"
const newForm = formContainer.cloneNode(true);
// creiamo un bottone "Remove"
const removeBtn = document.createElement("button");
removeBtn.classList.add("btn", "btn-danger", "mt-3");
removeBtn.textContent = "Remove";
removeBtn.addEventListener("click", () => {
// rimuoviamo il form corrispondente quando viene cliccato il bottone "Remove"
newForm.remove();
});
// inseriamo il bottone "Remove" nel form appena creato
newForm.appendChild(removeBtn);
return newForm;
} What I do know, at the moment, is that I should use the destroy functionality to destroy the select2 element and create a new one, but basically everything I tried so far didn't work. Is there anybody who had the same situation before and can help me out? The thing that is getting me hard life, in fact, is to understand how to clone the new select elements, give them another unique id and make them work.
Some issues to take care of:
id
attributes should be unique in valid HTML, so in the part that you want to clone there should be no id
attributes: they are not useful anyway as you cannot use them to uniquely identify an element in the document. You already have class attributes, so that should be enough to work with. For the same reason don't add an id
attribute dynamically for the select2
elements in your code.
Don't call select2
to the elements you will want to clone later. Instead, reserve a template that you will use to create those clones. For such templates, HTML has the template
element, and you could even use it to dynamically create the very first "form container".
You have a mix of jQuery and native DOM methods. If you want to use jQuery then use it to the full.
Here is a demo (HTML a bit simplified to focus on the changes)
$(document).ready(function () {
createFlightForm(false); // Reuse code for adding flight form
});
function searchAirports(newForm, className, idName) {
// newForm is now an argument to the function and is used to limit the scope of the loop:
$(className, newForm).each(function (index) {
// Don't set an id: id should be unique anyhow, and it is not useful here.
$(this).select2({
ajax: {
url: "/flight/search_airports",
dataType: "json",
data: function (params) {
return {
search_value: params.term, // search term
};
},
processResults: function (data) {
return {
results: $.map(data, function (obj) {
return {
text: obj.code + " " + obj.name,
id: obj.code,
};
}),
};
},
cache: true,
},
minimumInputLength: 3,
placeholder: "Search for a airport",
theme: "classic",
tags: true,
templateSelection: function (obj) {
return obj.id;
},
allowClear: true,
});
});
}
$("#addFlightBtn").click((e) => {
e.preventDefault();
createFlightForm(true);
});
// funzione per creare un nuovo form
function createFlightForm(needsRemoveButton) {
const formContainer = $("template").html(); // To clone, we just grab the HTML
// cloniamo il contenuto del div "formContainer"
const newForm = $(formContainer); // ...and create a new element with it.
if (needsRemoveButton) { // <--- new function argument
// creiamo un bottone "Remove"
const removeBtn = $("<button>").addClass("btn btn-danger mt-3").text("Remove").click(() => {
// rimuoviamo il form corrispondente quando viene cliccato il bottone "Remove"
newForm.remove();
});
// inseriamo il bottone "Remove" nel form appena creato
newForm.append(removeBtn);
}
// inseriamo il nuovo form nel div "newForm"
$("#newForm").append(newForm);
searchAirports(newForm, ".adep", "adep-");
searchAirports(newForm, ".ades", "ades-");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js"></script>
<template>
<div class="formContainer">
<hr>
<div class="form-group">
<label>Departure Aerodrome</label>
<select class="form-control adep" name="adep">
<option value="AMS">AMS - Amsterdam</option>
<option value="ROM">ROM - Rome</option>
</select>
</div>
<div class="form-group">
<label>Arrival Aerodrome</label>
<select class="form-control ades" name="ades">
<option value="AMS">AMS - Amsterdam</option>
<option value="ROM">ROM - Rome</option>
</select>
</div>
</div>
</template>
<div id="newForm">
</div>
<div class="col">
<button class="btn btn-primary" id="addFlightBtn">Add New Flight</button>
</div>