tetra-framework / tetra

Tetra - A full stack component framework for Django using Alpine.js
https://www.tetraframework.com
MIT License
569 stars 19 forks source link

Waqas/enhancement/form support #69

Closed waqasidrees07 closed 3 months ago

waqasidrees07 commented 3 months ago

merge my file handling code into form_support branch

nerdoc commented 3 months ago

please revert 6d31cae as it completely breaks my project layout. The idea directory should never be included here...

nerdoc commented 3 months ago

I think the best way to get file upload done correctly is the Js part first. look at tetra.js and tetra.core.js - there are the callServerMethod() (in both files, I don't know why).

I let ChatGPT reformat this method so that it looks like this (in e.g. tetra.core.js) because I am not good at Js.

    async callServerMethod(component, methodName, methodEndpoint, args, files = []) {
      const body = Tetra.getStateWithChildren(component);
      body.args = args;

      // Use FormData to handle file uploads
      const formData = new FormData();

      // Add JSON data to FormData
      formData.append('data', new Blob([Tetra.jsonEncode(body)], { type: 'application/json' }));

      // Add files to FormData
      files.forEach((file, index) => {
        formData.append(`file${index}`, file);
      });

      const response = await fetch(methodEndpoint, {
        method: "POST",
        headers: {
          "X-CSRFToken": window.__tetra_csrfToken
        },
        mode: "same-origin",
        body: formData
      });
      if (response.status === 200) {
        const respData = Tetra.jsonDecode(await response.text());
        if (respData.success) {
          let loadingResources = [];
          respData.js.forEach((src) => {
            if (!document.querySelector(`script[src="${CSS.escape(src)}"]`)) {
              loadingResources.push(Tetra.loadScript(src));
            }
          });
          respData.styles.forEach((src) => {
            if (!document.querySelector(`link[href="${CSS.escape(src)}"]`)) {
              loadingResources.push(Tetra.loadStyles(src));
            }
          });
          await Promise.all(loadingResources);
          if (respData.callbacks) {
            respData.callbacks.forEach((item) => {
              let obj = component;
              item.callback.forEach((name, i) => {
                if (i === item.callback.length - 1) {
                  obj[name](...item.args);
                } else {
                  obj = obj[name];
                  console.log(name, obj);
                }
              });
            });
          }
          return respData.result;
        } else {
          throw new Error("Error processing public method");
        }
      } else {
        throw new Error(`Server responded with an error ${response.status} (${response.statusText})`);
      }
    },

I think the FormData part is the way to go.