Introduction

Welcome to the Building Apps with Modulo tutorial!

By following this short tutorial, you can learn how use Modulo to organize growing web application projects, inclduding single page apps and Jamstack-style multi-page static sites.

DOCUMENTATION ALPHA NOTE: This tutorial is incomplete. This section needs better explanations, and future sections are missing content. 🐛

Simple Web Server: SimpleWebServer Don't have a local development server? Consider Simple Web Server, which is a tiny, easy-to-use development server: Free and open source desktop app for Windows, macOS, and Linux

Prerequisites

Start with Ramping Up - This is the second Modulo tutorial. This tutorial recommends you first complete most if not all of the first tutorial (Ramping Up With Modulo) before you start this one.

In addition to that, the following are prerequisites for taking this tutorial:

  • Software: A local development server (see right), a code editor (such as Geany, Notepad++, or VSCode)
  • OS: Apple macOS, GNU Linux, or Microsoft Windows
  • Coding skill prerequisites: Knowledge of HTML & CSS, comfortability editing code in a text editor. Familiarity with React, Vue, or similar frameworks is helpful due to overlap in concepts, but not required.

Ready to start building bigger and better apps with Modulo?

👷 🏗️ 📦 Let's BUILD!

Modulo Source

In the Ramping Up With Modulo, we learned how to add a component embedded on the same page that it is used. This is not recommended. Instead, you should put your components in a separate component library HTML file, and then importing them into each HTML file that needs them.

This can be done by simply copying the insides of the <script Modulo … tag, and pasting them into a new file. Then, you must include a special -src attribute to specify the new file. See below:

<script Modulo src="https://unpkg.com/mdu.js" -src="/some/path/to/my_components.html" ></script>

This "4-liner" can now be included in any of your HTML files to import whatever components you have defined in /some/path/to/my_components.html. This allows you to share the same components across multiple pages.

Where to put your components?

Modulo recommends your "top level" components, or your first or main components, be defined in /static/index.html. The /static/ directory is chosen because it is often the one used for files that should not get static-site generated. Using that filename makes your "boilerplate" on each file that needs them becomes as short as possible, since the index.html is the standard default.

Boilerplate variations

Single lines (with unpkg CDN)

Following the previous examples, using index.html and the default scheme (protocol), the boilerplate of a Moudulo HTML page becomes a "one-liner", just at the 80 character limit:

<!doctype html><script Modulo src="//unpkg.com/mdu.js" -src="/static/"></script> <x-App> <div>Lorem Ipsum</div> <!-- etc... --> </x-App>

CDN - CDNs are Content Delivery Networks, and generally the system by which static assets are distributed across web servers for speedy websites. In our case, we care about JavaScript CDNs that let us quickly load Modulo.js and/or third-party JavaScript libraries from the NPM.js repository, without having to use NPM the command-line client.

Multiple lines (with unpkg CDN)

Some might prefer shorter lines and pinning a version number (which, regardless of, formatting, is always wise when possible!):

<script Modulo src="https://unpkg.com/mdu.js@0.0.71/src/Modulo.js" -src="/static/" ></script>

Locally hosted variation (single line)

In the previous tutorial, and when you use npm init modulo or a .zip starter template, the 2000 line Modulo.js file will be included locally (and thus also pinned). See below:

<!doctype html><script Modulo src="/static/js/Modulo.js" -src="/static/" ></script>

Summary

  1. Move your components from an embedded definition to a separate file so it can be share between pages
  2. Name the file static/index.html (i.e., the "folder name" is "static")
  3. Include the file with -src="/static/"

Let's practice!

Learning Tips
  • Tip 1: The Try It Now instructions tell you to apply the concepts you learn. Most will have sample code to copy and paste. The goal is to edit the sample code correctly and so that you can apply the lesson you learned on a app you develop using your local developmet server on your computer. That is, put the learning into practice!

  • Tip 2: Either use the starting file below, or consider trying each concept on an app you are building.

Starting File: Click the SAVE button under the RUN button to have a "testing file" to use in this tutorial.

