Kaynağa Gözat

Autocomplete et addCollection en vanilla

Sangfroid 8 ay önce
ebeveyn
işleme
c4701fd0f2

+ 3 - 2
assets/app.js

@@ -13,17 +13,18 @@ import 'bootstrap-star-rating/js/locales/fr'
 import 'bootstrap-star-rating/css/star-rating.css';
 import 'bootstrap-star-rating/themes/krajee-fa/theme';
 import 'bootstrap-star-rating/themes/krajee-fa/theme.css';
-import './js/addCollectionWidget';
+//import './js/addCollectionWidget';
 import './js/filtre';
 import './js/colorModeBootstrap'
 import { switchView } from './js/switchViewVanilla';
 import { switchEtat } from './js/switchEtat';
+import './js/addCollectionVanilla'
 
 const sv = switchView("#switchview");
 const se = switchEtat ('[data-fonction="switch"]');
 
 jQuery(function() {
-    $('.add-another-collection-widget').addCollection();
+    //$('.add-another-collection-widget').addCollection();
 
     //$('[data-fonction="switch"]').switchEtat();
 

+ 57 - 0
assets/js/addCollectionVanilla.js

@@ -0,0 +1,57 @@
+import { autocomplete } from './autocomplete'
+
+(() => {
+    document
+        .querySelectorAll('ul.collection-form li')
+        .forEach(item => {
+            addDeleteLink(item)
+        });
+
+    document
+    .querySelectorAll('.add_item_link')
+    .forEach(btn => {
+        btn.addEventListener("click", addFormToCollection)
+    });
+
+    function addFormToCollection(e) {
+        const collectionHolder = document.querySelector('#' + e.currentTarget.dataset.collectionHolderId);
+        const path = e.currentTarget.dataset.path ?? '';
+    
+        const item = document.createElement('li');
+        item.className = collectionHolder.dataset.classLi
+    
+        item.innerHTML = collectionHolder
+        .dataset
+        .prototype
+        .replace(
+            /__name__/g,
+            collectionHolder.dataset.index
+        );
+    
+        if (path !== '') {
+            autocomplete(item, path);
+        }
+
+        collectionHolder.appendChild(item);
+
+        addDeleteLink(item);
+    
+        collectionHolder.dataset.index++;
+    };
+
+    function addDeleteLink(item) {
+        const input = item.querySelector('input') ?? item.querySelector('select');
+        const deleteLink = document.createElement('button');
+        deleteLink.className = 'btn btn-outline-secondary';
+        const i = document.createElement('i');
+        i.className = 'fa fa-trash fa-lg text-danger';
+        deleteLink.appendChild(i);
+
+        deleteLink.addEventListener("click", (event) => {
+            item.remove();
+            event.preventDefault(); // évite qu'un # apparaisse dans l'URL
+        });
+
+        input.after(deleteLink);
+    };
+})();

+ 50 - 0
assets/js/autocomplete.js

@@ -0,0 +1,50 @@
+export function autocomplete (inputField, path) {
+    
+    const input = inputField.querySelector('input');
+    input.autocomplete = 'off';
+
+    const results = document.createElement('ul');
+    results.className="autocomplete-results";
+
+    input.addEventListener('input', (e) => {
+        const term = input.value.trim();
+
+        if (term.length === 0) {
+            // Ne pas envoyer de requête si le champ est vide
+            return;
+        }
+
+        // Envoyer une requête Fetch
+        fetch(path+ '?query=' + encodeURIComponent(term), {
+            headers: {
+                'X-Requested-With': 'XMLHttpRequest'
+            }
+        })
+        .then(response => {
+            if (!response.ok) {
+                throw new Error('Erreur lors de la récupération des suggestions');
+            }
+            return response.json();
+        })
+        .then(data => {
+            results.innerHTML = '';
+            data.forEach(element => {
+                const li = document.createElement('li');
+                li.innerText = element;
+                li.classList.add('autocomplete-item');
+                results.appendChild(li);
+                li.addEventListener('click', (e) => {
+                    input.value = element;
+                    results.remove()
+                })
+            })
+            input.after(results);
+            return true;
+        })
+        .catch(error => {
+            console.error(error);
+            return false;
+        });
+    });
+
+}

+ 21 - 1
assets/styles/app.scss

@@ -25,4 +25,24 @@ body {
     margin-bottom: 10px;
     border-top-left-radius: 0;
     border-top-right-radius: 0;
-}
+}
+
+.autocomplete-results {
+    position: absolute;
+    top: 100%;
+    left: 0;
+    right: 0;
+    width: 100%;
+    border-radius: 9px;
+    border-style: groove;
+    border-top: none;
+    list-style-type: none;
+    padding-left: 0;
+    cursor: pointer;
+    z-index: 99;
+    background-color: $dark;
+}
+
+.autocomplete-item:hover, .autocomplete-item:active {
+    background-color: $primary;
+}

+ 11 - 9
src/Controller/VideothequeController.php

@@ -188,28 +188,30 @@ class VideothequeController extends AbstractController
     }
 
     #[Route("/ajax_req_realisateurs", name: "videotheque_ajax_realisateurs")]
