Creating a Component
In this brief tutorial you will learn how to build a simple component.
This tutorial assumes readers have a working knowledge of JavaScript and fundamental software development concepts. If this does not describe you, consider taking a moment to review a JavaScript tutorial before continuing.
1. Setup
Goal: Create a boilerplate page and open it in a browser.
Create page from template
Let's begin with a basic HTML 5 template; copy it to a HTML file and open it in a browser.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Froyo Component Example</title>
</head>
<body>
<!-- content goes here -->
</body>
</html>
Add the root element
Add an element to the body content which will serve as the component's root element.
Every Froyo component is required to have a root element. The root element can be any element type and it is typically attached to the DOM.
<body>
<div id="root"></div>
</body>
Import Froyo from CDN
Add the following script to the bottom of the document to import the latest development version of Froyo.
<body>
...
<script src="https://cdn.jsdelivr.net/gh/marksmccann/froyo@latest/packages/froyojs/bundles/froyojs.js"></script>
</body>
Add script tag
Lastly, add an empty script tag to the bottom of the document. This is where we will be working.
<body>
...
<script>
/* scripts will go here */
</script>
</body>
2. Creating the Component
Goal: Learn how to define a component and get it to render some content.
Define a new component
Define a new component by calling the defineComponent
function. This will return a constructor for our component.
<script>
const FrozenYogurt = froyojs.defineComponent({});
</script>
Initialize the component
Now that we've defined a component, let's initialize it with the root element.
<script>
const FrozenYogurt = froyojs.defineComponent({});
const instance = new FrozenYogurt(document.getElementById('root'));
</script>
The first argument of the constructor alternatively supports a raw query selector. Let's shorten the statement to this:
const instance = new FrozenYogurt('#root');
Render something
We defined a component, but it doesn't do anything yet. Let's make the component render something in the root element. We do this by adding a method named $root
to the render option. By returning a string we can render text within the content of the element.
const FrozenYogurt = froyojs.defineComponent({
render: {
$root() {
return 'The best flavor is: Vanilla';
},
},
});
After initializing, the root element should look like this:
<div id="root">The best flavor is: Vanilla</div>
Declare the component state
Next, let's make the content dynamic. We do this by binding the content of the root element to the component's state. Declare a new state property by adding an entry to the state option called flavor
and set its default value to "Vanilla".
const FrozenYogurt = froyojs.defineComponent({
state: {
flavor: {
default: 'Vanilla',
},
},
});
Render something with state
Now, let's apply the newly defined state to the root element. Replace "Vanilla" with this.$state.flavor
to make the message dynamic.
$root() {
return `The best flavor is: ${this.$state.flavor}.`;
}
Now that the content of the root element is bound to the state, we can control it. To test this, pass an object as the second argument of the constructor and set the initial value of flavor
to "Chocolate".
const instance = new FrozenYogurt('#root', { flavor: 'Chocolate' });
As a result, the rendered output should now be:
<div id="root">The best flavor is: Chocolate</div>
3. Adding Functionality
Goal: Learn how make the component functional.
Update the initial HTML
Let's start by adding some content to the root element to work with.
While this content could easily be generated with JavaScript, it is a good practice for static content to be included manually in the HTML. This gives consumers the ability to customize the content and it will reduce the effects of cumulative layout shift which can have a degrading impact in SEO evaluations.
<div id="root">
<button>Toggle</button><br />
The best flavor is: <span class="text">Vanilla<span>.
</div>
Declare DOM nodes
To access these newly defined elements in our component, add two entries to the nodes option. Set the type
of each node to "query" and provide a selector
; this tells the component to search for a match within the root element and save it to the instance.
const FrozenYogurt = froyojs.defineComponent({
nodes: {
button: {
type: 'query',
selector: 'button',
},
text: {
type: 'query',
selector: '.text',
},
},
});
Rendering the flavor
Next, remove the $root
entry from render
so that we do not overwrite our new content. Replace it with an entry for our new text
node (every property declared in nodes
can have a corresponding entry in render
). Return this.$state.flavor
from the function to apply the value to its content.
const FrozenYogurt = froyojs.defineComponent({
render: {
text() {
return this.$state.flavor;
},
},
});
Add the event listener
Now, let's add an event listener to the button element. Just like render
, every property declared in nodes
can have a corresponding entry in the events option. Define the event listener by adding an entry to events
called "button". This property must be a function that returns an object of events. Add a "click" property to this object with an empty function as its value.
const FrozenYogurt = froyojs.defineComponent({
events: {
button() {
return {
click: () => {},
};
},
},
});
Update the state
To make the component functional, we need to update the state when the button is clicked. In this case, we want to toggle the value of flavor
between "Vanilla" and "Chocolate". We can do this by setting this.$state.flavor
directly.
button() {
return {
click: () => {
this.$state.flavor = this.$state.flavor === 'Vanilla' ? 'Chocolate' : 'Vanilla';
},
};
},
Put it all together
At this point, the component should be fully functional. The component should look like the following and the flavor should toggle when the button is pressed.
const FrozenYogurt = froyojs.defineComponent({
state: {
flavor: {
default: 'Vanilla',
},
},
nodes: {
button: {
type: 'query',
selector: 'button',
},
text: {
type: 'query',
selector: '.text',
},
},
events: {
button() {
return {
click: () => {
this.$state.flavor = this.$state.flavor === 'Vanilla' ? 'Chocolate' : 'Vanilla';
},
};
},
},
render: {
text() {
return this.$state.flavor;
},
},
});
4. Conclusion
Goal: Start building your own components!
You have now created a functional component with Froyo. The fundamental concepts you have learned are universally applicable and can scale to support very complex components. You can start building your own. However, there is more to learn. We encourage working your way through the following guides to learn more.