The Modulo Tutorial: Part 2

In Part 2 of this tutorial, we'll explore three more features: Customizing with Props, substituting with Template variables, and finally building your finished project into a single faster-loading file. Prerequisites: HTML, CSS, and Part 1.

1. 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 CPart defines the properties that can be customized about a component each time it is reused. We already know that "slots" can be used to customize components with extra content, which is a good start. However, what if we wanted even more options for users to customize the components, such as by using HTML properties? Thus, to start our discussion of Props we'll first get more practice reusing components.

Other people's components

Before we learn how to use the Props CPart itself, let's first get acquainted with a concept we haven't done before: Reusing components that were coded by someone else! In a realistic scenario of an organization building a web app, it's likely that most of the components you will be using won't actually be written by you personally, but instead by someone else on some sort of internal "component library" team. Similarly, even if working by yourself, you'll need to learn how to component libraries written by others so you can use components already defined for you to accomplish common tasks. Don't reinvent the wheel!

In this next activity, we'll practice reusing components.

Try it now

Context: The <x-ExampleBtn> button component has already been defined for you. It was defined to accept two so-called "props": label, and shape. We'll cover how it was defined to use a Props CPart later on. For now, we'll practice only with using the x-ExampleBtn.

  1. Examine the code below. Examine the preview on the right. Do you see how the code in turn uses the x-ExampleBtn in two locations, with a different shape and label each? We can call each of these a component instance. Each component instance can receive different or customized props attributes.
  2. Note the label="..." prop attribute that is on each x-ExampleBtn. Test it out by editing the contents of this attribute on one or both of the buttons and click RUN to see the result on the right.
  3. Note the shape="..." prop that is on each button. It's impossible to have known this without reading the CSS of x-ExampleBtn, but it accepts either "square" or "round". Try changing one to the other and see the results.

Comprehension Challenge 1: Based on the patterns you see here, add a third button to this example that is round and contains the word "Hello".

Comprehension Challenge 2: See if you can think up what props might be attached to different types of widgets, e.g. a "modal pop-up" widget might specify the title, or an Open Street Map widget might specify latitude and longitude for the map being displayed. What would a text input need? What about a tabbed navigation interface? How about a component that shows a chess board with pieces in a specified setup?

Why use Props? Components are "write once, use everywhere". That is to say, you only need to define a component once to use it throughout your pages. The advantages of "write once, use everywhere" are clear: By writing "DRY" (non-repetitive) code, you can make changes to the single component definition and see those changes take effect wherever that component might be used. However, sometimes you want each instance of a component to have different content or modified behavior. This is where Props come into play: They allow you to customize differences in content or behavior between multiple uses of the same component.

Defining props

Let's "peel back the layers" and examine out how this ExampleBtn was written.

In order for a component to be able to "receive" props when it is reused, we must define a Props CPart, which is conventionally placed as the first thing in a component definition. Props CParts are defined somewhat like previous CParts, except they have no contents and are just an opening tag with attributes, followed by a closing tag. A Props CPart might look like this:

<Props name device frequency></Props>

The recommended style for Modulo code is to add a newline after each prop name for easier reading. HTML syntax doesn't care either way, so the above might be improved for readability if it were written as follows:

<Props
    name
    device
    frequency
></Props>