-    public function ajaxRealisateurs(RealisateurRepository $repo, SerializerInterface $serializer): Response
+    public function ajaxRealisateurs(Request $request, RealisateurRepository $repo): Response
     {
-        $realisateurs = $repo->findNomsComplets();
+        $query = $request->query->get('query');
+        $realisateurs = $repo->findRealisateurLike($query);
         $liste = array();
-        foreach ($realisateurs as $key=>$nom)
+        foreach ($realisateurs as $realisateur)
         {
-            $liste[$key] = $serializer->serialize($nom, 'json');
+            $liste[] = $realisateur->getNomComplet();
         }
 
         return new JsonResponse($liste);
     }
 
     #[Route("/ajax_req_genres", name: "videotheque_ajax_genres")]
-    public function ajaxGenres(GenreRepository $repo, SerializerInterface $serializer): Response
+    public function ajaxGenres(Request $request, GenreRepository $repo): Response
     {
-        $genres = $repo->findGenres();
+        $query = $request->query->get('query');
+        $genres = $repo->findGenreLike($query);
         $liste = array();
-        foreach ($genres as $key=>$nom)
+        foreach ($genres as $genre)
         {
-            $liste[$key] = $serializer->serialize($nom, 'json');
+            $liste[] = $genre->getName();
         }
-
+        
         return new JsonResponse($liste);
     }
 }

+ 11 - 0
src/Repository/GenreRepository.php

@@ -25,6 +25,7 @@ class GenreRepository extends ServiceEntityRepository
     {
         parent::__construct($registry, Genre::class);   
     }
+
     public function findGenres ()
     {
         $qb = $this->createQueryBuilder('g');
@@ -34,4 +35,14 @@ class GenreRepository extends ServiceEntityRepository
 
         return $query->getArrayResult();
     }
+
+    public function findGenreLike(?string $query = null): ?array
+    {
+        $qb = $this->createQueryBuilder('g');
+        $qb
+            ->where($qb->expr()->like('g.name', ':genreName'))
+            ->setParameter('genreName', '%'.$query.'%')
+        ;
+        return $qb->getQuery()->getResult();
+    }
 }

+ 10 - 0
src/Repository/RealisateurRepository.php

@@ -87,4 +87,14 @@ class RealisateurRepository extends ServiceEntityRepository
 
         return $query->getArrayResult();
     }
+
+    public function findRealisateurLike(?string $query = null): ?array
+    {
+        $qb = $this->createQueryBuilder('r');
+        $qb
+            ->where($qb->expr()->like('r.nomComplet', ':realName'))
+            ->setParameter('realName', '%'.$query.'%')
+        ;
+        return $qb->getQuery()->getResult();
+    }
 }

+ 22 - 19
templates/videotheque/form.html.twig

@@ -1,4 +1,5 @@
-    {{ form_start(form, {'attr': {'class': ''}}) }}
+    {% set classLi = "list-group-item mb-3" %}
+    {{ form_start(form, {'attr': {'class': ''} }) }}
     <div class="container-fluid">
         <div class="row">
             <div class="col-md">
@@ -24,13 +25,13 @@
                             {{ form_label(form.mediaVideos, 'Bandes annonces') }}
                         </div>
                         <div class="card-body">
