You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 05:29:53