Once you have defined which props you expect a component to receive by using the Props CPart, you can then reference the values of those props elsewhere within the component: Either in the Template CPart (what we'll cover next), or in the Script CPart (covered later).

2. Templating

Can I use props in CSS?

No. Note that the Style CPart intentionally does not support template substitution, or any such "live" modification whatsoever. If you want to directly insert props as CSS values, this should be done in the template by using an inline style= attribute. For more complicated CSS transformations, consider setting CSS variables on the style= attribute that are then used by the Style CPart, by using the var() CSS function.

Our discussion of props gives us a chance to explore the Template CPart a little bit deeper. Previously, we have only used a Template CPart to display static, unchanging HTML. That's quite limiting. The typical purpose of "templating" or "a templating language" is to allow customization, substitution, and transformation of HTML content. Templating isn't unique to web development: If you've used template files with word processors such Microsoft Word, or email templates within marketing or sales software, you'll know that with templating you can include placeholders where we want to insert data or values, in order to personalize each instance.

Using props within a template

Within a Template CPart, we can insert data or values using a special "placeholder" syntax that is as follows: {{ myVariableName }}. We can also use a dot (".") in this syntax to access properties. Thus, you will see this placeholder syntax more often used like this: {{ props.device }}. So, combining this information with the 3 props that we have above, we can create a template that has 3 placeholders that get filled in with specific data when the component gets reused. Examine the below code and try to guess at what it might do:

<Template>
    Hi {{ props.name }}, your {{ props.device }} is at {{ props.frequency }}hz.
</Template>

In this case, if we were to use our component with the attributes name="Tux", device="Radio" and frequency="2600", then the following text would be rendered: Hi Tux, your Radio is at 2600hz.

The "Modulo templating language" was modeled after Django, Jinja2, or Liquid, and thus is quite powerful, being capable of much than the simple substitutions we're learning here. We'll learn more features later.

Combining props with template HTML

In the previous example, we showed how text can be rendered using substitutions. Next let's see how such substitutions can be used to generate more complex HTML code.

Examine the following component definition. This component definition is the same definition for the x-ExampleBtn that was referenced in the previous challenge (with the exception of some CSS being omitted for simplicity):

At this point, we can finally begin to understand the <x-ExampleBtn> component that we were using previously. In this case, the value of the label="Example Button" prop gets inserted as the text content of the button element, and the shape="round" prop value gets inserted in the class= attribute of the button element. Thus, if the shape prop receives the value of "round", the button gets the CSS class of .my-btn__round, and similarly if it received the value of "square", it'd get the class .my-btn__square. These CSS classes, as you may have guessed, are what controls the look of the button, either giving it rounded edges or a more rectangular, towering look.

Try it now

Okay, let's take this new concept and apply it to our previous HelloWorld component. Go back to the project you were working on previously, and follow these steps to practice adding props to your component:

  1. Examine your HelloWorld component definition. Near the top, just below the "<component..." opening tag, add a Props CPart, just like the examples before.
  2. Experiment with specifying props. For example, to keep building this tutorial app, give it the props of goal and amount.
  3. If done correctly, your component definition will start something like this:
  4. Update the template to now use these props somewhere. For the tutorial example, we'll be building a little "clicker" that will count up to a number (the "goal") by an increment amount (the "amount"). So, for now, let's just display the goal prop and amount prop:
  5. Refresh the web browser to view the results. Assuming you still have the component displaying somewhere on your page, if done correctly, you will see your component update to reflect the changes you made to your template, possibly something like "Goal: " and "Amount: ".
  6. Comprehension question: Why doesn't it show any text after "Goal:" and "Amount:"?
  7. Answer: If you are seeing blanks, it's probably because you are not passing in the Props that are required.
  8. To fix this, we need to specify the props in the location where we are reusing or actually displaying the component on the page. This is done by adding attributes with the names "goal" and "amount" to the component tag instances.
  9. If you have made a lot of changes since Part 1 of this tutorial or did not follow the naming suggestions, your code may end looking different than this. However, if that's not the case, your code might end up looking like this:

Bonus Challenge: Read ahead about template filters, and try using those here.

Template filters

The Modulo templating language has two more big features: filters (for formatting values), and template-tags (for control-flow). We'll briefly introduce filters now, but for a more thorough introduction to the Modulo templating language, you should read the Templating section.

Template filters format or otherwise transform template variables. The template filter syntax consists of taking a template variable and adding a vertical bar followed the name of a filter (e.g. varName|filterName). The following example will transform the text contained in the props.name template variable to make it all uppercase:

Hello {{ props.name|upper }}

Some filters can also take extra modifiers or options. This is called the template filter argument. Do you recall how our previous ExampleBtn component only supported either "round" or "square" as CSS classes? In this next example, we are going to use the |allow template filter to ensure that only "round" or "square" are permitted:

<button class="my-btn my-btn__{{ props.shape|allow:"round,square" }}">
    (... snip ...)
</button>

Note how the argument is separated from the filter with a colon: The general syntax is varName|filterName:"argument". Thus, the |allow:"round,square" filter instructs Modulo to only output the property of props.shape if it exactly matches the text "round" or "square".

You can do more than this with filters: You can string together the dozens of available filters for more powerful combinations, and by using JavaScript script tags (covered in Part 3) you can easily author your own. The full Templating Reference has examples of all filters available.

Template tags

In addition to filters, the Modulo templating language also support powerful "template tags", which allow for more complicated custom behavior. This includes the "if" template-tag, which allows for conditional rendering (e.g. "only show the submit button if a form is filled correctly"), and the "for" template-tag, which allows for HTML to be repeated for each item of some given data (e.g. "every blog post get's it's own <li> element"). The full Templating Reference has examples of the different template tags available.

3. Build

Why build? Let's start with a "thought experiment", where we fast forward into the future. Imagine that as you create more and more components, you begin splitting them up into more component libraries. Also, as mentioned previously, perhaps you find some nice component libraries that others have made that you want to use in your project. In the end, you might find yourself with dozens of HTML files being loaded, scattered in different directories.

In this situation you will have too many <Library> tags, meaning your page may take a long time to start-up. As it's downloading all the different files, the browser will be showing the ugly, unformatted text in the mean-time. On slow connections, this could be end up being a frustrating experience for users. This is why building to one file is useful.

We've got one more topic for Part 2 of the Modulo tutorial: How to keep your project manageable and loading fast as you accumulate more and more Component definitions, and they require more and more files.

Library

In Part 1, we learned we can use -src= to split Template and Style Component Parts into separate HTML and CSS files respectively. However, what if our component definition files themselves get too big to manage? That is, what if we define so many components, that we need to start organizing the components themselves?

This is where the Library definition tag becomes useful. It allows us to cluster our components into individual Component Libraries, that then get imported into different Component namespaces.

First, re-examine our original Modulo import:

<script Modulo
    src="https://unpkg.com/mdu.js"
    -src="/libraries/my-stuff.html"
></script>

To use a Library definition tag, we move the -src="/libraries/my-stuff.html" and add a namespace to one or more <Library> tags in the Modulo script tag. This ends up looking like the following:

<script Modulo src="https://unpkg.com/mdu.js">
    <Library
        -src="/libraries/my-stuff.html"
        namespace="mylib"
    ></Library>
</script>

Why use namespaces? Namespaces allow different component library files to have conflicting component names. This is especially useful when using third-party component libraries or while working in a big team: That way, if both you and another developer define a component with some common name (eg name="Button"), there won't be a conflict as long as you load each into different namespaces.

Let's break this down:

However, there is a downside to splitting up your files: Each new file you create will require an additional request to load it. This means that each additional individual file you include in a page will cause the page to take a little longer to loader. So, if -src= causes it to load slower, how can we stay neat during development, but still have a fast site? This is where "building" comes into play.

Building: Packing it all up

"Building" is a feature of Modulo where it packs up all the components, JavaScript, and CSS you have loaded on a particular page into single .js and .css files, respectively. This single file file, called a build, contains all the code from all your components (including CParts that were split off using -src=).

These "builds" are fully independent: Once the "build" file is included using a <script src="...">, all your components on that page will work, without any need for including Modulo or your libraries specifically. In other words, this tag replaces all the boilerplate you have on your page, and you can remove all the importing-type code (e.g. <script Modulo>) from your HTML files, replacing it only with the single script tag. Not only that, it will also pre-compile JavaScript code, removing the need for "eval", and reducing file sizes. This causes the components to load much faster.

Generally, developers create builds before "launching" their site to "production" (e.g. publishing their site for the world to see), since it results in the fastest possible loading time. Note that you should only attempt to edit your original, source files: Builds are only for releasing or publishing, but not for editing. Never directly edit builds; instead, create new ones.

Pre-rendering HTML for instant loading

modulocli If you truly want to build using terminal or NPM-based tools, Modulo supports that as well with modulocli. However, Modulo is still in alpha stages, and so the browser-focused workflow is the best documented, tested, and least likely to change with updates. That said, it's usable: It's what was used to build this website! To get going, use the create-modulo scaffolding tool, by running the following: npm init modulo

Even with built JavaScript code, you might notice that there is a "flicker" effect, where the page remains unstructured and unstyled until all your components finish rendering their Templates. Depending on how complicated your page is, this could be brief and forgivable, or could take a while and create a bad user experience. One option is to add a "Spinner" to your page, that is removed once things are loaded. Another option is to use pre-rendered HTML.

Modulo can pre-render HTML as well, and output a brand new HTML file that can be published instead of your original one. A "pre-rendered HTML" file is a special, processed HTML file which basically "freezes" the result of loading the page for the first time. You can use this messy, processed HTML file as a drop-in replacement to your previous HTML page when launching, as it will already have JavaScript and CSS tags included for your build!

To build a project, we will need to use the Modulo Command Menu. The console is a feature of all web browsers. As a web developer you likely have used it: It's a panel, hidden by default, where JS, CSS, and other such error messages are displayed. While most JS frameworks require NPM-based tools to build, Modulo can be built right from your browser's console, simply clicking on the build commands in the COMMAND menu, that is visible in your browser developer tools console.

Build files will look like: modulo-build-xx4bz9v4.js, modulo-build-x9f2za71.css. Note the so-called "hash" (e.g. “xx4bz9v4”): These unique IDs identify each JS and CSS file that is generated by Modulo. If you change something in your components, you will cause one (or both) of these hashes to change, if they end up affecting the component's behavior (.js) or it's appearance (.css).

Try it now

  1. Open up your HTML file in your browser (e.g. Firefox or Chrome/Chromium). Do not open your component library HTML files: Be sure to open the file that is doing the importing and using the components, not the file(s) that contain the component definitions themselves.
  2. Bring up the console: Press Control+Shift+J (Linux, Windows) or Command+Option+J (macOS) on your keyboard to open the Console. Alternatively, you can right-click with your mouse and select "Inspect", and then go to the Console tab.
  3. Optional: Within the newly opened developer tools, navigate for a moment to the "Network" tab. Force refresh your browser. You should see a list of "requests" being sent, including one for each component library that needs loading. For example, if we named our component library "./libraries/my-component-lib.html", we'll see an additional request for this HTML file. If we had further split our files using "-src=", we'll see even more requests.
  4. Do you see a Modulo logo (%), with the word COMMANDS? Click on COMMANDS, and possibly one more time (on Firefox), and you should see a command menu, containing commands like build and test. To build, simply click the "build" command.

    Note: Look in the upper-right hand of your browser. You might see a warning, prompting you about "Allowing multiple downloads". You should allow it, otherwise it will block the generated files from being downloaded. If you see no warnings about this, then just continue to the next step.

  5. Your browser should reload, and then offer to download an HTML, CSS, and JS file, or perhaps offer a way to save them. These are the "build" files, and contain the contents of all libraries that were loaded on this page, with underlying JavaScript pre-compiled for speed and security. Save them all together in some spot.
  6. The HTML "build" is a copy of the HTML page you were working on, with two important differences:
  7. Difference #1: It's been modified to include the built versions of the CSS and JS: A link tag to include the CSS file is inserted before the </head>, and a script tag inserted before the </body> tag to include the JS file.
  8. Difference #2: It contains the page's current state, e.g., the result of an initial render. This means that when that HTML page is loaded, it will be "prerendered", and thus appear to load much faster, and components will render correctly, even before the JavaScript finishes loading.
  9. Now, open up your "build" file. If all went well, it should look and behave exactly as before, except load much faster.
  10. Tip: If all went well after building, you should see a link to the command you just ran. Clicking on the link will refresh the page, causing it to build again. You can also just hit refresh (Ctrl+R or Command+R) to rebuild. Consider keeping this window open, or as another tab, and resuming work in another window. Then, to rebuild, simply switch back to this window, and either click the button or hit refresh!

Important: Don't edit your builds! Built files are disposable. Don't edit the HTML, JS, or CSS files produced. Instead, continue working on your original "source" files, and then build again. The purpose of these new "built" files is to only share them when you are done with your website. These should only be used when "launching" your site. Your development should continue on the "source" files.

This can easily trip up beginners: The build "freezes" the component library in time, and you'll have to run build again if you change anything. A common mistake is forgetting that you are editing the original .html file while using the built .js version, causing much confusion as to why your changes don't take effect!

Integrating builds with existing projects

What can Modulo integrate with? Modulo was developed to be easy-to-integrate with existing code. In other words, it doesn't matter if you use WordPress or Django, Drupal or Rails, Jekyll or Hugo, Modulo components can be embedded into your page to add JavaScript-based interactivity and more convenient HTML development. Although probably less useful, you could conceivably even use it with JavaScript-based generators, such as Next.js or Gatsby—in the end, as long as JS and HTML is getting sent to a browser, Modulo components can be in the mix!

If you are experienced with other JavaScript build systems, such as those that are NPM-based (e.g. Webpack, Parcel, Rollup), you might be curious as to how to practically use Modulo's browser-based build system. Even if you aren't familiar with other build systems, you might (correctly!) think that in practice it must get hard to keep track of which JS files, CSS files, and pre-rendered HTML files should be included in the published version of the site, and further more that re-building after every change on every single page on larger sites could get tedious. Also, you might wonder how to integrate this process with pre-existing web apps, or apply it in a real-life web development team, as we've thus far only been talking about developing Modulo components in isolation. This is where workflow comes into play, i.e. the patterns and procedures you follow to keep a project humming along.

One useful workflow for integrating Modulo with existing projects is to have a sort of sandbox "testing page" or hidden internal-only "showcase" HTML page (i.e. similar to a "story book" or "design guide" of components). On this page, you develop and showcase your re-usable components, both to ease development, and also to serve as a demonstration for other members of your development team or organization.

From this "showcase" page, you can then run build to generate built JS files and CSS files whenever you have completed sufficient work on your components that you want to integrate and release into the rest of the project. The "self-packing" nature of these builds make the integration no harder than adding any other script or link tag. This works especially well for small projects, and avoids the need for a complicated automated build process for component libraries that you might only occasionally change. One downside of this workflow is that you might not be able to pre-render any of the pages you are integrating with, so there might be slight delays in mounting your components.

For example, see below for a complete page showing including a bundle in a hypothetical existing PHP project:

Part 2: Summary

In this tutorial, we learned how to specify props as attributes to use and configure other people's components and how to define the Props CPart which allows our components to be configured via attributes when used. We learned how to use Modulo's templating language to include variables and format values using filters, and finally wrapped up with trying Modulo's self-building feature.

Key terms

Next step

At this point, you've learned enough to be dangerous! If you intend to mostly use Modulo as a template developer, refactoring HTML into template-based web components, then you are all ready to get going. Feel free to try using Modulo to refactor your HTML, and be sure to test on all target browsers (Modulo is still under development, after all), and then finally use the self-packing feature to include HTML/CSS/JS that loads quickly. To improve your template development skills, you might want to skip learning about Script CParts (Part 3), and instead read deeper on the capabilities of the Modulo templating language, which you can use to build more complicated re-usable HTML.

However, if you are at least a little familiar with JavaScript and/or React-style SPA frameworks, and want to learn how to make more complex, interactive applications using State and Script, then get going with Part 3: State, Directives, and Scripting

Part 3: State, Directives, and Scripting »