Afficher PJ dans un custom widget

Un custom widget minimal pour afficher une pièce jointe de type image dans une vue personnalisée.

Le code :

js :


document.addEventListener("DOMContentLoaded", function () {
    const contentEl = document.getElementById("content");
    const properUrlEl = document.getElementById("properUrl");
    const shortUrlEl = document.getElementById("shortUrl");

    let properUrl = "";
    let attachmentName = "";
    let id = 0;
    let token = "";
    let baseUrl = "";

    grist.ready({ requiredAccess: "full" });

    grist.onRecord((record) => {
        attachmentName = record.Nom_PJ || "piece_jointe";
        id = record.PJ?.[0] || 0;
        contentEl.textContent = attachmentName;
        updateUrls();
    });

    grist.docApi.getAccessToken({ readOnly: true }).then((response) => {
        token = response.token;
        baseUrl = response.baseUrl;
        updateUrls();
    });

    function updateUrls() {
        if (!baseUrl || !id) return;

        // Lien principal : avec nom du fichier dans l’URL
        properUrl = `${baseUrl}/attachments/${id}/${encodeURIComponent(attachmentName)}?auth=${token}`
            .replace("0.0.0.0", "localhost");

        // Afficher le lien complet
        properUrlEl.textContent = properUrl;
        properUrlEl.href = "#";

        // Afficher le lien court sous forme du nom du fichier cliquable
        shortUrlEl.textContent = attachmentName;
        shortUrlEl.href = "#";
        shortUrlEl.style.color = "#2d68f0";
        shortUrlEl.style.textDecoration = "underline";
        shortUrlEl.style.cursor = "pointer";
    }

    // Téléchargement manuel (sinon bloqués car iframe sandboxée)
    function triggerDownload(url, filename) {
        fetch(url)
            .then((res) => res.blob())
            .then((blob) => {
                const blobUrl = window.URL.createObjectURL(blob);
                const a = document.createElement("a");
                a.href = blobUrl;
                a.download = filename || "piece_jointe";
                document.body.appendChild(a);
                a.click();
                a.remove();
                window.URL.revokeObjectURL(blobUrl);
            })
            .catch((err) => console.error("Erreur de téléchargement :", err));
    }

    // Clic sur le lien complet
    properUrlEl.addEventListener("click", (e) => {
        e.preventDefault();
        if (properUrl) triggerDownload(properUrl, attachmentName);
    });

    // Clic sur le lien court (nom du fichier)
    shortUrlEl.addEventListener("click", (e) => {
        e.preventDefault();
        if (properUrl) triggerDownload(properUrl, attachmentName);
    });
});

html :


<!DOCTYPE html>
<html lang="">
   <head>
      <script src="https://docs.getgrist.com/grist-plugin-api.js"></script>
      <style>
         /* Reset and base styles */
         * {
         box-sizing: border-box;
         margin: 0;
         padding: 0;
         }
         body {
         font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
         font-size: 14px;
         line-height: 1.5;
         color: #24292f;
         background-color: #ffffff;
         padding: 16px;
         }
         div {
         margin: 30px;
         }
      </style>
      <title></title>
   </head>
   <body>
      <div class="group">
         <div>
            <strong>Nom de la PJ</strong> :
            <p id="content"></p>
         </div>
         <div>
            <strong>Lien complet</strong> :
            <a id="properUrl"></a>
         </div>
         <div>
         <strong>Lien court (nom de la PJ)</strong> :
         <a id="shortUrl"></a>
         </div>
      </div>
   </body>
</html>

Lien du document : Ex - afficher PJ / charger PJ / télécharger PJ - Grist