Symfony中Fetch API多输入Ajax广告筛选失效问题求助
Symfony多选下拉筛选Ajax失效问题修复
问题描述
自学Symfony时,用Fetch API实现广告筛选的Ajax请求,复选框筛选正常,但多选下拉列表筛选无效。点击下拉列表时请求次数增加,但URL中无法获取到该列表的参数,请求参数缺失是问题核心。
现有代码
Script.js
window.onload = () => { const FiltersForm = document.querySelector("#filters, div.select"); document.querySelectorAll("#filters input, div.select").forEach(input =>{ input.addEventListener("change", () =>{ //On récupère les données du formulaire const Form = new FormData(FiltersForm); //On fabrique la queryString const Params = new URLSearchParams(); Form.forEach((value, key) =>{ Params.append(key,value); console.log(Params.toString()); }); //On récupère l'URL active const Url = new URL(window.location.href); //On lance la requête AJAX fetch(Url.pathname + "?" + Params.toString() + "&ajax=1",{ headers:{ "X-Requested-with": "XMLHttpRequest" } }).then(response => response.json() ).then(data => { //On va chercher le contenu const content = document.querySelector("#content"); //On met à jour le contenu content.innerHTML = data.content; }) .catch(e => alert(e)); }) }); }
AnnonceController.php
#[Route('/annonce/all_annonces', name: 'app_annonce_all_annonces', methods: ['GET'])] public function all_annonces(AnnonceRepository $annonceRepository, TypeAnnonceRepository $typeAnnonceRepository, DepartementRepository $departementRepository,RobeRepository $robeRepository, RaceRepository $raceRepository, Request $request): Response { //On récupère des filtres $filters = $request->get("listTypeAnnonces"); $departements = $request->get("listDepartements"); $races = $request->get("listRaces"); $robes = $request->get("listRobes"); $listAnnonces = $annonceRepository->getFiltersAnnonces($filters, $departements, $races, $robes); $listTypeAnnonces = $typeAnnonceRepository->findAll(); $listDepartements = $departementRepository->findAll(); $listRobes = $robeRepository->findAll(); $listRaces = $raceRepository->findAll(); //On vérifie si y a une requête Ajax if($request->get('ajax')){ return new JsonResponse([ 'content' => $this->renderView('annonce/_content.html.twig', [ 'listAnnonces' => $listAnnonces]) ]); } return $this->render('annonce/all_annonces.html.twig', [ 'listAnnonces' => $listAnnonces, 'listTypeAnnonces' => $listTypeAnnonces, 'listDepartements' => $listDepartements, 'listRobes' => $listRobes, 'listRaces' => $listRaces ]); }
all_annonces.html.twig
{% block body %} <div class="container is-widescreen"> <br> <h2 style="text-decoration: underline;">Toutes les annonces disponibles</h2> <br> <form id="filters"> {% for typeAnnonce in listTypeAnnonces %} <p><input type="checkbox" name="listTypeAnnonces[]" id="typeAnnonce{{ typeAnnonce.idtypeannonce }}" value="{{ typeAnnonce.idtypeannonce }}"> <label for="typeAnnonce{{ typeAnnonce.idtypeannonce }}">{{ typeAnnonce.libelle }}</label></p> {% endfor %} <div class="select is-multiple"> <select multiple size="5"> {% for departement in listDepartements %} <option name="listDepartements[]" id="departement{{ departement.iddepartement }}" value="{{ departement.iddepartement }}">{{ departement.libelle }}</option> {% endfor %} </select> </div> <div class="select is-multiple"> <select multiple size="5"> {% for robe in listRobes %} <option name="listRobes[]" id="robe{{ robe.id }}" value="{{ robe.id }}">{{ robe.libelle }}</option> {% endfor %} </select> </div> <div class="select is-multiple"> <select multiple size="5"> {% for race in listRaces %} <option name="listRaces[]" id="race{{ race.id }}" value="{{ race.id }}">{{ race.libelle }}</option> {% endfor %} </select> </div> </form> <br> <br> <div class="columns" id="content"> {% for annonce in listAnnonces %} <div class="column"> <div class="card"> <div class="card-image"> <figure class="image is-4by3"> <img src="{{ asset('imgAnnonce/first.JPG') }}" alt="Placeholder image"> </figure> </div> <div class="card-content"> <div class="media"> <div class="media-content"> <p class="title is-4">{{ annonce.titre }}</p> <p class="subtitle is-6">{{ annonce.prix }} €</p> </div> </div> <div class="content"> <a href="{{ path('app_annonce_show', {'idannonce': annonce.idannonce}) }}"><button type="button" class="button is-fullwidth is-warning is-light">En savoir plus</button></a> <br> <time datetime="2016-1-1">Annonce postée le {{ annonce.dateCreation|date("d/m/Y") }}</time> </div> </div> </div> </div> {% endfor %} </div> </div> {% endblock %}
修复步骤
1. 修正Twig模板中的select标签(核心问题)
FormData只会收集带有name属性的表单控件数据,你错误地将name加在了<option>标签上,而不是<select>标签上。需要修改每个多选下拉框:
<!-- 修正Departement下拉框 --> <div class="select is-multiple"> <select name="listDepartements[]" multiple size="5"> {% for departement in listDepartements %} <option id="departement{{ departement.iddepartement }}" value="{{ departement.iddepartement }}">{{ departement.libelle }}</option> {% endfor %} </select> </div> <!-- 修正Robe下拉框 --> <div class="select is-multiple"> <select name="listRobes[]" multiple size="5"> {% for robe in listRobes %} <option id="robe{{ robe.id }}" value="{{ robe.id }}">{{ robe.libelle }}</option> {% endfor %} </select> </div> <!-- 修正Race下拉框 --> <div class="select is-multiple"> <select name="listRaces[]" multiple size="5"> {% for race in listRaces %} <option id="race{{ race.id }}" value="{{ race.id }}">{{ race.libelle }}</option> {% endfor %} </select> </div>
2. 修正JavaScript中的事件监听和表单选择器
之前的代码错误地监听了div.select容器的change事件,导致点击容器时也会触发请求,同时表单选择器包含了无关元素,需要调整:
window.onload = () => { // 直接选择form元素,无需包含div.select const FiltersForm = document.querySelector("#filters"); // 只监听form内的input和select控件 document.querySelectorAll("#filters input, #filters select").forEach(input =>{ input.addEventListener("change", () =>{ // 直接用form实例化FormData const Form = new FormData(FiltersForm); const Params = new URLSearchParams(Form); // 直接用FormData初始化URLSearchParams,简化代码 console.log(Params.toString()); const Url = new URL(window.location.href); fetch(`${Url.pathname}?${Params.toString()}&ajax=1`,{ headers:{ "X-Requested-with": "XMLHttpRequest" } }).then(response => response.json()) .then(data => { const content = document.querySelector("#content"); content.innerHTML = data.content; }) .catch(e => alert(e)); }) }); }
3. 验证后端参数接收
后端代码无需修改,因为$request->get("listDepartements")等已经正确对应了修正后的name属性,只要前端能传递参数,后端就能正常接收。
原理说明
- 表单控件的
name属性是FormData收集数据的唯一标识,<option>的name不会被识别,必须将name加在<select>上,且多选框需要用[]后缀表示数组参数。 - 之前监听
div.select会导致重复触发change事件,因为容器的事件冒泡,只监听实际的表单控件可以避免这个问题。 - URLSearchParams可以直接用FormData初始化,简化代码逻辑。
内容的提问来源于stack exchange,提问作者Titistar
相关产品推荐
相关产品推荐