-                            <button type="button" id="add_media-video" class="btn btn-link add-another-collection-widget mb-3" data-path="" data-list-selector="#media-video-fields-list">Ajouter une bande annonce</button>
-                            <ul id="media-video-fields-list"
-                                data-prototype="{{ form_widget(form.mediaVideos.vars.prototype)|e }}"
-                                data-widget-tags="{{ '<li class="list-group-item mb-3"></li>'|e }}"
-                                data-widget-counter="{{ form.children|length }}">
+                            <button type="button" id="add_media-video" class="btn btn-link add_item_link mb-3" data-collection-holder-id="media-video">Ajouter une bande annonce</button>
+                            <ul id="media-video" class="collection-form ps-0"
+                                data-index="{{ form.mediaVideos|length > 0 ? form.mediaVideos|last.vars.name + 1 : 0 }}"
+                                data-class-li="{{ classLi }}"
+                                data-prototype="{{ form_widget(form.mediaVideos.vars.prototype)|e('html_attr') }}">
                                 {% for mediaVideoField in form.mediaVideos %}
-                                <li class="list-group-item mb-3">
+                                <li class="{{ classLi }}">
                                     {{ form_widget(mediaVideoField) }}
                                     {{ form_errors(mediaVideoField) }}
                                 </li>
@@ -50,13 +51,14 @@
                         {{ form_label(form.genres, 'Genres') }}
                     </div>
                     <div class="card-body">
-                        <button type="button" id="add_genre" class="btn btn-link add-another-collection-widget mb-3" data-path="{{ path('videotheque_ajax_genres') }}" data-list-selector="#genre-fields-list">Ajouter un genre</button>
-                        <ul id="genre-fields-list"
-                            data-prototype="{{ form_widget(form.genres.vars.prototype)|e }}"
-                            data-widget-tags="{{ '<li class="list-group-item mb-3"></li>'|e }}"
-                            data-widget-counter="{{ form.children|length }}">
+                        <button type="button" id="add_genre" class="btn btn-link add_item_link mb-3" data-path="{{ path('videotheque_ajax_genres') }}" data-collection-holder-id="genre-fields-list">Ajouter un genre</button>
+                        <ul id="genre-fields-list" class="collection-form ps-0"
+                            data-index="{{ form.genres|length > 0 ? form.genres|last.vars.name + 1 : 0 }}"
+                            data-prototype="{{ form_widget(form.genres.vars.prototype)|e('html_attr') }}"
+                            data-class-li="{{ classLi }}"
+                        >
                             {% for genreField in form.genres %}
-                            <li class="list-group-item mb-3">
+                            <li class="{{ classLi }}">
                                 {{ form_widget(genreField) }}
                                 {{ form_errors(genreField) }}
                             </li>
@@ -74,13 +76,14 @@
                         {{ form_label(form.realisateurs, 'Réalisateurs') }}
                     </div>
                     <div class="card-body">
-                        <button type="button" id="add_realisateur" class="btn btn-link add-another-collection-widget mb-3" data-path="{{ path('videotheque_ajax_realisateurs') }}" data-list-selector="#realisateur-fields-list">Ajouter un réalisateur</button>
-                        <ul id="realisateur-fields-list"
-                            data-prototype="{{ form_widget(form.realisateurs.vars.prototype)|e }}"
-                            data-widget-tags="{{ '<li class="list-group-item mb-3"></li>'|e }}"
-                            data-widget-counter="{{ form.children|length }}">
+                        <button type="button" id="add_realisateur" class="btn btn-link add_item_link mb-3" data-path="{{ path('videotheque_ajax_realisateurs') }}" data-collection-holder-id="realisateur-fields-list">Ajouter un réalisateur</button>
+                        <ul id="realisateur-fields-list" class="collection-form ps-0"
+                            data-index="{{ form.realisateurs|length > 0 ? form.realisateurs|last.vars.name + 1 : 0 }}"
+                            data-prototype="{{ form_widget(form.realisateurs.vars.prototype)|e('html_attr') }}"
+                            data-class-li="{{ classLi }}"
+                        >
                             {% for realisateurField in form.realisateurs %}
-                            <li class="list-group-item mb-3">
+                            <li class="{{ classLi }}">
                                 {{ form_widget(realisateurField) }}
                                 {{ form_errors(realisateurField) }}
                             </li>