Search code examples
typescriptinternationalizationcontent-management-systemeachsvelte

svelte recursively create treeview from unknown object


I am coding some kind of primitive CMS just for editing, updating and adding new strings of an app. the strings are all stored in a database and each of them have a special key (representing the folderstructure of the project "shared.atoms.copie...") for referencing from the sourcecode to the database. the editing stuff and so on is already coded, but now i need to provide the user some kind of navigation and the possibility to see a preview of the page, which is being edited, by clicking (for example) a node of the navigation

for that i want to build a treeview of the already from all keys built object which looks like the following:

tree: {
  common: {
    date: [{
      lang: "en", 
      key: "common.date", 
      version: 1, 
      value: "Date"
    },
    block: {
      lang: "en", 
      key: "common.block", 
      version: 3, 
      value: "Block"
    }]
  },
  shared: {
    atoms: {
      copyClipboard: {
        copiedToClipboard: [{
           lang: "en", 
           key: "shared.atoms.copyClipboard.copiedToClipboad", 
           version: 3, 
           value: "Copied to Clipboard!"
        }]
      }
    }
  }
}

in javascript i got this solution what does give me a tree, when i use {@html htmlTree}:

function buildHtmlTree(treeNode) {
  let objKeys = Object.keys(treeNode);
  if (objKeys.length == 0) {
    return;
  }
  for (let key of objKeys) {
    let currentNode = treeNode[key];
    htmlTree += `
      <ul class="ml-2 mb-4">
        <span>
          ${key}
        </span>
      `;
    if (Array.isArray(currentNode)) {
      // is leaf
      for (let currentLanguage of currentNode) {
        htmlTree += `
          <li class="ml-2">
            ${currentLanguage.lang}: ${currentLanguage.value}
          </li>
          `;
      }
    } else {
      // is branch
      buildHtmlTree(currentNode);
    }
    htmlTree += `</ul>`;
  }
}

buildHtmlTree(tree);

now i am trying to make it the svelte-way, but i'm stuck at this ... i think i just made an infinite-loop.

<script>
  let svelteTree = Object.keys(tree);
</script>

<section>
  {#each svelteTree as key}
    <ul class="ml-2 mb-4">
      <span>{key}</span>
      {#if Array.isArray(svelteTree[key])}
        {#each svelteTree[key] as currentLanguageSvelte}
          {currentLanguageSvelte.lang}: {currentLanguageSvelte.value}
        {/each}
      {:else}
        <svelte:self svelteTree={svelteTree[key]} />
      {/if}
    </ul>
  {/each}
</section>

Solution

  • If the values are not an Array nor an Object, you can just output it without calling svelte:self:

    {#each Object.entries(data) as [key, values]}
     <ul class="ml-2 mb-4">
      <span>{key}:</span>
      {#if Array.isArray(values)}
        {#each values as item}
          <svelte:self data={item} />
        {/each}
      {:else if (typeof values === 'object')}
        <svelte:self data={values} />
      {:else}
        <span>{values}</span>
      {/if}
     </ul>
    {/each}
    

    Working example