Container

The container library enables seamless two-way communication between parent and child components, without relying on props. This makes it a powerful tool in your arsenal.

Set and Get

Below are examples of how to set and retrieve values from the container:

// 1. Set a regular value (string/number)
container.set("myContainerName1", "Hello World 1");

console.log(container.get("myContainerName1"));
// Result: Hello World 1

// 2. Set an object
container.set("myContainerName2", {
  title: "Hello World 2",
  description: "Lorem ipsum dolor",
});

console.log(container.get("myContainerName2").title);
// Result: Hello World 2

// 3. Set a function
container.set("myContainerName3", function(arg1, arg2) {
  console.log(`Hello ${arg1} ${arg2}`);
});
// Pass the expected arguments to the function after the first argument
// When get is called, the function is automatically triggered
container.get("myContainerName3", "World!", 3);
// Result: Hello World!

Has and Overwrite

If you try to create a container with a name that already exists, it will throw an error telling you that the container already exists and that you need to take appropriate action. This is where has and overwrite come in—a safeguard to avoid collisions.

Has

if (container.has("myContainerName1")) {
  container.set("myContainerName1", "Hello World 1");
}

Overwrite

container.set("myContainerName1", "Hello World 1", true);

Example

If you are familiar with the Step-by-Step tutorial, you might recognize the increment example below, but with the exception that we are using the container instead of props to increment the value.

Let's modify our parent view (src/templates/views/Start.js):

import Increment from "@/templates/views/blocks/Increment";

export default function Start(props, container, helper, context) {

  container.set("increment", 0);
  
  return `
    <article class="relative card-1 border-bottom ingress">
      <div class="wrapper md">
        <h1 class="headline-1">${props.title}</h1>
        <p>${props.description}</p>
      </div>
    </article>
    <div class="increment">
      ${this.block(Increment, { title: "Start incrementing" })}
    </div>
  `;
}

And let's modify our child component (src/templates/views/blocks/Increment.js):

export default function Increment(props, container, helper, context) {
  // Tell the user that the 'increment' container needs a default value
  if (!container.has("increment")) {
    throw new Error(`Set a default integer value for the 'increment' container in the parent component!`);
  }

  // Get increment value from container
  const increment = container.get("increment");

  // Set and overwrite the increment value in container
  const clickEvent = this.bind((event) => {
    container.set("increment", increment + 1, true);
  });

  return `
  <article class="relative card-1 border-bottom ingress">
    <div class="wrapper md">
      <header class="mb">
        <h2 class="headline-2">${increment > 0 ? "Incremented" : props.title}</h2>
        <p>Has been incremented <strong>${increment}</strong> times!</p>
      </header>
      <a class="button bg-primary sm my-btn" href="#2" onclick="${clickEvent}">Increment +</a>
    </div>
  </article>
  `;
}

What's the Point?

Now you might be asking yourself what's the point of utilizing the container and not just props. While props can solve many cases, the container facilitates seamless communication with its parent component—in this case, Start. You can actually get the increment in Start and also increment it there too, as shown below in the src/templates/views/Start.js example:

import Increment from "@/templates/views/blocks/Increment";

export default function Start(props, container, helper, context) {
  if (!container.has("increment")) {
    container.set("increment", 0);
  }

  const clickEvent = this.bind((event) => {
    container.set("increment", container.get("increment") + 1, true);
  });

  return `
    <article class="relative card-1 border-bottom ingress">
      <div class="wrapper md">
        <h1 class="headline-1">${props.title}</h1>
        <p>${props.description}</p>
        <a class="button bg-primary sm my-btn" href="#" onclick="${clickEvent}">Increment from start +</a>
      </div>
    </article>
    <div class="increment">
      ${this.block(Increment, { title: "Start incrementing" })}
    </div>
  `;
}

Isn't that cool! We have now created an example where you can seamlessly, between a parent and child component, increment a number with buttons from both the parent and child components.

Last updated