LogoLogo
WEM ModelerWEM ReferenceTutorialsMyWEM
  • WEM Platform Documentation
  • Getting Started
    • Introducing WEM
    • Create WEM Account
    • WEM Login
    • MyWEM Portal
      • MyWEM Features
      • MyWEM Support
      • Allow Support Access
    • Partner Portal
  • WEM Modeler
    • Introducing the WEM Modeler
    • Modeler Start Page
    • Manage Projects & Workspaces
    • User Roles Scheme
    • Create a new Project
    • Modeler Application Overview
    • Project Settings
      • Project Configuration
      • Security Settings
      • Portal Settings
      • SMTP Settings
      • Mobile App Settings
    • Expression Editor
    • Flowcharts
      • User Interaction
      • End Node
      • Sub-flowchart
      • Navigation
      • Clear session
      • Decision
      • Assignment
      • List Action
      • Loop
      • Copy Data
      • Save and Discard
      • Import Data
      • Export Data
      • Invoke Webservice
      • HTTP Request
      • Execute Process
        • Different processes explained
        • Send Email
        • Change settings
        • Changing files
        • Generate File
      • Ping server
      • Authenticate
      • Comet Messages
      • OpenAI
      • Async Tasks
      • Note Node
    • Data Model
      • WEM Session Folder
    • Services and Integration
      • Webservices
      • Remote Data Sources
      • Authentication Providers
        • SAML 2.0
          • SAML App Registration in EntraID
        • OAuth 2.0
      • Comet - Real Time Messages
      • Certificates
    • Concepts and Concept Queries
      • Concepts
      • Concept Queries
      • Concept Relations
    • Navigation
      • HTTP Endpoints
      • Tasks
    • Files and Assets
      • Widget Libraries
      • Template Fragments
      • Files
      • Hyperlinks
    • Multilanguage Dictionary
    • Agentic AI
      • WEM AI Agent
      • AI Flowchart Nodes
      • AI User Interaction
    • WEM Preview
    • WEM Runtime
      • Publishing Process
      • Staging
      • Live
    • Debugging
  • Template Editor
    • Interaction Template Editor
      • Interaction Template Hotkeys
    • General and preview
    • Style
      • Heading Type
      • Paragraph Style
      • Text Styling
      • Alignment
      • Hyperlink
      • Tooltip
    • Layout
      • Adaptive columns
      • Div
      • Panel
      • Alert
      • Table
      • Image
      • Icon
      • Label
    • Interaction
      • Form
      • Validation
      • Required field
      • Button
      • Dropdown
      • Free form item
    • Miscellaneous
      • Tabbar
      • Google maps
      • Nested template
      • Script
      • Reference
      • Conditional
      • Data grid
      • Repeater
      • Rich text
      • Custom
    • WMT Tool
  • WEM Reference
    • WEM Expressions Reference
    • Functions
      • Aggregation
        • Average
        • Count
        • First
        • Last
        • Max
        • Min
        • Sum
      • Concepts
        • ConceptId
        • Description
        • FindConcepts
        • GetAncestors
        • GetChildren
        • GetDescendants
        • GetParent
        • Intersection
        • LocalName
        • Synonyms
        • ToConcept
        • Union
      • Date
        • Date
        • DateDiff
        • DateOnly
        • DatePart
        • DayOfWeek
        • FromUnixTimestamp
        • MoveDate
        • Now
        • TimeOfDay
        • Today
        • ToDuration
        • ToLocal
        • ToUnixTimestamp
        • ToUTC
        • UTCNow
        • UTCToday
      • Environment
        • ApplicationId
        • CountryCode
        • CountryName
        • Hostname
        • IpAddress
        • IsFirstRequest
        • IsHttps
        • IsPreview
        • IsStaging
        • IsLive
        • NodeTrail
        • PortalId
        • PortalName
        • ProjectId
        • ProjectName
        • ProjectVersion
        • Setting
        • UrlPath
      • Files
        • FileName
        • FileSize
        • FileUrl
        • MimeType
      • Lists
        • Distinct
        • IsNewRow
        • IsOnRow
        • List
        • NameOf
        • RowId
      • Math
        • Abs
        • Ceiling
        • Cos
        • Distance
        • Exp
        • Floor
        • Log
        • Log10
        • Pi
        • Pow
        • Random
        • Round
        • Sin
        • Sqrt
        • ToNumber
      • Security
        • AesDecrypt
        • AesEncrypt
        • AesIv
        • AesKey
        • CreateJsonWebTokenH256
        • HmacSha256
        • Md5
        • Password
        • PasswordStrength
        • Pbkdf2
        • Sha1
        • Sha256
        • Totp
      • Text
        • AsText
        • Base32ToText
        • Base64ToText
        • CompareFuzzy
        • Concat
        • Contains
        • FormatDate
        • FormatDuration
        • FormatNumber
        • Guid
        • HexToText
        • HtmlAttributeEncode
        • HtmlEncode
        • IndexOf
        • JavascriptEncode
        • JsonEncode
        • Length
        • Padl
        • Padr
        • RegExIsMatch
        • RegExMatches
        • RegExReplace
        • Replace
        • Split
        • StringPart
        • SubString
        • ToBase32
        • ToBase64
        • ToBase64Url
        • ToHex
        • ToLower
        • ToRichText
        • ToString
        • ToTitleCase
        • ToUpper
        • Trim
        • UrlDecode
        • UrlEncode
        • XmlAttributeEncode
        • XmlEncode
        • XPath
        • XPathList
        • XPathNumber
      • Validation
        • HasValue
        • IsEmpty
        • IsKnown
        • IsUnknown
        • IsValidBankaccount
        • IsValidBSN
        • IsValidBTW
        • IsValidEmailAddress
        • IsValidIBAN
        • IsValidPhonenumber
        • IsValidURL
      • Localization
        • AvailableLanguages
        • CurrentLanguage
        • CurrentTimezone
        • LanguageStrategy
        • Languages in WEM
    • Keywords
      • Contains
      • Concepts
        • Ancestor of
        • Child of
        • Concept
        • Descendant of
        • Overlaps
        • Range of
        • Parent of
        • Contains
      • Environment
        • ApplicationRoot
        • Platform
      • Lists
        • In
        • Of
        • Where
        • Contains
        • All
      • Logic
        • And
        • Not
        • Or
        • Choose
        • If Then Else
      • Mobile
        • BatteryLife
        • BuildVersion
        • ConnectivityTypes
        • DeviceId
        • DeviceName
        • IsDevelopmentBuild
        • IsDistributionBuild
      • Text
        • Contains
        • Ends with
        • NewLine
        • Starts with
    • Operators
      • Addition +
      • Division /
      • Equality =
      • Equality-strong ==
      • Greater-than >
      • Greater-than-or-equal >=
      • If-unknown ?
      • Inversion -
      • Less-than <
      • Less-than-or-equal <=
      • Logical-and &, and
      • Logical-not !, not
      • Logical-or |, or
      • Modulo %
      • Multiplication *
      • Power ^
      • Subtraction -
      • Unequality <>
    • Data Types
      • Text
      • Number
      • Yes / No (Boolean)
      • Date Time
      • Duration
      • File
      • Rich Text
      • Concept
      • Conceptset
      • Reference
    • SQL Compatibility
      • Functions
      • Keywords
      • Operators
    • Content Security Policy
    • WEMScript for Custom HTML
    • WEM Widgets
      • WEM Widget Structure
      • WEMScript for Widgets
      • Widget Editor
      • Content Placeholder
    • WMT
      • File Structure
      • Basic Functional Template
      • Styling
      • Font Icons
      • Reference
        • Breadcrumb
        • Button Bar Buttons
        • Common Scripts
        • Common Styles
        • Conditional
        • Content
        • CSP
        • CSP Nonce
        • Expression
        • Hidden Fields
        • Language Select
        • Navigation
        • Node ID
        • Node Name
        • Placeholder
        • Placeholder Conditional
        • Startup Scripts
        • Template Fragment
        • Uses
    • Regression Test Framework
      • WEM Test Script
  • Tutorials
    • Tutorials
      • Making a Change Log
      • Placeholders for end-users
    • First Application
      • Expand First Application
    • Video Tutorials
      • By Partners
      • Security
    • Building Widgets
      • Basics
        • 1. Introduction
        • 2. Hello, World!
        • 3. General
        • 4. Properties
        • 5. Events
        • 6. Styling
        • 7. View State
        • 8. Resources
        • 9. Placeholders
        • 10. Template Editor Script
        • 11. Global Scope
        • 12. Risks and Responsibilities
        • 13. Conclusion
      • In-depth
        • 1. Introduction
        • 2. Platform Availability
        • 3. Development Workflow
        • 4. Properties
        • 5. View state
          • 1. Runtime.viewState
          • 2. sessionStorage
          • 3. Summary
        • 6. Styling
        • 7. Events
          • 1. Navigation
          • 2. Row position
      • WEMscript
        • 1. Introduction
        • 2. Variables
        • 3. Properties
        • 4. OutputId()
        • 5. Register Input - Required
        • 6. Encoding
        • 7. Script Block - Introduction
        • 8. Types
          • 1. Boolean
          • 2. Concept
          • 3. ConceptSet
          • 4. Datetime
          • 5. Duration
          • 6. File
          • 7. Number
          • 8. Richtext
          • 9. Text
        • 9. Script Block - Global Scope
        • 10. Script Block - ES Modules
        • 11. CSS - attachShadow
      • Examples
  • Tips and Tricks
    • Tips and Tricks
      • Keyboard Shortcuts
      • Video's in WEM
      • Extracting text from File
    • Custom Hostnames
    • FAQ
    • Expressions
      • Order of Operators
    • Processes
      • Email Process
      • Tips for creating PDF's
      • ZIP Archive Process Node
    • Concepts
      • Concepts and Runtime
    • Working with data
      • The Power of the Calculated Field
      • Consuming your OData source with Excel 2016
      • Expose your data via OData
      • Using OData to transfer data between runtimes
      • WEM Storage Options
    • Integration
      • Integrating external systems
      • Json
      • Single Sign-on
      • Office365 Integration
    • Performance
      • Performance Tips
      • Performance Profiler
      • Template Performance Profiler
      • The Split Loop
    • Security
      • 2FA with TOTP
      • Blacklist or whitelist IP addresses
    • Widgets
      • Advanced Charts
        • Boxplot Chart
        • Brush Chart
        • Bubble Chart
        • Candlestick Chart
        • Card with Chart
        • Effect Scatter Chart
        • Heat Map
        • Line, Area and Bar Chart
        • Map-Bar Morph
        • Mix Time Line Chart
        • Nested Pie Chart
        • Polar Area Chart
        • Polar Bar Chart
        • Profit Chart
        • Radial Bar Chart
        • Radial Bar Gauge
        • Range Bar Chart
        • Rosetype Pie Chart
        • Sankey Energy Chart
        • Scatter Chart
        • Scatter-Aggregate-Bar Chart
        • Stacked Columns Chart
        • Stepline Chart
        • Treemap
      • Advanced Input
        • Color Picker
        • Conceptset Image Selector
        • Google maps locator
        • Multi select input button
        • MultiFile Upload
        • On/Off switch
        • Process Arrow Steps
        • Rate
        • Rating view
        • Single select input button
        • Slider
        • Tree
      • Template Actions
        • Follow exit
      • Global widgets
        • Agenda
        • Audio Player
        • Calendar
        • Draw Signature
        • Grid Widget
        • Image capture
        • Kanban Board
        • Path
        • Progress Bar
        • reCAPTCHA
        • Timeline
      • Document widgets
        • Json Viewer
        • PDF Viewer
    • Various
    • Go to MyWEM
