Parsing a component key:value from an experience page

Hello, I have written some code to fish out a key from a component that I need on the server side. This component is on an experience page within a container. I’ve used SPA-SDK methods to accomplish this.

I am looking for some advice on cleaning up this code. I had to cast some types and drill into grand children which seems brittle. Any advice would be appreciated:

  const component = page?.getComponent("myContainer");
  const children = component?.getChildren();
  const grandChildren = children?.[0]?.getChildren();
  const myModule = grandChildren?.find(
    (grandChild) =>
      (grandChild as ContainerItem)?.getType() === "myCType",
  );

  const reference = (myModule as any)?.model?.content?.["$ref"];
  const content = page?.getContent<Content>(reference);

  const { myKey } =
    (content as unknown as { [key: string]: { [key: string]: string } })
      ?.data ?? {};

In the above, page is a fetched Bloomreach experience page

Hi @Tim_McMahon ,
I am curious about the following:

  • What SDK version and framework are you using?
  • Is this a template / render function of a component?
  • How is your setup such that a container component has grand children? Its very odd to me, container-items are supposed to be ‘leaves’ in the page model tree. Is the “myContainer” actually a container component, or a static component?

In the case that this is a mapped component you should also get ‘component’ as a property of the component, so you wont have to look it up using the page object yourself.

Either way I think you could take a look at the typedoc for the Component API.

In this case for example, if you know the name of the component, or the ID, you can directly get the component in the nested tree. Note that right now you are finding the first instance of a component with a certain ctype, but since container-items are determined by the business user this could be absent, or a different component you expect. Its probably safer to query using the nested path or ID.

Once you have your container-item you should be able to use getContent to retrieve the content you need and assuming its a Document you can use getData to get the data property and retrieve the value for your key.

Hi @Joeri_de_Gooijer

Thanks for the reply.

  • I am using v21.0.0 of the spa-sdk
  • This code is in a loader function on a Remix page. Similar to getServerSideProps in Next. I don’t think I have access to it as a mapped component since I have not entered React yet.
  • I’m not sure how to answer this one totally. I see ‘myContainer’ as a Managed component. See attached:
    Screen Shot 2023-08-02 at 2.41.54 PM

I updated my code to the following based on your advice (strings updated per screenshot above):

  const parentComponent = page?.getComponent("main");
  const childComponent = parentComponent?.getComponent("container");

  const myModule = childComponent?.getComponent<ContainerItem>("component-name-kcGVj");

  const myContent = myModule?.getContent<MyParams>(page);

  const { myKey } = myContent ?? {};

I feel like this is much better, but I’m still dependent on some happy strings to get to the content. Is there a way to dynamically get the name of the component? Am I still missing something?

I tried doing .getComponent("component-name-kcGVj") higher up, but it was returning undefined until the point in the first block of code.

Hi @Tim_McMahon ,
In your original post you were looking for grandchildren in the container component, that seemed odd. Your screenshot shows what I expect: a container (managed component) that might have 1 level deep container items.

If you check the typedoc you will notice that there is a getComponentById, you can use that method from a parent component. In your case you could do page.getComponent() to get the root component and then getComponentById('component-name-kcGVj') to get your component. Now that I’ve written that it seems obvious to me that we are missing the ability to call getComponentById directly from the Page object, I’ll put it on the to-do list :slight_smile: .

I’m curious as to why you are trying to drill down into this specific container-item. I wouldn’t expect one to try and manage that outside a mapped react component code as they are dynamic in the sense that the business user is in control of the container-items in a container.

Hi @Joeri_de_Gooijer

If I try

  const myComponent = page?.getComponent();
  const myModule = component?.getComponentById("component-name-kcGVj");

I get undefined when I log myModule. Am I doing something wrong?

To answer your question on the drill down: I need to get a property from this specific item because it holds a filter key that we send to Discovery in order to produce a product list page based on a business user’s configuration. For example, if a business user wants to show all products where the facet foo is set to bar they would enter a filter key bar on the appropriate container-item on the experience page.

We do as much Discovery on the server-side as possible, hence the reason for needing this on the server-side instead of client-side.

Ah I think I understand your use case. You require the data to be already available during the framework component rendering because that is required to be done synchronously in SSR right?

I now realize that component-name-kcGVj is most likely the name of the component and not the id. In that case its easier to retreive it using the path in the page tree like

const root = page?.getComponent();
const myModule = root?.getComponent('main', 'container', 'component-name-kcGVj');

However even in this case you might get undefined back because the component might not be defined on the page, or every page for that matter. The container-items are controlled by the business user after all so they might not be in that container at all, or maybe in a different position (which changes the generated ID), maybe they might have a different name due to the project they are in, etc.

I think to solve your use case you could drill down to the container component, iterate over the children to retrieve the different Discovery API information you need to make the calls by looking for specific component types/properties, provide the information in whatever Remix has to provide it to the component render tree as a prop so it does not have to be retrieved while rendering the react component tree. Its actually a bit similar to what we do with the BrPage component where you provide the ‘page’ object as a prop after retrieving it such that we can render the component tree without any async behavior.

Yours is an interesting and probably common use case! That we do not have a good code example for, I will make an internal issue to properly test and describe this in the documentation.

2 Likes