Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Great! You’ve made your first widget. Now, let’s take a step back to the General tab.
Here, you will find three sections: General Settings, Availability Settings, and Content Security Policy.
This section is straightforward; here, you can rename the widget, but you can only rename it.
This section indicates whether the widget is available for any of the following three WEM applications: the WEM web application, the WEM Android application, and the WEM iOS application.
For example, if you have a WEM web application and a widget that only supports Android, that widget will not be available for that project, even if you have imported the library it resides in.
You can always return to a widget to make it compatible with other platforms.
CSP (Content Security Policy) is a security feature implemented in WEM and web applications in general to help prevent various types of attacks, such as Cross-Site Scripting (XSS) and data injection attacks.
Any external sources that the widget uses should be included in the CSP. For instance, if you have the following HTML element:
That image will not load when CSP is enabled in your WEM portal unless you add the hostname https://some-website.com
as an img-src
directive in the CSP settings overlay behind the "Edit settings" button.
CSP allows web developers to specify which content sources are trusted and can be loaded by the browser when rendering a web page, thereby making the WEM application safer.
As of WEM 4.2, Content Security Policy features have been added. The widget documentation was written before this update, and some example code may not be compatible. This is one of the unfortunate side effects of enhancing application security. For now, to keep things simple, we will not use CSP in our examples. Just ensure that your WEM portal has CSP disabled at the moment for the example code to work.
We will delve deeper into these CSP settings later on.
A veteran web developer knows that there are multiple ways to style HTML elements. For example, this can be done statically by including a <link rel="stylesheet" href="style.css">
, or dynamically via JavaScript with element.style.backgroundColor = "tomato"
, to name a few. Yes! "Tomato" is, unironically, the best red color there is, in my personal opinion!
One way to style widgets is by using the Less editor under the Styling tab. Less is a CSS preprocessor that provides additional functionality on top of CSS. If you prefer not to write in Less, you can still write standard CSS without using any Less features.
If you do want to write in Less, you will also have access to built-in variables from Bootstrap 3, which can be very handy if you want to customize some of their components in a widget. You can find these variables in the right-side panel of the screen.
In this chapter, we are going to write a simple message box. If you are familiar with Bootstrap 3, you know they have an Alert component available out of the box, and we provide that as one of our base components. However, for the sake of this exercise, we will write one from scratch.
Create a new widget called "Message" in the "Widget Academy" library.
Copy and paste the following script:
Create a Literal Text Property called "Text" and place it in the General section.
Create a Dropdown Property called "Style" and place it in the Appearance section, with the following labels: "Info," "Success," and "Danger," and the corresponding values: "info," "success," and "danger." Note that the values are case-sensitive and should be in lowercase!
Finally, click on the Styles tab and copy and paste the following Less code:
Note that we are using the predefined Bootstrap branding color variables to ensure this widget adopts the color styles of the selected design template in a portal, making it look nice across various design templates. Additionally, we use two Less color functions: lighten()
and darken()
to differentiate the branding colors from the background, border, and text color of the message box.
When learning a new language, it is almost traditional to create a "Hello, World!" program as your first project. Let's do that too! Along the way, I will skip a few details here and there, but don't worry; I will explain them in detail in the upcoming chapters. So, let's create our first widget!
To create a widget, we need to open the Widget Editor and create a widget library first.
Open or create a new WEM web project.
Select the "Templates, widgets, files, and hyperlinks" tab in the project tree.
Right-click on "Widget libraries" to display the context menu.
Click on "Open widget editor."
To create a widget, we first need to create a widget library:
Navigate to your workspace in the widget editor tree.
Right-click on your workspace to display the context menu.
Click on "Create library."
Let's name the library "Widget Academy."
Now let's create the widget:
Right-click on "Widget Academy" to display the context menu.
Click on "Create widget."
Name the widget "Hello, World!"
Double-click on the newly created widget to open it.
Click on the Script tab.
Write the following HTML code in the code editor:
Press Save.
Congratulations! You have created your first widget! But how do you see it in action? To view it, we need to add the widget library to our project. Once we do that, we can add the widget to the template in the template editor.
Select the "Templates, widgets, files, and hyperlinks" tab in the project tree.
Right-click on "Widget libraries" to display the context menu.
Click on "Add widget library."
Select the "Widget Academy" library.
Press OK.
Now, let's place the widget on a template:
Create a new flowchart.
Create a new interaction node.
Drag and drop the "Custom" ribbon item (the one with the rocket) at the top of the template.
Expand the "Widget Academy" library.
Select the "Hello, World!" widget.
Press Select.
Save the template.
Now, preview this interaction node, and you will be greeted with the words:
"Hello, World!"
This widget may not be anything special. We didn't even write any WEMscript, nor did we discuss binding with the WEM data model. I skipped a lot of details. But if you’ve made it this far, then let the fun begin!
I believe many programmers are familiar with the classic "Hello, World!" program.
When I was around 13 years old, I wrote my own "Hello, World!" program in QBasic 1.0, which came installed with MS-DOS on our computer. I remember changing the text "World" to my own name, "Vincent." After pressing F5 to run the program, I was thrilled to see my computer greeting me:
I was amazed! I felt the power in my fingertips and wanted to learn more. I used a dial-up modem to connect to various bulletin board systems (BBS) in search of QBasic source codes to study. My parents were not always pleased, as calling on a phone line was expensive back in those days. Not to mention, no one could use the phone while the modem was connected. And let's not forget the times when I wanted to download the latest shareware demo of a game that was just a few megabytes! If someone accidentally picked up the phone, I would have to start the download all over again. Good times!
Although WEM is a no-code platform, I love writing code. Creating widgets is for those who enjoy programming. If you are making widgets, you are engaging in traditional programming. You could even say that if you create widgets, you can call yourself a web developer, as building advanced widgets requires extensive knowledge of HTML, CSS, and JavaScript.
In this module, we will teach you the basics of building a functional widget. We will start simply by creating a "Hello, World!" widget, then extend it to make it more interactive, and eventually conclude with a fully functional widget.
Creating widgets opens up an unlimited array of possibilities, but it can also be quite challenging. I hope that by building widgets, you will feel as excited as I was when I wrote my first programs.
When we talk about widget events, we refer to a unique set of WEMscript that can follow a button click, execute a flowchart, navigate to a navigation item, or refresh the screen. It is also possible to change the view state, which we will discuss in later chapters.
To introduce events, let's create a small virtual dice that will output a random number between 1 and 6, along with a "Roll again" button that will trigger an event to refresh the screen and display a new random number. Copy and paste the following script, and let's dissect what is happening:
It is important to inform the WEM runtime that we want to run a specific event by registering it. The register event
statement does exactly that, and it is mandatory for an event to function properly. Based on the previous chapters, the second line should now be clear regarding its purpose. The third line is interesting and requires some explanation.
Before I explain that, let's create the refresh screen event first:
Select the Events tab.
Click on "New event."
Name it "RefreshScreen."
And that's it! There is no need to write any event code for a refresh screen event. We will delve deeper into this in later chapters. Now, let's continue with the interesting part on line 3:
A veteran web developer will recognize the onclick
attribute. What is interesting is the <?attr
, which I briefly explained in previous chapters; it is a shorthand for HTML attribute encoding the output. This is important because, without it, we could potentially create invalid HTML. The @RefreshScreen
is another interesting aspect; it is not a number variable, a boolean, or text, but rather an event. You may wonder how you can output an event. What gets output is JavaScript code that will trigger the @RefreshScreen
event, and it will look something like this:
Don't worry; you never have to call the WEM Runtime API directly, as it is abstracted from the widget developer. This is just to illustrate what is happening under the hood. Note that it does not matter where you trigger an event; it could also be in an ondblclick
attribute, an onblur
event, or even manually called in JavaScript.
In our previous chapters, we created a simple static widget. Now, let's make it a little more dynamic by adding properties to the widget. These properties are similar to any other property you see in the user interface of the modeler, such as the Name of an interaction node, the Validation of a text data field, or an Action on an assignment node.
Let's create a widget that provides a more personal greeting:
Inside the "Widget Academy" library, create a new widget and name it "Hello, You!"
Select the Scripts tab and paste the following code:
There are a few new things happening here, so let’s unpack them.
The <? ?>
symbols denote code blocks, where you write WEMscript. The code within these blocks is executed server-side and is not included in the output. In the first line, we declare a variable using the var
statement. For those familiar with WEM expressions, you’ll notice that we convert the name to uppercase using ToUpper(@Name)
. Yes! In WEMscript, you can also write WEM expressions! In the second line, we use the print
statement to output the contents of the variable @uppercasedName
. Notice the html
argument provided to the print
statement; this will HTML encode the output. For example, <
will be output as <
. If we don't HTML encode it, we risk breaking the HTML or, even worse, allowing a hacker the opportunity to inject malicious code, something we will discuss in depth in later chapters.
You may be wondering if writing <? print html ... ?>
will become tedious over time. Fortunately, WEMscript has a shorthand for that: <?= @uppercasedName ?>
. Our updated code now looks like this:
This is much shorter and more convenient! There are also other shorthand notations, such as <?js
for JavaScript and <?attr
for HTML attribute encoding, which will be discussed in later chapters.
Now, you may notice that when you validate the widget (try it now by pressing the validate button next to the save button), an error will appear: "An unknown variable is used." This is because we have not yet added @Name
as a property.
To add the property:
Select the Properties tab.
Click on "New property" in the toolbar.
Name the property "Name."
Set the property type to "Text."
Set the input type to "Literal."
Set the property group to "General."
We will go into much more detail about the different property types and settings in later chapters. For now, we are ready to test our new widget. Place this widget on a template, select it, and you will notice that a Name property is visible in the right panel. Fill in the property with a value, such as your name, save the template, and preview it.
If you are familiar with the Modeler and creating page templates, then you are probably acquainted with components such as Alert, Panel, Conditional, and others. To create a similar widget, we need an additional feature called Placeholders. This feature allows you to reserve a section in your widget for content that you can add in the Template Editor within the Modeler. Let's create a simplified Alert component to explain the concept of placeholders.
Create a new widget called "Information Card."
Select the Placeholders tab.
Click on "New placeholder."
Name it "Contents."
You can provide a label and a description if you wish; these will be displayed in the Template Editor.
Copy and paste the following script:
To keep this chapter simple, we are reusing the Alert Bootstrap classes. Other than that, there is one new statement here called render
. As the name implies, this statement will render the contents of the @Contents
placeholder. And that's it!
Now, place this widget on a template and notice that there is an additional container that was not previously shown in the other widgets we created. Try dragging some components, adding some text, or including other widgets in this container.
As you can see, placeholders are very easy to implement and incredibly powerful.
To maintain the state of the scroll position of an HTML element, the zoom position of a chart, or any state that is specific to the view when the widget needs to be redrawn, we can use the view state. This state is accessible when the widget is rendered on a template but is destroyed when the widget is no longer rendered. We will discuss this in more detail in later chapters. For now, let's extend our previous "Message" widget to include the ability to collapse and expand.
Copy and paste the following script:
Notice that we are registering an event again using register event @ToggleCollapseState
. We will create an event later that will change the view state. In the second line, you will notice the @Collapsed
variable. This is a view state variable (also called a view state field). We assign the @Collapsed
variable to its original value, but when it is unknownboolean
, we assign it the value false
. This ensures that the toggle behavior functions correctly. More on that in the event we will create shortly.
To create the view state field:
Click the "View state" tab.
Click on "New field" in the toolbar.
Name it "Collapsed."
Select "Boolean" as the type.
Now let's create the event:
Create the "ToggleCollapseState" event.
Copy and paste the following event code:
Notice that if we did not assign @Collapsed
to false
when it was unknownboolean
, this event would not work, because assigning a variable with not unknownboolean
still results in unknownboolean
.
The Less style of the widget has also been updated. Replace the existing style with the following:
Now, preview the widget, and you will see that it is clickable and toggles between an expanded and a collapsed state. Notice that if you refresh the page, the view state still exists. Maintaining the view state is essential for creating a good user experience (UX).
These are just the basics of view state. For instance, you can also manage view state via JavaScript, which we will explore in more detail in later chapters.
You have learned the basics of the concepts involved in building a widget. While we haven't gone in-depth yet, we have only scratched the surface. However, I hope you now have a foundational understanding of what it takes to create basic widgets. I also hope I didn’t scare you off with the last chapter. With almost daily news about data breaches and computers being compromised by ransomware, it is crucial to prioritize security in programming. While programming with security in mind has always been important, it is even more essential in today’s world.
When we refer to a resource, we mean any type of file that will be embedded with the widget. This can include images, sounds, PDF documents, JavaScript, CSS, XML files, and more.
In this chapter, we are going to create a CSS file, a JavaScript file, embed a video, and add them as resources to our widget. Let's get started!
Copy the following CSS content:
Paste it into your favorite text editor.
Save the file as "tickle.css" on your computer.
Copy the following JavaScript content:
Paste it into your text editor.
Save the file as "tickle.js."
Download the video "tickle.mp4" that we are going to embed here.
Now that we have all the resources locally on our computer, let's create the widget.
Create a new widget named "Tickle."
Select the Resources tab.
Click on "New resource."
Name it "TickleCss."
A resource has been created, but without any file attached to it. In this case, we need to upload our newly created "tickle.css" file.
Select the "TickleCss" resource if it is not already selected.
Click the "Browse" button in the details panel on the right.
Select the newly created "tickle.css" file on your computer.
Click "OK."
The last three steps may vary slightly depending on the browser you are using.
The file should now be uploaded, and under the "File" column in the resources overview, the cell should display "tickle.css."
Now, repeat the process to create two more resources for "tickle.js" and "tickle.mp4," naming them "TickleJs" and "TickleVideo," respectively.
Almost there!
Copy the HTML code below and paste it as a Script in the Widget Editor:
We introduce a new statement here called scriptreference
, which requires two arguments. The first one should be a unique key (more on that in later chapters), and the second one is the URL of the resource. We also introduce the FileUrl
function to retrieve the file URLs of all the resources.
Important Note: Every CSS and JavaScript file that you include in the widget will be globally interpreted in the browser. This means that if you are not careful, you could create CSS collisions or, even worse, overwrite JavaScript variables, functions, and classes! We will discuss this in more depth in later chapters.
For now, preview this widget, and hover over the video! :)
Do you have some spare time to give your widget an extra touch by writing some JavaScript? Although this is not mandatory for a functional widget, it can enhance the professionalism of your widget and, in some cases, improve the user experience (UX) in the Template Editor by providing visual feedback to the user.
Let's update our "Information Card" widget with a Template Editor script.
Select the "Template Editor Script" tab.
Copy and paste the following code:
Don't forget to save!
This code should look quite familiar to a JavaScript veteran. Notice that we use utility.createElement
instead of document.createElement
to create our HTMLElement
. This is because we only provide a subset of the JavaScript API, just enough to create a preview. We will go into more detail about this in later chapters.
If you still have the template open where this widget was placed, you should already see the changes from the default look to our custom preview. If not, open the template to see the updated appearance.
My guess is that you are eager to build a widget on your own, and your fingers are itching to write some code! But as the great Uncle Ben from Peter Parker famously said, "With great power comes great responsibility." While this quote was ironically popularized by the movie Spider-Man, it holds true nonetheless. So, sit tight, and let’s have a candid discussion about the risks and responsibilities involved.
WEM is a no-code platform that allows you to create applications ten times faster than traditional methods. We are proud to share customer stories that prove this. With the development of AI, it’s not unreasonable to think that we will be able to build apps even faster. However, what people often overlook is that WEM implements numerous safety precautions, enabling you to build without worry while it handles the hard work in the background, allowing you to sleep soundly at night.
I’m referring to safety on a technical level. I’m not suggesting that WEM guarantees you will never create unsafe applications. For instance, someone might forget to add authorization to a sensitive page that displays client information openly. This is a user error that WEM cannot prevent. WEM provides you with tools built on universal standards to implement authorization, but it cannot ensure that you will implement it correctly. Think of WEM as a car equipped with bulletproof glass; it offers protection, but if a reckless driver looks too deeply into the glass, don’t be surprised if the car comes home with bumps and scratches.
"What does this have to do with widgets?" With widgets, you gain more control—essentially, more power. And you know what Uncle Ben said? Building widgets requires not only extensive knowledge of HTML, CSS, and JavaScript but also an understanding of how the WEM Runtime works. It’s a lot to take in, and if you’re not careful, you might inadvertently introduce XSS (cross-site scripting) vulnerabilities into your widget without even realizing it.
I take my job as a programmer very seriously, and you should too. I see too many people copying code from the internet and pasting it without understanding what it does. "It works, so it must be fine, right?" So, I want to ask you a question: Are you a good programmer, or a good Googler?
Do you know the dangers of using innerHTML
? It seems safe to use, right? After all, any <script>
tags are ignored, so it should be perfectly safe. Well, not quite.
Here’s something interesting. Do you know what the following CSS does?
That’s right! This is a stripped-down version of a keylogger written in CSS! What about this one?
Yes, if you are creative enough, you can even execute scripts without using <script>
blocks. In the example above, because the image "x" does not exist, the onerror
event will be triggered. So even if innerHTML
ignores <script>
blocks, there are still ways to execute all kinds of malicious actions.
From this point forward, a strong knowledge of HTML, CSS, and JavaScript is essential. I’m going to be more strict this time. If you don’t understand the dangers of using innerHTML
, you should. If you don’t know why it’s important to encode output, you should. We are going to delve deeper and get more technical, so strap yourself in a little tighter, and let’s go!
There is one important aspect to address before concluding this chapter. When you use widgets in a WEM project, the WEM runtime will render these widgets within the page, and the styles and JavaScript associated with the widget will be added in a global context. Therefore, you must be careful when naming your CSS selectors and JavaScript identifiers, just as you would when building a standard website.
While there are modern solutions to this issue, WEM does not yet support all of them. In later chapters, we will explore these solutions in greater detail. For now, let's take a brief look at the potential issues.
In CSS, naming collisions occur when two or more styles are defined with the same selector. This can lead to unexpected results, potentially overriding earlier styles. For example, consider the following:
Design systems like Bootstrap often use simple class names such as .btn
or .alert
. This is because you typically want to incorporate one design system per website. However, widgets are not design systems, and as demonstrated in the example above, it is easy to create conflicts when using generic names.
When using JavaScript in your widget, you must be cautious when naming your functions, classes, and variables. If you are not careful, you may inadvertently override them, leading to undefined behavior in the widget's functionality. For instance:
In this example, clicking Bob's button will log "Jane here!" to the console. While this may seem like a harmless example, it is not the expected behavior. There are multiple ways to address this issue, but they are beyond the scope of this chapter.
The scriptreference
statement is also susceptible to naming collisions. Consider the following example:
It is important to note that it does not matter that both use @ButtonJs
as the same reference name, as this is a local variable of the widget. However, using "button"
as a namespace is too generic. In this case, it will not override other functionalities but will instead use the script that has already been registered under the name "button"
. Another statement that has the same issue is called scriptmodule
, which we will discuss in later chapters.
There are various ways to address these issues, with some solutions being better than others. One simple approach is to avoid using generic names like button
. Instead, use a format like yourcompanyname-componentname
to make it less generic. In CSS, you would have something like:
In a scriptreference
, you would write:
This is just one of the possible solutions, and there are many more depending on the context. I have also omitted a solution for JavaScript, as that topic requires a dedicated section of its own, which we will explore in later chapters.
For now, it is essential to remember that you should avoid using generic names unless you are absolutely certain of what you are doing.
Now that you have learned the basics of building a widget, we also need to discuss the risks and responsibilities involved in this process. The next chapter will be serious but very important!