Powered by GitBook
On this page
  • Invariant Culture
  • ID Attribute
  • Special Script Blocks
  • Conclusion

Was this helpful?

Export as PDF
  1. Tutorials
  2. Building Widgets
  3. WEMscript

7. Script Block - Introduction

In this chapter, we will create a Note widget. It is a relatively simple widget to build, but we need to address a few challenges to ensure it functions correctly as a WEM widget.

As mentioned previously, adding widgets will introduce CSS and JavaScript into a global space. This can be quite challenging for more advanced widgets, as we want to avoid overriding and breaking existing CSS and JavaScript. Therefore, we will start with some scaffolding to see how we can prevent these issues.

We will learn about invariant culture, naming collisions, cleaning up the widget, and a few other concepts along the way. Let's get started!

  • Create a new widget and name it "Note."

  • Copy and paste the following CSS into a new file called "note.css."

  • Add it as a resource named "NoteCss."

.wem-note {
	background-color: lightgoldenrodyellow;
	display: flex;
	flex-direction: column;
	position: fixed;
	left: calc(var(--x) * 1px);
	top: calc(var(--y) * 1px);
	width: 250px;
	height: 250px;
}

.wem-note > .handle-bar {
	background-color: tomato;
	cursor: pointer;
	flex: 0 0 20px;
}

