Relation Columns are not accessible from the details > fields > option

I have a table of tags which is related to another table - instructions

What I need to do is to list which instructions are used by the tag when I selected the tag detail screen.

Under Options I do not have the ability to select the rel2instructions column from the tag detail screen editor

the relationcolum from tags to instructions

I want the user to be able to select the tag to see what other instructions share the same tag.

Ideas?

thanks

You can’t display a relation in the fields component. You’ll need to display the relation using a collection instead.

1 Like

thanks much. Been Beating my head against that wall a lot.

Ya, in classic apps we used to be able to display a relation in a “basic table” component, but that didn’t come over when Glide introduced Pages (current version of Glide apps).

BIG fan of yours btw. Your vids are a game changer

Thanks for that! :blush: Let me know if there’s other content you’d like to see.

I have quiet the request list… be careful what you wish for. One thing I’d like to do (think there’s a tutorial on this?) is to create a traditional tag button with the word on a pill shaped image rather than the image and the word.

Is this supported already and I am just not finding it?

worked great btw. I am going to use this extensively in the app. Cheers

1 Like

Did the pill need to be clickable?

yes on the clickable button

What do you mean by “on the clickable button”? So in the end you mean it has to be clickable and lead you to some sort of screen, or whatever type of action you need?

yes - I have tags now that loads the detail screen for each tag which includes a description and the other tables that reference the tag

So can a record hold multiple tags?

yes

Example - Tags could be, veggies, grains, …

Recipes are tagged with the ingredients they use.

If I click the grains tag I see a list of all the recipes that use grain

I have it working now but with images and text for the tag style

I have a working prototype for you. Will record a video during today my time.

Here’s what I came up with. It involves a horizontal cards component that uses a relation to a unique list of tags as the source. When clicked, it will show all the items with that tag. It’s styled with CSS (see below):

.tagsList [data-testid="cc-card"] {
width: fit-content !important;
}

.tagsList .card {
padding: 0 3px;
text-align: center;
}


.tagsList p {
font-size: 0.75rem;
}

Here’s an alternative approach using custom component.

Prompt

Create a list of tags in a pill-style that would use the “Tags” column as input, each element delimited by a comma, justify-content: start, align-items: start.

Once clicked, determine the chosen tag’s name, set the chosen tag’s name to the “Viewing” column, wait 100ms, and execute the “Show tag” action.

Code

