Datagrid inside a component

Hi everyone,

I’m trying to wrap the Datagrid element into a Reusable Component to keep my project DRY.

The issue is that Datagrid parameters (specifically the column configurations) are very complex and nested. The standard Component Property editor feels too limited to handle these deeply indented arguments effectively.

Has anyone found a workaround for this? Is there a good architectural approach to avoid manual page-by-page updates?

Thanks for any tips!

Mmm interesting…. My first thought: if your goal is mainly to keep datagrid styles consistent across your project, you can probably get pretty far just by adding a shared class on your datagrid elements.

That said, once you start wanting to reuse more complex stuff (like column configs, formatting, logic, etc.), I can see how it gets messy fast inside a component.

Can you share one or two examples of what you’re trying to “reuse” across datagrids and what will change from one instance to the other?

Hi Joyce,

Thank you for the quick response. I appreciate your input.

To clarify, I’m developing a template software that I can resell and customize for my clients. The goal is to maximize code reusability to minimize setup time for new clients.

Think of it as something similar to Monday.com or Salesforce, where you have various objects (accounts, contacts, opportunities, etc.), each sharing a similar user interface featuring a grid for list views. This results in datagrids appearing on almost every page of the application, making it increasingly difficult to manage changes to the grid functionality.

While I’m already using classes for styling, it’s proving insufficient. I’m encountering a lot of redundant workflow, especially when dealing with component actions linked to a specific datagrids instance.

Currently, my best approach is to create a component that accepts column definitions as parameters + building a separate ‘datagrid builder’ component that allows for the creation of JSON configurations to be used as column definitions. While it’s not pretty I think in the long run this will save me some time.

However, I’m still curious to know if anyone has found a more efficient or sophisticated way to handle this challenge.

Weweb reusable components adds a lot of overhead and has a lot of memory leak problems. I went down that road and never managed to get down my memory usage to reasonable levels. It was often 1-2 gb in production app and I was not happy about that.

I recently switched to custom component developement using claude code, connected weweb with github and the developement is flawless. Now i have two different custom AG grid enterprise components that i reuse all over my app. Memory usage now stays at around 300-350 mb, no matter how many tables i open and close.

I would advice to at least try that, and see if it fits your use.

Hey Thomas,

I’m also using a custom-coded version of ag-Grid to add specific column types.

The main issue is that this approach doesn’t really help with factoring the logic that lives outside of the component such as workflows that update WeWeb variables. I’d also prefer not to embed too much logic directly within the component, as that would go against the purpose of WeWeb and the no/low-code.

Thanks you for the informations about memory, I’ll definitely take a closer look at that.

I use the thisinstance.id of the datagrid to save all my grid relevant data to a global variable in localstorage that is shared among all my datagrids. That way, when creating a new datagrid the only thing i change is the id, and the rest is dynamic. I store this json definition in db on the user as well, so we dont loose the users individual settings when the user log on from another pc.

i have a global workflow writing a json of column definitions on every change coming from the datagrid. This defines visibility on default, tablet, mobile separately, as well as column widths, sorting and filters. I have a global popup that only need the id of the triggering datagrid to know what settings to pull up and change.

I dont know if this fully answers your question, as it is a bit hard to understand completely what you are asking, but this might help at least.

Not 100% sure with custom coded components and using data grid but from my knowledge you may be able to emit events of some sort from the custom coded component that could trigger workflows depending on the trigger emitted.

Thanks for raising this. One approach that’s worked well for me is to map out the data flow first before building anything in WeWeb. Once you know exactly what data needs to go where, the component connections become much clearer.

Also worth checking if there’s a relevant WeWeb tutorial in the docs for your specific use case - the examples there saved me a lot of time.

Interesting!

I’m actually working on something quite similar. While this is usefull to get data out of the grid, the challenge now is sending commands to it.

For instance, I have a column configuration menu nested inside the grid component, and I want to toggle it via a global workflow.

Essentially, I’m looking for a way to use that instance Id to trigger a ‘toggle menu’ component action within the specific component instance.