<Props text ></Props> <Template> <h1>{{ props.text|default:"Modulo App" }}</h1> <hr /> <p><button @click:="script.doCountup">COUNT</button></p> <p>{{ state.num }}</p> <slot></slot> </Template> <State num:=0 ></State> <Script> function doCountup() { state.num++; } </Script> <Style> :host { --light-shading-color: #ffffff55; --dark-shading-color: #00000015; display: block; background: var(--background-color); } :host, button { border: 5px var(--primary-color) ridge; font-size: 2rem; text-align: center; box-shadow: 8px 8px 10px inset var(--light-shading-color), -8px -8px 10px inset var(--dark-shading-color); } h1 { margin: 0; padding: 0; } button { background: var(--primary-color); color: var(--background-color); font-weight: bold; border-style: outset; display: inline-block; } button:active { border-style: inset; } </Style>
Try It Now

Practice cutting out an embedded component and moving it to a static/ directory.

  1. Create a new "static" directory (i.e. folder) to house your component library files.
    • Ensure it's static, all lowercase, so the tutorial code snippets will match
  2. Create a new file called index.html to hold your Component code:
  3. Copy your existing component definition from your embedded HTML file into this new file, and then delete it from your main HTML file.
  4. Add a -src= attribute to your Modulo script tag, pointing toward the static directory, e.g. -src="/static/"
  5. Refresh the web browser to view the results.
    • If done correctly, nothing should change compared to the component being embedded, i.e. it should have the same behavior if it is being loaded from another file
    • To confirm it's working, try editing your component in the library file and refresh to see the results. Try force refreshing if you have trouble with the file changes when you save not being noticed.
RESULTS
  • If done correctly, your new component library file (e.g. /static/index.html) should contain the following text (note the lack of <script Modulo ... etc):

    <!-- Component library (/static/index.html) --> <Component name="App"> <!-- ... snip ... --> </Component>
  • For example, if you named everything based on the above suggestions, your new Modulo tag / new might look like:

    <script Modulo src="https://unpkg.com/mdu.js" -src="/static/" ></script>
  • Now, you can simply "drop in" these 4 lines of script tag on multiple HTML pages, and they will be all you need to import and share the same set of components between pages.

Bonus Challenge
  • Try practicing with multiple "main HTML" files (e.g. /index.html or /about.html or "Modulo_App.html" or something) sharing a single Component library (e.g. /static/index.html)

  • Try defining multiple components in one Component library HTML file


CPart Source

In the previous section, -src= was used on a <script Modulo ... tag to move components into a separate file. While this is enough for a few Component definitions, pretty soon, once you start writing dozens of components, you'll be back to having one big, unwieldy file to edit.

This is why there's a feature of Modulo to "split off" CParts (individually) into separate files. This allows you to work with dedicated .html, .css, and .js files separately, instead of embedding everything into a single, unwieldy HTML file. Thus, the next thing to learn is how the -src= attribute can be used on anything in Modulo, notably individual Component Parts.

Splitting off CParts

Splitting off definitions - Any definition in Modulo can be "split off" into a > separate file. Begin by cutting out the contents, and pasting them in a new file, and then save this new file in somewhere in your static directory. Finally, to re-include it, add a -src= attribute to CPart, just as we did with the Modulo tag. Note the dash (-) before it. This is what distinguishes it from an ordinary src attribute, which Modulo will ignore. By default, -src= is supported by any definition that uses it's contents, so that means Template, Style, Script, StaticData, Component, Modulo, along with ones we'll cover later, such as Library, Configuration, and Artifact.

Examples of using "-src="

For example, if we start with:

<Template> <p>It's template time!</p> </Template> <Style> p { color: blue } </Style>

To "cut out" these CParts, we…

  1. Copy <p>It's template time!</p> into it's own separate file (for example) called my-template-stuff.html, and…

  2. Copy p { color: blue } into it's own separate file (for example) called stylish-paragraphs.css, and finally…

  3. We replace the content with -src= attributes pointing to the files we made.

Once we're done, the component would look like this:

<Template -src="./my-template-stuff.html" ></Template> <Style -src="./stylish-paragraphs.css" ></Style>

Paths and "-src="

The rules for -src= relative paths are similar to HTML's rules: Any URI path can be specified, and use a dot (.) to specify a path relative to the current HTML file (e.g. the Component library file). The behavior is the exact same as if you had included the text in the same file, meaning this feature is only intended to be used when a component library gets too long and difficult to edit. In that case, conventional usage is to start splitting off larger CParts into their own files, until the HTML library file itself is just tie-ing everything together.

Try It Now

Practice splitting your components into separate HTML, JS, and CSS files in a static directory

  1. Create a new components directory in your static directory
    • Ensure the full path, then, is static/components, so the tutorial code snippets will match
  2. One by one, snip out the HTML, CSS, and optionally JS code from each Component you have defined in index.html
  3. Create as many as 3 new files per component in static/components/
    • Name each after the component with a .html, .css, and .js file extension, one for each CPart (Template, Style, and Script)
    • Paste in the content of each CPart
  4. Add a -src= attribute to each of the 3 CParts
  5. Refresh the web browser to view the results.
    • If done correctly, nothing should change compared, so to confirm it's working, try editing your CParts in their individual files
RESULTS

If done correctly, your new component library file (e.g. /static/index.html) should contain code that looks similar to the following code:

<!-- Component library (/static/index.html) --> <Component name="App"> <Props text ></Props> <Template -src="./components/App.html" ></Template> <State num:=0 ></State> <Script -src="./components/App.js" ></Script> <Style -src="./components/App.css" ></Style> </Component>
Comprehension questions
  • How does this technqiue cause the component library file (components/index.html) become more clean and easy to use?
    • Answer:
    • With this technique index.html file, will mostly just be the "header" or high-level information on each Component
    • The "guts" of each component, which is what developers spend most of their time editing, will go into separate files with appropriate extensions

CDN Source

Unpkg vs other CDNs - The Modulo documentation uses the unpkg CDN. You can use any CDN that supports serving JavaScript. A couple other popular alternative CDNs which should work with Modulo include: Skypack, JSDelivr and CDNJS

The final use of the -src= is to quickly bring in JavaScript from NPM. Don't worry, even if you don't have NPM installed locally, you can still use this feature, thanks to use of the unpkg.com CDN, which in turn fetches NPM packages so we don't have to (see aside).

JavaScript dependencies as -src

Including a -src= attribute will bring in that source code and execute it in the current script static context. That means it's private to that script, and can be used as a helper function there.

Here's a demo of this from the ModuloJS homepage:

<!-- This component generates a "type-as-you-go" Markdown preview. It uses -src= as a quick way to bring in the needed NPM dependency from the unpkg CDN. --> <Template> <textarea [state.bind] name="text"></textarea> <div>{{ script.html|safe }}</div> </Template> <State text="### Markdown Using `-src` to *quickly* add a **markdown parser**" ></State> <Script -src="https://unpkg.com/snarkdown"> const { snarkdown } = this; // (this === window) function prepareCallback() { return { // Every rerender, convert markdown to HTML html: snarkdown(state.text), }; } </Script> <Style> textarea, div { border: none; width: 95%; padding: 2%; min-height: 80px; } </Style>

Using Configuration

A better solution is to use a Configuration Definition to integrate third party javascript. Configuration definitions are not the same as a Script CPart. It will run before everything else, and is great for bringing in definitions using the -src= attribute. These should go along side your components, above everything.

Registering a util with Confgiuration

See this example, which is the code used by the template we'll use in Part 2:

<script Configuration -src="https://unpkg.com/showdown/dist/showdown.min.js"> // Register "getMarkdownData" function, using "showdown" to convert // markdown to HTML (including parsing meta data) function getMarkdownData(source) { const converter = new showdown.Converter({ metadata: true }); const html = converter.makeHtml(source); const data = converter.getMetadata(); data.body = html; //Object.assign(String(html), { safe: true }); return data; } modulo.register('util', getMarkdownData); </script> <Component name="App"> <!-- ... etc ... --> </Component>

Registering as templateFilter

Sometimes its very convenient to expose tools as template filters that you can directly use them in your HTML templates. This can require very little code. See below:

<script Configuration -src="https://unpkg.com/snarkdown"> modulo.register('templateFilter', snarkdown); </script> <!-- ... etc ... --> <Template>{{ state.text|snarkdown|safe }}</Template>

Integrating a frontend plugin

One of the most classic JavaScript libraries is CKEditor, a rich text editing system. As an example, this is very easy to initially integrate with -src= into a Modulo-powered Web Component.

For this final example, we can practice doing it yourself, by following the example procedure described below.

Try It Now

Can you integrate the CKEditor so it displays by following the two clues below? If you get stuck, try the embedded solution at the end.

<Script> // Step 1: Fill in -src="" function updateCallback() { // Step 2: Add code to integrate //console.log(element); } </Script>
Edit me with CKEdit!
Clue 1

The CKEditor CDN website provides the following example:

<!DOCTYPE html> <html> <head> <title>CKEditor</title> <script src="https://cdn.ckeditor.com/ckeditor5/39.0.2/classic/ckeditor.js"></script> </head> <body> <div id="editor"></div> <script> ClassicEditor .create(document.querySelector('#editor')) .then(editor => { console.log(editor) }) .catch( error => { console.error(error) } ); </script> </body> </html>
Clue 2

Steps to integrate: We can transform many front-end libraries into a Modulo component using the following 2 steps:

  1. Take their provided CDN tag and put it in the -src= of a Script CPart
  2. Copy the example script tag and change references (e.g. often id selectors) to DOM to simply element (so it attaches to the Component's host element)

Embedded Demo Limitation: Due to the way the TRY IT NOW embedded "mini-demos" don't attempt to "sandbox" very much, and the way that CKEditor sets up it's own JavaScript data structures globally, it will break if you attempt to re-run it over and over in the same embedded mini-demo. Try using the ACE-based demo, or working locally if that becomes a nuissance.

SOLUTION

When these two steps are followed with the clues provided, we get the following functioning component:

<Script -src="https://cdn.ckeditor.com/ckeditor5/39.0.2/classic/ckeditor.js"> function updateCallback() { ClassicEditor .create(element) .then(editor => { element.editor = editor; // in case we need it later! }) .catch(error => console.error( error )); // (Optional) Prevent component from rerendering // element.rerender = () => {}; } </Script>
Edit me with CKEdit!

Alternative integration: Using mount references

In most cases, you might prefer to mount within the element, so you can add extra styling or other HTML to more properly "wrap around" it:

<Template> <div [script.editor]></div> </Template> <Script -src="https://cdn.ckeditor.com/ckeditor5/39.0.2/classic/ckeditor.js"> function editorMount({ el }) { ClassicEditor .create(el) .then(editor => { element.editor = editor; // in case we need it later! }) .catch(error => console.error( error )); } </Script>

Alternative integration: Using Configuration and Mount

Perhaps the best case is using a Configuration script, and moving it out of the Component entirely:

<script Configuration -src="https://cdn.ckeditor.com/ckeditor5/39.0.2/classic/ckeditor.js"> modulo.register('util', ClassicEditor, { name: 'ClassicEditor' }); </script> <Component name="App"> <Template> <div [script.editor]></div> </Template> <Script> // We can bring in classes registered this way as such const { ClassicEditor } = modulo.registry.utils; function editorMount({ el }) { ClassicEditor.create(el) // Etc, same as before } </Script> </Component>

Part 1: Summary

In this tutorial, we learned how to keep our components in a component library and then load that library into different HTML files.

  • -src= - A special attribute that allows content in Modulo to be loaded from separate files, making it easier to manage as projects grow.

  • CDN - A third-party server that hosts JavaScript for us, often notably NPM packages that are ready for the brwoser

  • Configuration - A type of CPart that is run before everything else, and is useful for registering things like new global functions or template filters

Continue to Part 2:

Part 2: Jamstack, Markdown, and CMS »