<html>
  <head>
    <script src="//unpkg.com/alpinejs"></script>
    <style>
      /*! tailwindcss v3.4.4 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.flex{display:flex}.cursor-pointer{cursor:pointer}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.justify-start{justify-content:flex-start}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.5rem*var(--tw-space-x-reverse))}.rounded-full{border-radius:9999px}.bg-\[\#613DC7\]{--tw-bg-opacity:1;background-color:rgb(97 61 199/var(--tw-bg-opacity))}.bg-transparent{background-color:transparent}.px-4{padding-left:1rem;padding-right:1rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}
    </style>
  </head>
  <script>
        const S = Alpine.reactive({"tags":null,"viewing":null});
        S.init = () => {
          window.parent.postMessage({ type: "start-component" }, "*");
        };
        Alpine.data("state", () => S);
        let updateTimeout = null;
        Alpine.effect(() => {
            let updates = {};
            // Alpine needs to know about all the fields to watch them
            if (S.tags !== null) updates.tags = S.tags;
            if (S.viewing !== null) updates.viewing = S.viewing;
            // Debounce updates to prevent spamming the parent window
            if (updateTimeout) {
              clearTimeout(updateTimeout);
              updateTimeout = undefined;
            }
            updateTimeout = setTimeout(() => {

              const updatesNoProxy = JSON.parse(JSON.stringify(updates));
              window.parent.postMessage({ type: "set-state", state: updatesNoProxy }, "*");
            }, 50);
        });
        function showTag() {
      window.parent.postMessage(
        { type: "trigger-action", state: "showTag" },
        "*"
      );
    }
        function setState(newState) {
          for (const key in newState) {
            if (S[key] !== newState[key]) S[key] = newState[key];
          }
        }
        window.addEventListener("message", event => {
          switch (event.data.type) {
            case "set-state":
              const state = event.data.state;

              setState(state);
              break;
            case "set-theme":
              const theme = event.data.theme;

              const root = document.querySelector(&apos;:root&apos;);
              root.style.setProperty(&apos;--accent-color&apos;, theme.colors.accent);
              root.style.setProperty(&apos;--foreground-color&apos;, theme.colors.foreground);
              root.style.setProperty(&apos;--background-color&apos;, theme.colors.background);
              break;
            default:
              break;
            }
        });
  </script>
  <body
    x-data="state"
    class="flex items-start justify-start bg-transparent"
  >
    <div
      class="flex flex-wrap justify-start items-start space-x-2"
    >
      <template x-for="tag in tags.split(',')">
        <div
          class="bg-[#613DC7] text-white rounded-full px-4 py-2 cursor-pointer"
          @click="viewing = tag.trim(); setTimeout(() => showTag(), 100)"
          x-text="tag.trim()"
        ></div>
      </template>
    </div>
  </body>
  <script src="//cdn.jsdelivr.net/npm/@iframe-resizer/child"></script>
</html>

Updated: Succesfully changed, plus styling the text (font size + weight).

<html>
  <head>
    <script src="//unpkg.com/alpinejs"></script>
  </head>
  <style>
  body {
    font-size: 12px;
    font-weight: 600;
  }
</style>
  <script>
    const S = Alpine.reactive({
      tags: null,
      viewing: null,
    });
    S.init = () => {
      window.parent.postMessage(
        { type: "start-component" },
        "*"
      );
    };
    Alpine.data("state", () => S);
    let updateTimeout = null;
    Alpine.effect(() => {
      let updates = {};
      // Alpine needs to know about all the fields to watch them
      if (S.tags !== null) updates.tags = S.tags;
      if (S.viewing !== null) updates.viewing = S.viewing;
      // Debounce updates to prevent spamming the parent window
      if (updateTimeout) {
        clearTimeout(updateTimeout);
        updateTimeout = undefined;
      }
      updateTimeout = setTimeout(() => {
        const updatesNoProxy = JSON.parse(
          JSON.stringify(updates)
        );
        window.parent.postMessage(
          { type: "set-state", state: updatesNoProxy },
          "*"
        );
      }, 50);
    });
    function showTag() {
      window.parent.postMessage(
        { type: "trigger-action", state: "showTag" },
        "*"
      );
    }
    function setState(newState) {
      for (const key in newState) {
        if (S[key] !== newState[key])
          S[key] = newState[key];
      }
    }
    window.addEventListener("message", (event) => {
      switch (event.data.type) {
        case "set-state":
          const state = event.data.state;

          setState(state);
          break;
        case "set-theme":
          const theme = event.data.theme;

          const root = document.querySelector(":root");
          root.style.setProperty(
            "--accent-color",
            theme.colors.accent
          );
          root.style.setProperty(
            "--foreground-color",
            theme.colors.foreground
          );
          root.style.setProperty(
            "--background-color",
            theme.colors.background
          );
          break;
        default:
          break;
      }
    });
  </script>
  <body
    x-data="state"
    class="flex flex-col items-start justify-start bg-transparent w-full"
  >
    <div
      class="flex flex-wrap items-start space-y-2 w-full"
    >
      <template x-for="tag in tags.split(',')">
        <div
          class="bg-[#613DC7] text-white rounded-full px-4 py-2 cursor-pointer mr-2 mb-2"
          @click="viewing = tag.trim(); setTimeout(() => showTag(), 100)"
          x-text="tag.trim()"
        ></div>
      </template>
    </div>
  </body>
  <script src="//cdn.jsdelivr.net/npm/@iframe-resizer/child"></script>
</html>

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.