In Part 2 of this tutorial, we'll explore three more features: the State CPart, Binding State Data, and finally Template Filters.
State
So far, our components have been static and unchanging. They can't do a lot. They might be useful for refactoring HTML into more D.R.Y., reusable components, but not much more than that. They can't validate form data, use APIs, or form complete applications. In fact, they can't have any sort of interaction whatsoever, or any dynamic or changing content.
Dynamic state
Let's put on our "Holmes-style" detective hats and get out our detective
magnifying glasses, we're about to go sleuthing! Examine the usage of this
<x-GuessingGame>
component, written with Modulo as well:
Guessing game time!
This demo component is doing much more than what we have covered in the tutorial so far. It is changing, or mutating based on user interaction. This is no longer a simple, static component, but a dynamic component.
We can deduce that this component used a State CPart. We know that it used a State CPart because it changes or mutates. Without a State CPart, a component cannot "change state", or mutate or have dynamic content, and instead will be rendered the same way every time (that is, assuming it has the same Props). In other words, if a component needs to have dynamic content or change over time, then a State CPart is necessary to store this dynamic content.
Examining privacy
State allows each component instance to store data. Each component instance has
a separate state from every other instance. Instead of using the complex
x-GuessingGame
example from before, examine the behavior of the following
x-HelloCount
counting buttons, which increment whenever clicked:
Counting time!
Once again, we can determine that state is being used, as the text on the button changes when that button is clicked. Furthermore, this demonstrates how state is not shared: Each button is a totally separate instance that keeps track of it's own separate number.
So far, in our sleuthing, we've accumulated two "CPart facts":
- State Fact: Used to make components change - A State CPart is needed if our component is mutable, or has dynamic, changing data or behavior
- State Fact: Private - Each State is private to each component instance (that is, they keep individual and separate copies of their state data)
Let's "peel back the layers" and examine out how these "stateful" or dynamic components were written.
State Variables
What are variables? If you are new to coding, you can think of the concept of variables as being like "buckets for data", or "blanks that can be filled in". They allow us to "give a name" to refer to data, allowing for more generic code and templates to be written. That is, the same code can have different results if the variables change.
Before we can modify data or content, we must first learn how to first create State and State variables, which is what we'll learn now. To add one or more State variables to our component, we must define a State CPart. State CParts are defined much like Props, except that instead of just listing the attribute names, initial values must be provided as defaults. A State CPart might look like this:
Here we are defining two state variables: count
, which we initialize to
equal "1"
, and color
, which we initialize to equal blue
. We can then use
the state variables in our Template, in a similar way to how we did with
Props:
Try it now
Practice modifying the State CPart (
<State>
) below and re-running to see how that affects the output.Practice incorporating these CParts into your own components on a real page by copying the code here and pasting it within your component definition (that is, the one that you created in the Part 1 of this tutorial)
State Binding
So far, we can manually change state in the source code, but our app is still not interactive, since the actual users (that is, non-coders) cannot change the state. This is where "binding" comes into play, where user input is "bound" to state, such that users of your app can also modify state variables while using your app.
To "bind", we'll need to use a directive. A directive is a type of HTML
attribute. You can recognize directive by spotting certain special characters
in the attribute name. For example, <input [state.bind] />
is an input
HTML
tag with a [state.bind]
directive. Directives are useful for a variety of
tasks, ranging connecting CParts to each other, to more complicated
modifications to DOM elements. For now, we'll only care about one directive:
[state.bind].
The [state.bind] directive
State and predictability The purpose of State is to separate out everything that changes about a component into it's own isolated data structure. It should be the case that for a well-written component, if anything changes visually, that "visual" change should always start with changing state. There should never be a "mismatch", or a way for visual changes to occur without state changes. If such a thing were possible, it would imply a component that is non-deterministic, or renders unpredictably. In other words, given a particular state (and props), a component should be predictable or deterministic in that it renders the same way every time.
State comes with a directive that helps "bind" it to form data. What does this
mean? You can attach a [state.bind]
directive to any <input>
, and the
State CPart will "sync up" the input with the state after every keystroke.
The binding is "two-way", or it goes in both directions: The input gets the
initial state value, and if the state ever changes, the input will be updated
to reflect that, and if the input ever changes, the state gets updated.
It's best practice to bind all of your form inputs that are in components to state variables. This is because in order to get the benefits of separating out state, all visual changes, including something as simple as typing a single character in an input, should be reflected in state changes.
To bind an input to state, use something like the following:
Try it now
Examine and try the following code:
Comprehension Questions
- Do you see how it "quickly reacts" or re-renders the sentence text as you type?
Multiple binds
State allows for multiple inputs to be bound in the same form. It's important that you always include a "name" attribute when binding. This should contain the name of the State variable to be kept in sync with that input.
Try it now
Examine the "silly story generator" demo below.
Do you see how the 5 different form fields are each bound to state, so that when they are changed, it "reacts" and re-renders the story at once?
Practice modifying the demo to add new variables and input.
Extra: Write your own silly story generator!
State and input types
State also allows binding of different inputs for different data types. For
example, you can use type="color"
for a color picker, or type="range"
for a
range slider.
Clarification: These are all just HTML5, built-in input types. To peruse a
full selection of input types, consider MDN's "The HTML5 input types" Guide in
their Learn Web Development
series.
Similarly, the other attributes, (e.g. name
, along with type
, max
, min
,
and step
), are also not Modulo directives, but are instead plain HTML
attributes documented in the MDN link. The State CPart will look at type
and
name
in order to behave appropriately, but will ignore the others.
Try it now
Examine the below code. See how it uses different types for different inputs for a "reactive" font selector / customizer?
Local file challenge: Practice incorporating State and binding by developing a form or similar application on your local page.
Comprehension Questions
Background: Earlier jQuery-style JS frameworks were more concerned with manipulating the DOM directly. Now, the modern approach is to combine templating and/or DOM building tools (e.g. JSX, virtual DOM) with "state management" (e.g. Redux, useState). Modulo closely follows this modern approach, where the "moving parts" of the application are put into state, and then by changing state, the component re-displays itself, showing the new data.
Why have state? What benefits does it have? Why not just modify DOM directly whenever you want to using JavaScript?
Why keep State separate for each individual component? Why is state private, instead of shared across all components?
Why use State binding? Why do we "bind" state to inputs? Why not use JavaScript to get the value?
Answer: This is due to perceived flaws with the original approach of direct DOM manipulation: Pretty soon as an app grows, you get a big tangled mess of different code reaching in different spots. Frontend frameworks clearly needed an "MVC Model"-like structure to "keep stuff separate".
This "detangles" the spaghetti mess of DOM manipulation: Instead of one button inserting stuff over here, and one input reaching in and sending data over there, the "state" creates a single "choke-point" that keeps data "flowing" in one direction. No matter what you want changed, you do one thing: Change state and rerender!
This also allows for quickly developing "reactive" forms. Furthermore, using Modulo in particular, it allows for declarative programming of reactive forms without use of JavaScript.
If you are still scratching your head over the use of [state.bind]
but are
familiar with vanilla JS or jQuery, it's all about reducing the need to
"manually reach" into the DOM. It simplifies code like this var inputData =
document.getElementsByName('myinput')[0].value
(or the similar
$('[name=myinput]').val()
in jQuery), with more readable code like
state.myinput
, and similarly untangles code for validation, API requests,
etc.
Template Filters
Built-in Filters and Custom Filters - Modulo comes with many filters pre-installed: Read Templating Reference - Built-In Filters for examples of all filters available. Utilizing JavaScript, you can also define custom filters. Read Templating - Filters for more information.
The Modulo templating language has two core features: filters (for formatting values), and template-tags (for control-flow). We will learn the first of these now.
Basic filters
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:
Filters can be combined together, one after the other. Think of them like a "transformation pipeline". For example:
Try It Now
Template filter arguments
Some filters can also take extra modifiers or options. This is called the
template filter argument. Do you recall how our previous x-ExampleBtn
component only supported either "round" or "square" as CSS classes? In this
next snippet, see how the |allow
template filter ensures that only "round" or
"square" are permitted:
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".
Try It Now
- Try modifying the "Username" input box in the preview below. Do you see how it "quickly reacts" or re-renders the username text in lower-case as you type?
- Try also adjusting the "Size" input and "Color" inputs to see how it updates the appearance the text.
- Local file challenge: Add a use of template filters somewhere in your local project. Can you make a simple calculator?
Comprehension Questions
- Identify every template filter in the sample code above.
Answer:
- It does a bit of pixel math on
state.size
using|add
and|multiply
, - The
state.team
variable is used with|allow
to only support "blue" or "green", and anything else turns a|default
of red - The
|lower
filter is used to force the username into lowercase
Part 2: Summary
In this section, we learned how to know when to use Modulo's State feature
(when there is changing data or appearances), and how to use it. We learned how
to use [state.bind]
directive to an input. We learned how to use Modulo's
templating language to include variables and format values using filters.
Key terms
- State - A CPart used to include changing data, which is used to render the HTML of the component
- Directive - A special type of HTML attribute that "hooks in"
functionality to otherwise plain HTML elements. One built-in directive
includes:
[state.bind]
- two-way binds inputs to state variables, so modifying the input modifies the variable, and vice-versa
- Template variable and template filter - Modulo's Templating language
can include template variables (e.g.
{{ props.name }}
) mixed in with the HTML, and possibly modified or reformatted by attaching template filters (e.g.{{ props.name|upper }}
)
Next steps
At this point, you've learned enough to be dangerous! In the next step, you'll learn more about more component render and CSS options, along with the first template tags, which will let you make useful, simple interactive widgets for websites.