DEV Community

Cover image for Learn how to make a styled picture frame web component with props (Learn Modulo.js - Part 2 of 10)
michaelb
michaelb

Posted on

Learn how to make a styled picture frame web component with props (Learn Modulo.js - Part 2 of 10)

👋 Welcome back! Didn't catch Part 1? No worries, you can start at the beginning, or just plunge in here!

Introducing: The PictureFrame Component

Our task in this tutorial will be to build a picture frame component, for styling photographs on a web app. Last time we ended with a snippet a little like the one below. However, in this tutorial, we've changed the "Template" to show instead a pink/salmon "picture frame" of a hippo with a caption below it. To begin this tutorial, copy and paste the following into a new file, and open in your browser:

<template Modulo>
    <Component name="PictureFrame">
        <Template>
            <div style="display: inline-block; border: 10px inset salmon; padding: 10px; margin: 10px; width: 100px; background: pink;">
                <img
                    style="width: 50px;"
                    src="https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/Hippo_walking.jpg/320px-Hippo_walking.jpg"
                />
                <p>Photograph: <em>The Return of the Hippo</em></p>
            </div>
        </Template>
    </Component>
</template>
<script src="https://unpkg.com/mdu.js"></script>
<x-PictureFrame></x-PictureFrame>
Enter fullscreen mode Exit fullscreen mode

Introducing Part 2

Viewing x-PictureFrame component, displaying a hippo and the source code showing corresponding attributes

In this tutorial, we'll add a Style to plain HTML components, along with a discussion on the first core concept in the Modulo framework: Component Parts, before finally peeking at one more important component part: Props.

First, you might notice something messy about the above code. All the style is rammed into a style= attribute! When coding CSS, placing all our styles in inline style= attributes is often hard to maintain. Modulo supports the "Style" Component Part to let us write CSS code more naturally. What's a Component Part? Well, we'll get to that as well. First, let's get stylish!

Step 1: Creating a Style Component Part

<Template>
    <div class="salmon-frame">
        <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/Hippo_walking.jpg/320px-Hippo_walking.jpg" />
        <p>Photograph: <em>The Return of the Hippo</em></p>
    </div>
</Template>
<Style>
    img {
        width: 50px;
    }
    .salmon-frame {
        display: inline-block;
        border: 10px inset salmon;
        padding: 10px;
        margin: 10px;
        width: 100px;
        background: pink;
    }
</Style>
Enter fullscreen mode Exit fullscreen mode

Much better! This will look and behave much like a "Style" tag in HTML, allowing you to create classes and selectors to your heart's content.

Step 2: Styling the host component itself

Often, you'll want to style the Web Component itself (in this case, the <x-PictureFrame> tag). We can use the special :host selector for this:

<Style>
    :host {
        text-align: center;
    }
    /* ... etc ... */
</Style>
Enter fullscreen mode Exit fullscreen mode

Introducing Component Parts

The central concept to Modulo is that of Component Parts. All component definitions consist of some number of Component Parts. Thus, a component definition is really just a collection of Component Part definitions. "Under the hood" of your component, each Component Part will have a different role to contribute to the functionality of your component.

We've already learned the two most basic Component Part:

  1. Template - <Template> - Templates are where you put any arbitrary HTML code that you want your component to contain. For now, we'll just include some unchanging HTML. In the next tutorial, we'll learn how to use "templating language" to control what HTML is produced in what circumstance.

  2. Style - <Style> - Just like the <style> tag in HTML. However, it has one one import distinction: The styles contained will be limited to your component. This is because CSS written here will be automatically prefixed so that it will only apply to your component and any HTML generated by the Template Component Part. This is quite useful: It allows us to write isolated CSS for each component without even thinking about it. Keeping our CSS separate means fewer unexpected interactions.

Keep in mind both Style and Template have a lot of configurable options, but for our usage, the default is good enough.

Introducing Props

Now, let's introduce a third important Component Part: Props

In the previous section, we were mostly concerned with defining components. Recall that components are defined once, but can be used many times. The purpose of Props is to allow more flexibility in that second step: Props Component Part defines the properties that can be customized about a component each time it is reused.

Props to us!

Right now, our picture frame doesn't have a way to be re-used for different photos or captions. We can't just all be chased by hippos forever! 🦛🦛🦛

We want to eventually be able to do something like this:

<x-PictureFrame
    image="https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/Hippo_walking.jpg/320px-Hippo_walking.jpg"
    caption="Hippopotamus out of water"
    photographer="Lee R. Berger"
></x-PictureFrame>
Enter fullscreen mode Exit fullscreen mode

Then, it'd feel like a "real" HTML tag -- that is, configurable via attributes. Let's "peel back the layers" and examine out how this can be accomplished using Props.

Step 3: Adding Props

In order for a component to be able to "receive" props when it is reused, we must define Props. Props definitions are like previous definitions, except they have no contents and are just an opening tag with attributes, followed by a closing tag. See the following:

<Props
    image
    caption
    photographer
></Props>
Enter fullscreen mode Exit fullscreen mode

Note: A common mistake is to forget to include Props. Every attribute you expect your component to receive must be listed in Props to be available for the next step.

Step 4: Using Props in Template

Now, we'll get our first sneak peak at the true power of Templates: Adding in data and values into your HTML. You can use the following syntax to get your "props" and inject them into the generated HTML:

<div class="salmon-frame">
    <img src="{{ props.image }}" />
    <p>Photograph: <em>{{ props.caption }}</em></p>
    <p>Photographer: <em>{{ props.photographer }}</em></p>
</div>
Enter fullscreen mode Exit fullscreen mode

Note the {{ and }}: This the templating syntax that indicates where the "props" values get inserted.

<x-PictureFrame> - Complete Example

Combining it all, we get the following results. Note how our custom Web Component can be re-used with different attributes:

<template Modulo>
    <Component name="PictureFrame">
        <Props
            image
            caption
            photographer
        ></Props>
        <Template>
            <div class="salmon-frame">
                <img src="{{ props.image }}" />
                <p>Photograph: <em>{{ props.caption }}</em></p>
                <p>Photographer: <em>{{ props.photographer }}</em></p>
            </div>
        </Template>
        <Style>
            :host {
                text-align: center;
            }
            img {
                width: 130px;
            }
            .salmon-frame {
                display: inline-block;
                border: 10px inset salmon;
                padding: 10px;
                margin: 10px;
                width: 200px;
                background: pink;
            }
        </Style>
    </Component>
</template>
<script src="https://unpkg.com/mdu.js"></script>
<h1>Learn Modulo.js: Part 2</h1>
<h2>Style and Props</h2>
<x-PictureFrame
    image="https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/Hippo_walking.jpg/320px-Hippo_walking.jpg"
    caption="Hippopotamus out of water"
    photographer="Lee R. Berger"
></x-PictureFrame>
Enter fullscreen mode Exit fullscreen mode

Conclusion

That's all for Part 2! Be sure to follow to catch the rest, and, as always, feel free to ask questions or suggestions in the comments.

Top comments (0)