.wem-note > .message {
	flex: 1 0;
	outline: none;
	padding: 8px;
	resize: none;
}

Notice that we use a more specific class name for our Note widget, .wem-note.

  • Copy and paste the JavaScript code below into a new file called "note.js."

  • Add it as a resource named "NoteJs."

class WemNote {
    messageEl;
    rootEl;
    x; 
    y; 
    previousX; 
    previousY;
    
    constructor({ x = 100, y = 100, message = "New note" }) {
        this.setThisContext();

        const handleBarEl = document.createElement("div");
        handleBarEl.classList.add("handle-bar");
        handleBarEl.addEventListener("mousedown", this.startDragHandler);

        this.messageEl = document.createElement("textarea");
        this.messageEl.classList.add("message");
        this.messageEl.value = message;

        this.rootEl = document.createElement("div");
        this.rootEl.classList.add("wem-note");
        this.rootEl.append(handleBarEl, this.messageEl);
        
        this.updatePosition(x, y);
    }

    dispose() {
        this.removeWindowEventListeners();
    }

    getData() {
        return {
            x: this.x,
            y: this.y,
            message: this.messageEl.value,
        };
    }

    moveHandler(event) {
        const deltaX = event.screenX - this.previousX;
        const deltaY = event.screenY - this.previousY;

        this.previousX = event.screenX;
        this.previousY = event.screenY;

        this.updatePosition(this.x + deltaX, this.y + deltaY);

        // Disable browser selection.
        event.preventDefault();
        event.stopImmediatePropagation();
    }

