Developing a comoponent with SolidTemplateElement
What is SolidTemplateElement?
It is a class that can extend your component in order to purify your code of all useless complexity so that you can concentrate on the essential: your functionality.
Warning
To start this tutorial, you should have followed this first part.
What are we going to do?
For the example, we are gonna make a simple FAQ component. It just displays questions and answers in an accordion and allows the user to submit a new question.
Something like this:
Let’s suppose we want to make the recipient’s email customizable. To obtain this rendering, it would be enough to implement in our html page a component that looked like this:
<solid-faq
data-src="https://api.startinblox.com/faqs/"
recipient-email="alice@startinblox.com"
></solid-faq>
Note
Remember the ``data-src`` attribute ? This attribute hosts the data source you want to interact with in this component.
Note
Want to learn more about web components ? We recommend this introduction.
Let’s start :)
1. Set the base of you component in a solid-faq.js file
Create a component that extends SolidTemplateElement.
Here is the minimum code your component must contain that extends SolidTemplateElement
.
/** * solid-faq.js */ // Import SolidTemplateElement import SolidTemplateElement from "https://unpkg.com/@startinblox/core@0.10/dist/solid-template-element.js"; // Name your component and extend SolidTemplateElement export class SolidFAQ extends SolidTemplateElement { constructor() { super(); } // Define the attributes you want static get propsDefinition() { return { dataSrc: "data-src", recipientEmail: "recipient-email", }; } // Pass your attributes to your template template({ dataSrc, recipientEmail }) { // If we have no data sources, we display nothing if (!dataSrc) return ""; let tmpl = ` <solid-display data-src="${dataSrc}" fields="question, answer" id="faq" ></solid-display> `; // Otherwise, set the possibility to submit a question if (recipientEmail) { tmpl += ` <a href='mailto:${recipientEmail}?subject=A%20new%20question%20for%20the%20FAQ&body=Hi!'> Your question question not here ? </a> `; } return tmpl; } } customElements.define("solid-faq", SolidFAQ);
2. Pay attention to propsDefinition method
You are going to set your attribute in this method. recipientEmail is the parameter where we are going to fill in the email of our recipient.
static get propsDefinition() {
return {
dataSrc: 'data-src',
recipientEmail: 'recipient-email'
}
}
Note
Note the syntaxe convention => recipientEmail: ‘recipient-email’
3. Let’s focus on the template
The template contains the HTML you want to render. Pass your attributes to your template and write it. To display the questions and answers, we are going to use solid-display. The attributes fields is used to define which datas you want to display from your data source.
We add a conditional rendering: if no email is filled in, the possibility to submit a question is not displayed.
// Pass your attributes to your template
template({ dataSrc, recipientEmail }) {
// If we have no data sources, we display nothing
if (!dataSrc) return "";
let tmpl = `
<solid-display
data-src="${dataSrc}"
fields="question, answer"
id="faq"
></solid-display>
`;
// Otherwise, set the possibility to submit a question
if (recipientEmail) {
tmpl += `
<a href='mailto:${recipientEmail}?subject=A%20new%20question%20for%20the%20FAQ&body=Hi!'>
Your question question not here ?
</a>
`;
}
return tmpl;
}
4. Set fake datas
Creating data sources is quite another matter. For the example, we will use static data in JSON-LD.
Create a file named data-faq.jsonld
at the root of your project and put these datas:
{
"@id": "",
"@type": "ldp:Container",
"ldp:contains": [
{
"question": "What is Startin'blox ?",
"answer": "A cooperative and a technology to build the web of our dreams",
"@id": "questions-1",
"permissions": []
},
{
"question": "What is the SOLID project ?",
"answer": "A set of standards that allows web applications to all speak the same language and become interoperable.",
"@id": "questions-2",
"permissions": []
}
],
"permissions": [],
"@context": "https://cdn.happy-dev.fr/owl/hdcontext.jsonld"
}
Note
If you want to know more about how our API looks like, have a look to our SOLID introduction.
5. Implement your component
Import the script of your component and set the component in your index.html
.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!--Import a custom font-->
<link href="https://fonts.googleapis.com/css2?family=Montserrat&display=swap" rel="stylesheet">
<!--Import the framework-->
<script type="module" src="https://unpkg.com/@startinblox/core"></script>
<!--Import the component-->
<script type="module" src="/solid-faq.js"></script>
<title>Solid FAQ Demo</title>
</head>
<body>
<h1>Solid FAQ Demo</h1>
<!--Import the component-->
<solid-faq
data-src="data-faq.jsonld"
recipient-email="alice@startinblox.com"
></solid-faq>
</body>
</html>
6. Test your component
npm run serve
You should be able to display your data but at the moment it’s a bit ugly. Let’s add some style.
4. Implement JS and CSS in your component
Create a js file, like /js/main.js
.
Add the JS you need to make your accordion work, like this:
/**
* js/main.js
*/
// Select the component
var component = document.getElementById("faq");
// We use populate event to detect when the component is generated
component.addEventListener("populate", (event) => {
// Seclect each question
var acc = document.querySelectorAll("solid-display-value[name='question']");
var i;
for (i = 0; i < acc.length; i++) {
//For each question, if we click..
acc[i].addEventListener("click", function () {
// Add or remove the "active" class
this.classList.toggle("active");
// Select the answer just below the question
var panel = this.nextElementSibling;
// If the answer is opened, then close it
if (panel.style.maxHeight) {
panel.style.maxHeight = null;
// Otherwise, open it.
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
});
}
})
Note
Did you notice the populate event? It’s an event that allows you to trigger javascript only after the component you want to manipulate has been generated. See the Event documentation for more explanation.
Here is the CSS used for the demo:
/**
* css/main.css
*/
body {
font-family: 'Montserrat', sans-serif;
background-color: #4475B8;
}
h1{
color : white;
}
solid-faq {
max-width : 700px;
}
solid-display-value[name='question'] {
cursor: pointer;
padding: 18px;
text-align: left;
border: none;
outline: none;
transition: 0.4s;
display : block;
background-color: white;
color : #4475B8;
}
solid-display-value[name='answer'] {
padding: 0 30px;
max-height: 0;
overflow: hidden;
transition: max-height 0.2s ease-out;
display : block;
background-color : #ECECEC;
color : #414141;
border : 1px #FDD17A solid;
line-height: 30px;
}
solid-display-value[name='question']:after {
content: '\02795';
font-size: 13px;
float: right;
margin-left: 5px;
}
solid-display-value[name='question'].active:after {
content: "\2796";
}
solid-faq solid-display+a{
color : white;
line-height : 50px ;
}
Use Helper functions
SiB framework provides you helpers functions to add JS and CSS in your component.
Add at the begin of your solid-faq.js
, import your JS and CSS with those functions:
...
// Import Helper functions
import {
importCSS,
importJS,
} from "https://unpkg.com/@startinblox/core@0.10/dist/libs/helpers.js";
// Use the Helpers functions
importJS(`./js/main.js`);
importCSS(`/css/main.css`);
export class SolidFAQ extends SolidTemplateElement {
...
It should be better now, no ?
7. Translate your component
Warning
This part is not working and need improvement. You can jump to the step 8 :)
To translate the static strings of your components, follow these steps:
In your component, create a folder which contains all the translation files. You can name it
locales
for example. Inside, create one file per language, with the structure[code_language].json
, for example:fr.json
.In each file, add one line per string to translate. Your file should look like this:
{
"label.question": "Your question is not here ?"
}
In the constructor of your component, define the path of your folder:
// For the demo
const base_url = "./";
// The production url of your component =>
//const base_url = "https://path/to/your/component";
export class SolidFAQ extends SolidTemplateElement {
constructor() {
...
this.setTranslationsPath(`${base_url}/locales`);
}
...
}
Use the
localize
method to show the translated strings in your template:
const base_url = "https://unpkg.com/@startinblox/solid-faq"; // url of your component
export class SolidFAQ extends SolidTemplateElement {
...
template( { dataSrc, recipientEmail } ) {
if (!dataSrc) return '';
let tmpl = `
...
<a href='mailto:p.${recipientEmail}?subject=A%20new%20question%20for%20the%20FAQ&body=Hi!'>
${this.localize('label.question')}
</a>
`;
return tmpl
}
}
As a developer who uses a component, you can also add you own translation files by targeting you translation folder. Like for the component, this folder should contain one file per language:
<solid-conversation
data-src="./data/conversations.jsonld"
extra-translations-path="http://my_app/locales"
></solid-conversation>
8. Does it work well?
If you get any trouble (or any idea to improve!), please mail me :)
Note
Document your component!
Each time you create a component, remember to document it with a README.md, especially the version of the core with which it was developed. If you create a component by default you will be considered as the maintainer. This means that you are responsible for its future updates but also that you could get support contracts on this component in the future :)
Releases are posted here. Subscribe to the newsletter not to miss the next ones!
Warning
This tutorial could be improved by adding accessibility.
Go Deeper
Discover other features of the framework playing with the demo on the website.