Bonjour à tous,

Dans le but d'allèger le nombre de requêtes et aussi de proposer leur lancement seulement au click sur un onglet j'ai fait ce code JS qui marche parfaitement excepté que le TWIG inclus n'est pas interprété en tant qu'html, il m'est renvoyé tel quel dans le contenu de la div "tab-content".

Par exemple au click sur l'onglet le path TWIG est affiché comme du texte : {{render(controller('App\Controller\AvoirsController::index',{id:documentId}))}} au lieu d'apeller le controller en question.

D'ailleurs, si remis dans le template d'origine tout fonctionne à merveille... Il y a donc une partie mal interprétée lors de l'envoi depuis JS...

J'ai essayé en parsant le contenu, rien n'y fait ...

Merci d'avance pour vos lumières

_tabs-ecritures.php :

{% block body %}
    <div class="tabs">
        <input type="radio" class="tabs__radio" name="tabs-ecriture" id="lignes" checked>
        <label for="lignes">Détails</label>

        <input type="radio" class="tabs__radio" name="tabs-ecriture" id="acomptes">
        <label for="lignes">Réglements</label>

        <input type="radio" class="tabs__radio" name="tabs-ecriture" id="avoirs">
        <label for="lignes">Avoirs</label>
    </div>

    <div class="tabs__content"></div>
    {% block javascripts %}
        <script src={{asset('assets/js/TabsEcritures.js')}} type="text/javascript" defer></script>
    {% endblock %}
{% endblock %}

tabsEcritures.js :

window.onload = () => {
  const tabContent = document.querySelector(".tabs__content");
  const radButtons = document.querySelectorAll(".tabs__radio");

  radButtons.forEach((rb) =>
    rb.addEventListener("change", (e) => {
      console.log(e.target.id);
      switch (e.target.id) {
        case "lignes":
          tabContent.innerHTML = `<div class="lignes-ecritures">{{render(controller('App\\Controller\\LignesEcrituresController::index',{id:documentId}))}}</div>`;
          break;
        case "acomptes":
          tabContent.innerHTML = `<div class="acomptes-ecriture">{{render(controller('App\\Controller\\AcomptesController::acomptesEcriture',{id:documentId}))}}</div>`;
          break;
        case "avoirs":
          tabContent.innerHTML = `<div class="avoirs-ecriture">{{render(controller('App\\Controller\\AvoirsController::index',{id:documentId}))}}</div>`;
          break;
        default:
          return;
      }
    })
  );
};

2 réponses


Hello,

En réalité, tu es dans un fichier javascript.
Il n'y a aucune interraction avec Twig à ce niveau là.

C'est comme si tu écrivais du PHP dans un fichier JS, il ne sera jamais interprété.

Pour moi tu as 2 solutions :

  • Charger ces infos dans ta vues de base, et simplement les afficher et/ou cacher en fonction du tab
  • Venir charger ces informations via une requêtes Javascript (fetch; XHR; Ajax) et venir les insérer dans ton HTML

Si tu cherches une question de performance, la 2nd solution serait la plus adaptée.

Bonjour Azorgh et merci pour ta réponse claire et précise...

Je me doutais bien que ça ne serait pas aussi facile ...

J'ai donc commencé à mettre en place une requête Ajax mais curieusement l'url que je renseigne s'additionne à l'url de la page courante alors que celle-ci ne devrait contenir que celle précisée dans le controller AcomptesController par exemple :

Lorsque je fais un fetch pour obtenir les acomptes, il éxécute la requête sur l'adresse : /admin/ecritures/modifier-ecriture/11798/acomptes-ecritures, or la fin de cette adresse ("acomptes-ecriture") s'est rajoutée à l'adresse principale de ma page pourtant les 2 contrôleurs sont bien séparés...

L'adresse réelle qui produit les bons résultats est bien celle du controller des acomptes c à d https://127.0.0.1/admin/acomptes/acomptes-ecriture/+ numéro de la facture, alors pourquoi me fait il une concaténation avec l'adresse de la page courante pourtant générée avec un autre controller ?

Merci d'avance pour ton aimable aiguillage... :)

