autocomplete: switch autocomplete.js

Turns out implementing a autocomplete widget from scratch is more
tricky than expected. It's a shame no such widget has been implemented
part of the HTML standart :(
This commit is contained in:
Félix Baylac-Jacqué 2022-02-13 12:18:24 +01:00
parent 68434bf446
commit d0715b0d15
2 changed files with 75 additions and 121 deletions

View file

@ -15,6 +15,8 @@
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/autoComplete.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/css/autoComplete.02.min.css">
<title>Aquilenet: Éligibilité FTTH</title>
<style>
@ -63,14 +65,10 @@
<div id="methodAddress" class="collapse testMethods">
<h2 class="text-center form-title">Test d'éligibilité par adresse</h2>
<form id="formAddressTest" method="post" action="/test/address">
<div class="form-group" id="communeForm">
<label class="form-label" for="communeInput">Commune</label>
<input autocomplete="off" type="text" list="communes" name="commune" class="form-control"
id="communeInput" ondblclick="this.focus();this.select()" aria-describedby="communeHelp"
placeholder="Nom de la commune ou code postal">
<datalist id="communes">
</datalist>
</div>
<div class="form-group row" id="communeForm">
<label class="form-label col-sm-2 my-1" for="commune-autocomplete">Commune</label>
<input id="commune-autocomplete" class="col-sm-9" type="search" dir="ltr" spellcheck=false autocorrect="off" autocomplete="off" class="form-control" autocapitalize="off"/>
</div>
<div class="form-row collapse" id="voieForm">
<div class="col-sm-3 my-1">
<label class="form-label" for="numeroVoieInput">Numéro de voie</label>
@ -79,12 +77,9 @@
oninvalid="this.setCustomValidity('Veuillez renseigner le numéro de voie')"
oninput="setCustomValidity('')">
</div>
<div class="col-sm-9 my-1">
<label class="form-label" for="voieInput">Nom de voie</label>
<input autocomplete="off" type="text" name="voie" list="voies" class="form-control" id="voieInput"
aria-describedby="voieHelp" placeholder="Nom de voie">
<datalist id="voies">
</datalist>
<div class="col-sm-7 my-1">
<label class="form-label" for="fantoir-autocomplete">Nom de voie</label>
<input id="fantoir-autocomplete" class="form-control" type="search" dir="ltr" spellcheck=false autocorrect="off" autocomplete="off" autocapitalize="off"/>
</div>
</div>
<br>
@ -131,94 +126,6 @@
return inputStr;
}
// Function to update list of communes (calls backend API)
function updateCommunes(search = '') {
var api = "addresses/communes?limit=15";
if (search != '') {
api += "&s=" + search;
}
fetch(api, { signalCommunes })
.then(response => response.json())
.then(data => {
valueMatch=false;
if (JSON.stringify(data) !== JSON.stringify(communes)) {
$("#communes").empty();
communes = data
communes.forEach(commune => {
value=commune.codeZip + ' ' + commune.nom
$("#communes").append("<option codeInsee=" + commune.codeInsee + " value='" +
value + "'></option>");
if (value === search) {
valueMatch=true;
}
});
}
if (communes.length == 1 || valueMatch) {
codeInsee = communes[0].codeInsee;
$('#voieForm').collapse('show');
} else {
$('#voieInput').val('');
$('#voieForm').collapse('hide');
}
})
.catch(err => {
console.error("Error fetching communes:", err)
})
}
// Function to update list of voies (calls backend API)
function updateVoies(search = '') {
var api = "addresses/fantoirvoies/" + codeInsee + '?limit=15';
if (search != '') {
api += "&s=" + search;
}
fetch(api, { signalVoies })
.then(response => response.json())
.then(data => {
valueMatch=false;
if (JSON.stringify(data) !== JSON.stringify(voies)) {
$("#voies").empty();
voies = Object.entries(data);
let voie;
for (i in voies) {
voie = voies[i]
$("#voies").append("<option value='" +
voie[0] + "'></option>");
if (voie[0] === search) {
valueMatch=true;
}
}
if (voies.length == 1 || valueMatch) {
$('#btnTestAdresse').collapse('show');
voie = voies[0]
codeRivoli = voie[1]
$("#voieInput").val(voie[0]);
$('#btnTestAdresse').focus();
} else {
$('#btnTestAdresse').collapse('hide');
}
}
})
.catch(err => {
console.error("Error fetching communes:", err)
})
}
function fetchWithBackoff (fetchFn, timeout, input) {
if(timeout !== null) {
clearTimeout(timeout);
}
if (input.length < 3) {
return null;
}
timeout = setTimeout(() => {
const cleanInput = sanitizeInputStr(input);
fetchFn(cleanInput);
}, 100);
return timeout;
}
$('#methodAddress').on('show.bs.collapse', function () {
$('#communeInput').trigger('input')
$('#communeInput').trigger('keyup')
@ -234,27 +141,70 @@
$('#numeroVoieInput').focus();
});
let timeoutCommune = null;
$('#communeInput').on('input', function () {
controllerCommunes.abort();
timeoutCommune = fetchWithBackoff(updateCommunes, timeoutCommune, $(this).val());
const autoCompleteCommune = new autoComplete({
selector: "#commune-autocomplete",
placeHolder: "Code postal/nom de commune...",
data: {
src: async (query) => {
const api = "addresses/communes?limit=100";
const reqUrl = query === '' ? api : api + "&s=" + query
const source = await fetch(reqUrl, { signalCommunes });
const data = await source.json();
return data;
},
keys: [ "codeZip", "nom" ],
},
resultList: {
element: (list, data) => {
}
},
resultItem: {
highlight: true
},
debounce: 300,
events: {
input: {
selection: (event) => {
const selection = event.detail.selection.value;
autoCompleteCommune.input.value = selection.codeZip + " - " + selection.nom ;
codeInsee = selection.codeInsee;
$("#voieForm").collapse("show");
}
}
}
});
$('#communeInput').on('input', function () {
if ($(this).val() === '') {
$('#voieForm').collapse('hide');
}
});
let timeoutVoie = null;
$('#voieInput').on('input', function () {
controllerVoies.abort();
timeoutVoie = fetchWithBackoff(updateVoies, timeoutVoie, $(this).val());
});
$('#voieInput').on('input', function () {
if ($(this).val() === '') {
$('#btnTestAdresse').collapse('hide');
}
const autoCompleteFantoir = new autoComplete({
selector: "#fantoir-autocomplete",
placeHolder: "Nom de la voie...",
data: {
src: async (query) => {
const api = "addresses/fantoirvoies/" + codeInsee + "?limit=100";
const reqUrl = query === '' ? api : api + "&s=" + query
const source = await fetch(reqUrl, { signalVoies });
const data = await source.json();
return Object.entries(data).map(e => {return {"name": e[0], "value": e[1]}; });
},
keys: [ "name" ],
},
resultList: {
element: (list, data) => {
}
},
resultItem: {
highlight: true
},
debounce: 300,
events: {
input: {
selection: (event) => {
const selection = event.detail.selection.value;
autoCompleteFantoir.input.value = selection.name;
codeRivoli = selection.value;
$('#btnTestAdresse').collapse('show');
}
}
}
});
$('#formAddressTest').submit(function(eventObj) {

View file

@ -72,6 +72,10 @@ body {
text-shadow: dimgray 0 1px 1px;
background-image: linear-gradient(to bottom, #ffd38c 0%, #ffedd0 51%, #e0e0e0 100%);
}
/* Autocomplete */
/* #fda085 51%, */
/*