    removeWindowEventListeners() {
        window.removeEventListener("mousemove", this.moveHandler, { capture: true });
        window.removeEventListener("mouseup", this.removeWindowEventListeners);
    }

    setThisContext() {
        this.moveHandler = this.moveHandler.bind(this);
        this.removeWindowEventListeners = this.removeWindowEventListeners.bind(this);
        this.startDragHandler = this.startDragHandler.bind(this);
    }

    startDragHandler(event) {
        this.previousX = event.screenX;
        this.previousY = event.screenY;
        
        window.addEventListener("mousemove", this.moveHandler, { capture: true });
        window.addEventListener("mouseup", this.removeWindowEventListeners);
    }

    updatePosition(x, y) {
        this.x = x;
        this.y = y;
        this.rootEl.style.setProperty("--x", this.x);
        this.rootEl.style.setProperty("--y", this.y);
    }
}

The above CSS and JavaScript code are standalone and can even be used in a non-WEM application if desired. The code below is necessary to make it work as a WEM widget. There are a few interesting aspects that we will dissect later.

<? 
	scriptreference "wem-note" FileUrl(@NoteJs)

	register input @Message 
	register input @X invariant culture
	register input @Y invariant culture
?>
<input type="hidden" id="<?attr OutputId(@Message) ?>" name="<?attr OutputId(@Message) ?>">
<input type="hidden" id="<?attr OutputId(@X) ?>" name="<?attr OutputId(@X) ?>">
<input type="hidden" id="<?attr OutputId(@Y) ?>" name="<?attr OutputId(@Y) ?>">
<? startupscript ?>

	const note = new WemNote({
		message: <?js @Message ?>,
		x: <?js @X ?>,
		y: <?js @Y ?>,
	});

	document.documentElement.append(note.rootEl);

	window[Symbol.for(<?js OutputId() ?>)] = note;

<? end ?>
<? submitscript ?>
	
	const note = window[Symbol.for(<?js OutputId() ?>)];
	const { message, x, y } = note.getData();
	
	document.getElementById(<?js OutputId(@Message) ?>).value = message;
	document.getElementById(<?js OutputId(@X) ?>).value = x;
	document.getElementById(<?js OutputId(@Y) ?>).value = y;
	
<? end ?>
<? unloadscript ?>

	const noteSymbol = Symbol.for(<?js OutputId() ?>);
	window[noteSymbol].dispose();
	delete window[noteSymbol];

<? end ?>

Invariant Culture

An extra argument, invariant culture, has been added to the register input statements in the following lines of code:

register input @X invariant culture
register input @Y invariant culture

In the previous encoding section, I explained the different writing formats used by various cultures (for example, in English, a "." is used as a decimal separator, while in Dutch, a "," is used). This discrepancy can make it challenging for programmers to read and write numbers consistently between systems. An "invariant culture" is essentially an artificial culture where the format specifications remain constant. In the case of numbers, the "invariant culture" specifies that the decimal separator will be a ".".

ID Attribute

In addition to the name attribute we encountered in our Simple Form widget, we have now added an id attribute to the <input> elements in the following lines of code:

<input type="hidden" id="<?attr OutputId(@Message) ?>" name="<?attr OutputId(@Message) ?>">
<input type="hidden" id="<?attr OutputId(@X) ?>" name="<?attr OutputId(@X) ?>">
<input type="hidden" id="<?attr OutputId(@Y) ?>" name="<?attr OutputId(@Y) ?>">

The reason we need the id attribute here is that we have an additional step in the submitscript to retrieve the new values from the note instance, and we want to write those values back to these inputs. The name attributes are still mandatory on the <input> fields for sending the data back with the request to the WEM runtime.

Special Script Blocks

You may have noticed that we haven't written any JavaScript inside a <script> tag yet. This is not incorrect by any means, and there are situations where it is even necessary when using third-party libraries. However, when building a widget, you want to have more control over when to call functionality in response to specific events. Each piece of JavaScript code in these special JavaScript blocks can be thought of as handlers, similar to a click handler on a button. We have three special JavaScript blocks; let's walk through them.

Startup Script

<? startupscript ?>

	const note = new WemNote({
		message: <?js @Message ?>,
		x: <?js @X ?>,
		y: <?js @Y ?>,
	});

	document.documentElement.append(note.rootEl);

	window[Symbol.for(<?js OutputId() ?>)] = note;

<? end ?>

This code will run after the page has been fully replaced, making it ideal for initializing JavaScript instances since you can be sure that all elements have been rendered. As shown in the example above, we initialize our note here. You may be wondering about the Symbol.for statement; I will explain that later.

Submit Script

<? submitscript ?>

    const note = window[Symbol.for(<?js OutputId() ?>)];
    const { message, x, y } = note.getData();

    document.getElementById(<?js OutputId(@Message) ?>).value = message;
    document.getElementById(<?js OutputId(@X) ?>).value = x;
    document.getElementById(<?js OutputId(@Y) ?>).value = y;

<? end ?>

This code executes after an event has been triggered, such as a button click, but before the request is made. It is ideal for sanitizing and serializing data, as well as updating the data just before sending it with the request. This is why we need the id attribute on the input fields.

Unload Script

<? unloadscript ?>

    const noteSymbol = Symbol.for(<?js OutputId() ?>);
    const note = window[noteSymbol];
    note.dispose();

    delete window[noteSymbol];

<? end ?>

The code above runs after receiving a new response from the server but before the new page is rendered. It is perfect for cleaning up a widget instance. In this case, we call the note.dispose() method to clean up our Note widget. It is always important to perform cleanup, especially when there may still be a callback from setTimeout() that needs to be cleared with clearTimeout(). Additionally, our Note widget could still have "mouseup" and "mousemove" event listeners attached to the window object. Although optional, deleting the note ensures that there are no lingering references in the JavaScript runtime, which can assist the garbage collector.

Conclusion

We have built the foundational components to initialize our widget via the startupscript, update our form data using the submitscript, and clean up our widget in the unloadscript. To reduce the risk of naming collisions, we prefixed our class names to make them less generic. In the next chapters, we will explore additional details, such as the purpose of window[Symbol.for(<?js OutputId() ?>)] in the code above.

Previous6. EncodingNext8. Types

Last updated 2 months ago

Was this helpful?