Voici le controller AcomptesController.php :

    //Afficher les lignes d'acomptes pour cette écriture + le total d'acomptes en Ajax
    #[Route('acomptes-ecriture/{id}', name: 'acomptes-ecriture')]
    public function acomptesEcriture(Ecritures $ecritures, AcomptesRepository $acomptesRepository, $id): Response
    {
        $lignes = $acomptesRepository->findBy(['liaison' => $id], ['date_acompte' => 'desc']);

        $idEcriture = $ecritures->getId();

        $totalAcomptes = $ecritures->getTotalAcomptesEcriture();

        return new JsonResponse([
            'content' => $this->renderView('_partials/_acomptes-ecriture.html.twig', compact('lignes', 'totalAcomptes', 'idEcriture'))
        ]);
    }
Code JS :
```

window.onload = () => {
const tabContent = document.querySelector(".tabscontent");
const radButtons = document.querySelectorAll(".tabs
radio");

radButtons.forEach((rb) =>
rb.addEventListener("change", (e) => {
let url = "";
switch (e.target.id) {
case "lignes":
url = "lignes-ecriture";
break;

    case "acomptes":
      url = "acomptes-ecriture";
      break;

    case "avoirs":
      url = "avoirs-ecriture";
      break;
    default:
      return;
  }
  fetch(url, {
    method: "GET",
    headers: {
      "X-Requested-With": "XMLHttpRequest",
      "Content-Type": "Application/json",
    },
  })
    .then((response) => response.json())
    .then((data) => {
      tabContent.innerHTML = data.content;
    })
    .catch((error) => alert("Erreur : " + error));
})

);
};

Hello,

Petite attention sur le formattage de ton code qui est pas foufou sur ta réponse ! :)

Sinon, quand tu fais une requête comme ça, le mieux est préciser le chemin complet en commençant par un "/" afin de partir de la racine du site.

Toujours pareil ici, 2 solutions :

  • Ecrire en dur l'URL complète : /admin/acomptes/acomptes-ecriture/
  • Récupérer cette URL que ton Symfony renvoi pour être sûr qu'elle soit correcte (ou si elle change par exemple).

Tu pourrais par exemple définir ton rendu Twig de tes tabs ainsi :

{% block body %}
    <div class="tabs">
        <input type="radio" class="tabs__radio" name="tabs-ecriture" id="lignes" checked data-url="{{ path('lignes-ecriture', { 'documentId': documentId})) }}">
        <label for="lignes">Détails</label>

        <input type="radio" class="tabs__radio" name="tabs-ecriture" id="acomptes" data-url="{{ path('acomptes-ecriture', { 'documentId': documentId})) }>
        <label for="lignes">Réglements</label>

        <input type="radio" class="tabs__radio" name="tabs-ecriture" id="avoirs" data-url="{{ path('avoirs', { 'documentId': documentId})) }>
        <label for="lignes">Avoirs</label>
    </div>

    <div class="tabs__content"></div>
{% endblock %}

Ainsi dans ton Javascript, tu pourras récupérer le data-url qui sera toujours correct comme ceci :

window.onload = () => {
    const tabContent = document.querySelector(".tabscontent");
    const radButtons = document.querySelectorAll(".tabsradio");

    radButtons.forEach((rb) =>
        rb.addEventListener("change", (e) => {
            const url = e.target.dataset.url;
            fetch(url, {
                    method: "GET",
                    headers: {
                        "X-Requested-With": "XMLHttpRequest",
                        "Content-Type": "Application/json",
                    },
                })
                .then((response) => response.json())
                .then((data) => {
                    tabContent.innerHTML = data.content;
                })
                .catch((error) => alert("Erreur : " + error));
        })
    );
};

(Il faut juste vérifier la récupération de l'URL, je suis pas sûr de ce que j'ai fait de tête, voir ici : https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes)

Mille mercis pour cette réponse qui fonctionne parfaitement...

Je comprends mieux maintenant comment anticiper les retours Ajax et le passage de variables en JS mais il faudra que je m'exerce une fois seul ...

Concernant le formattage du message, je ne sais pas ce qui se passe, j'ai pourtant bien mis 3 backtits partout mais ça buguait à mort hier soir me disant que je n'étais pas autorisé à modifier le message alors que j'étais connecté, bref, je me suis déconnecté puis reconnecté et ça a remarché...

J'en profite pour faire part d'un plantage lorsque l'on va pour sélectionner une ou plusieurs étiquettes pour un message, sur firefox le control une fois rempli fait planter le navigateur...