I could use a variable parameter but this mean that it’s not retro active and that I have to re-configure all previously set grids, this is what I was trying to avoid with the ‘datagrid inside of a weweb component’

Any idea ?

Thanks for the help, Thomas !

Thank U Reaction GIF by The Office

I asked claude to make a code to search for a component by UUID, so I could find the correct component from a global context. Don’t know if it is the best way, but it works anyway. I wrapped it in a reusable formula.

Global formula:

function getWeWebComponent(componentUid) {

  const frontDoc = wwLib.getFrontDocument();

  const appRoot = frontDoc.getElementById('app');

  const vueApp = appRoot?.__vue_app__;

  const rootComponent = vueApp?._container?._vnode?.component;




  if (!rootComponent) return null;




  function findComponent(component, targetUid, depth = 0) {

    if (depth > 100) return null;

    const props = component.props || {};

    const attrs = component.attrs || {};

    const el = component.proxy?.$el;

    const uid = props.uid || props['data-ww-uid'] || attrs['data-ww-uid'] || el?.getAttribute?.('data-ww-uid');

    if (uid === targetUid) return component;

    const subTree = component.subTree;

    if (subTree) {

      if (subTree.component) {

        const found = findComponent(subTree.component, targetUid, depth + 1);

        if (found) return found;

      }

      const searchVnodes = (vnodes) => {

        if (!vnodes) return null;

        for (const vnode of vnodes) {

          if (vnode?.component) {

            const found = findComponent(vnode.component, targetUid, depth + 1);

            if (found) return found;

          }

          if (vnode?.children && Array.isArray(vnode.children)) {

            const found = searchVnodes(vnode.children);

            if (found) return found;

          }

        }

        return null;

      };

      if (Array.isArray(subTree.children)) {

        const found = searchVnodes(subTree.children);

        if (found) return found;

      }

    }

    return null;

  }




  const wwElement = findComponent(rootComponent, componentUid);

  if (!wwElement) return null;




  function findMethodInTree(comp, depth = 0) {

    if (!comp || depth > 15) return null;




    if (comp.proxy && typeof comp.proxy.updateColumnOverrides === 'function') {

      return comp.proxy;

    }




    if (comp.subTree?.component) {

      const found = findMethodInTree(comp.subTree.component, depth + 1);

      if (found) return found;

    }




    if (Array.isArray(comp.subTree?.children)) {

      for (const child of comp.subTree.children) {

        if (child?.component) {

          const found = findMethodInTree(child.component, depth + 1);

          if (found) return found;

        }

      }

    }




    const children = comp.subTree?.children;

    if (children && typeof children === 'object' && !Array.isArray(children) && typeof children.default === 'function') {

      try {

        const slotContent = children.default();

        if (Array.isArray(slotContent)) {

          for (const item of slotContent) {

            if (item?.component) {

              const found = findMethodInTree(item.component, depth + 1);

              if (found) return found;

            }

          }

        }

      } catch (e) { }

    }




    return null;

  }




  return findMethodInTree(wwElement);

}




// Usage:

const result = getWeWebComponent(uuid);

return result;

Then I trigger the event like this:

const datagrid = formulas['2e2deb8d-1255-4830-8068-6a4064c3c7f0'](context.component?.props?.[/* component_id */ 'db11f48e-4d11-4109-975d-04720cf7ad36']);




if (datagrid) {

    datagrid.updateColumnOverrides();

}

Hey,

Thanks for the suggestion! After thinking it through, I’ve decided to go with a config object approach using boolean variable IDs to manage the toggle. This allows me to use a global variable across all my grids, which feels like a more optimized, production-ready solution than scanning the DOM.

I appreciate the help regardless !

Hey Thomas :waving_hand:

Would you still have access to that setup by any chance? If so, it would really help us if you could create a bug ticket with a short video or steps showing the memory usage you observed. That’s the best way for us to properly investigate.

So far, we haven’t identified any confirmed cases of memory leaks tied to reusable components. In most of the performance issues we’ve reviewed, the root cause ended up being project-specific but, of course, we’d want to take a close look here to be sure.

If there is something going on, we definitely want to understand it and fix it.

Really appreciate your help on this :folded_hands: