wl-forms-training

Add Functionality to Winlink Forms

Purpose

Build out Form features to improve capabilities like handling button clicks and saving and loading files.

Javascript

JavaScript in conjunction with Web APIs are the “action” components of websites:

Developing websites with JavaScript is not simple, however it is not hard to find solutions to problems you encounter.

You can copy-paste as much (or as little) of the code as you wish throughout these modules.

The more you know about JavScript and Web APIs, the more you can do.

What to Expect

This training module is not comprehensive:

This module does not intend to teach you JavaScript so that you can walk-away ready to write a website.

The goal is to introduce commonly used aspects and code patterns to apply and get going quickly with Winlink Forms.

If you have none or very little programming experience:

If you are an experienced programmer:

The idea is to introduce the capabilities so you can pick it up and run with it.

Web APIs

Modern browsers have Web APIs built into them that allow Javascript to interact with the document loaded into the browser.

Advice

Some examples here (and elsewhere) are going to break lots of standards and best practices.

Inline Javascript:

It is usually good practice to referentially load javascript from one or more files:

An example of referentially loading JavaScript from a file, and deferring processing to reduce document loading delays:

<body>
  <article>
    ...
  </article>
  <script src="path/to/script.js" deferred></script>
</body>

Helpful Tools

Browser Developer Tools:

Basic Javascript

Semicolon: Defines the end of a line of code ;

’//’: Everything following this on the same line is a comment and is not executed.

Javascript console.log("your message"): Add this to any area inside <script> block to output information to the Console tab in Developer Tools.

Template Literal: Concatenate text with variable values (strings). For example: var word="world!"; console.log(`Hello ${word}`; displays “Hellow World” in the Console tab when the line is executed.

Function: Keyword defines an encapsulated code block that can be referenced from elsewhere. function sayHello() { console.log('Hello World!')};

Web API

Web API allows Javascript interaction with the web page through pre-defined functions and properties.

For most Winlink Forms, the most useful Web API tools are probably:

Events

These are things like button click (‘click’) or when tabbing away from a text box (‘blur’).

Onload (‘onload’) allows javascript to run a function while the web page is loading, before the user can interact with the page.

Two ways to set an event “handler”:

  1. Javascript + WebAPI: addEventListener('click', function() {...});
  2. HTML inline attribute ‘onClick’: <input type="button" onclick="myFunction();">Click me</input>

Javascript + WebAPI is modern, widely supported, provides wide flexibility in defining event-based actions, and is easier to maintain.

Using HTML attribute onClick="" is still supported in most browsers but:

Try to avoid using inline handlers like ‘onClick’ except for in the most basic of projects.

Transferring Data From Form To Template

  1. Define a Winlink Template with Tags, named appropriately for what data they will display.
  2. Within the <form> element, add some <input type="text"> elements so a user can input data.
  3. Name each input element with the name attribute that matches an existing Tag in step 1.
  4. When the user complets the form and clicks the Submit button, data is transferred to the Winlink Template file for processing.
  5. The Winlink Template matches <input> element’s name attribute with existing var Tags, and copies the data from the Form to the message.

Define Template Tags:

Msg:

<var myCall>
<var toCall>

Define Form Names that match Template Tags:

<div>
    <label for="mycall">Enter your callsign:</label>
    <input type="text" name="mycall" id="myCallsign" />
    <label for="tocall">Enter recipients callsign:</label>
    <input type="text" name="tocall" id="yourCallsign" />
</div>

Saving Form Data To File

Blob: Some value(s) stored in a structured way that is useful for reading from, or writing to files.

URL: A JavaScript object that creates a link, much like a hyperlink used to navigate the internet.

Document API:

WebAPI HTML Element .click() performs a click programmatically. Effect is the same as clicking the element with a mouse (think: Submit button).

// Saves form data to Downloads folder
function downloadFile(fileContent, fileName) {
  // 1. Create a Blob from the content ('application/json' is a good portable alternative)
    const myFile = new Blob([fileContent], { type: 'text/plain' });

    // 2. Create a URL for the Blob
    const fileURL = URL.createObjectURL(myFile);

    // 3. Create a temporary anchor element
    const dlBtn = document.createElement('a');
    dlBtn.setAttribute('href', fileURL);
    dlBtn.setAttribute('download', fileName); // Suggests a file name for the download

    // 4. Append to the DOM and programmatically click the link
    document.body.appendChild(dlBtn);
    dlBtn.click();

    // 5. Clean up by removing the link and revoking the URL
    document.body.removeChild(dlBtn);
    URL.revokeObjectURL(fileURL);
}

Loading Form Data From File

This approach uses a proxy element (similar to saving data to a file) but requires a bit more HTML and JavaScript to work properly:

  1. Set an HTML Element to an input of type “file”.
  2. Optionally create a button the user can press to start loading a file.
  3. Allow the user to locate the file to load.
  4. Parse the loaded data. This can be difficult depending on the type of data such as Numbers vs. Date vs. Strings.
  5. Load the Form elements with the file data. Larger forms with more form fields results in more code necessary to complete the task.
<input type="file" id="txtfiletoread" accept=".txt" />
<input name="loadbtn" type="button" class="loadDataButton" id="loadbtn" value="Load Race data" title="Load data from a file." />

This is an outdated way to set a click event that you will see in Winlink Standard Forms: <input type="button" onclick="document.getElementById('txtfiletoread').click();">Load File</input>. Instead of using this syntax, keep HTML easy to read and maintain by writing the HTML:

<section>
  <input type="button" name="SaveButton" id="saveButton" value="Save Data" />
    <input type="button" name="LoadButton" id="loadButton" value="Load Data" />
    <input type="file" id="txtfiletoread" accept=".txt" />
</section>

Then adding JavaScript to:

const fileSelector = document.getElementById('txtfiletoread');

const loadButton = document.getElementById('loadButton');
loadButton.addEventListener('click', () => {
  fileSelector.click();
});

fileSelector.addEventListener('change', (event) => {
  const file = event.target.files[0]; // Get the first selected file
  if (file) {
    readFileData(file);
  }
});

function readFileData(file) {
  const fileReader = new FileReader();

    // this subsection of code is not run until FileReader has tried to load file data
    fileReader.onload = (e) => {
        // prompts user for a filepath and name
        const content = e.target.result;
        console.log('File content: content');

        // parse the JSON data for convenient access
        let jsonData = JSON.parse(content);

        // check for valid data and log an error if empty
        if (jsonData === undefined || jsonData.length < 1) {
            console.error('JSON string data is empty. No file data to load.');
        } else {
            console.log("File content parsed as JSON:", jsonData);

            // assign the values to the input elements
            document.getElementById('myCallsign').value = jsonData.MyCallsign;
            document.getElementById('yourCallsign').value = jsonData.YourCallsign;
        }
    };

    // check for fileReader error and log a message if for example file not found
    fileReader.onerror = (e) => {
      console.error("Error reading file:", e.target.error);
    };

    // Read the file as text
    fileReader.readAsText(file);
}

And then use CSS to hide the “file” type input element to keep the UI tidy:

#txtfiletoread {
  display: None;
}

This is fairly complex, but follows a common, well known pattern and is safe.

Storing Data Locally

Here is an exercise to try:

  1. Add the saveToLocalStorage() function code to the <script> area of styled-form.html.
  2. Create a button called “Store In Browser” on styled-form.html (in the <body>, probably next to the other buttons).
  3. Create an event listener (addEventListener('click' () => {});) that calls the function below, taking a value from an input element (try myCallsign).
  4. Save the changes.
  5. Load the form into a browser.
  6. Open the Developer Tools and click on the Application tab, Local storage node, file:// node.
  7. The key-value data should appear in the child node after clicking your new Store In Browser button.
function saveToLocalStorage(itemKey, itemValue) {
    var existingItem = localStorage.getItem(itemKey);

    if (existingItem.length > 0) {
        localStorage.removeItem(itemKey);
    }

    console.log('Storing item to browser memory:', itemKey, itemValue);
    localStorage.setItem(itemKey, itemValue);
}

JavScript Built-In Objects

Since this module is diving into JavaScript, following are some more tid-bits that you can reference when building your own Winlink Forms.

Primitives: Simple representations of values. See MDN JavaScript Glossary > Primitive

Objects:

Logic is provided by if(comparison){ ... } and then{ ... } code blocks:

There are other ways to compare things, but if...then is good for a vast majority of Winlink Form creation needs.

JavaScript is full other other functionality. Probably the most commonly used are map() and forEach(). This is true specifically for Winlink Forms.

Parsing Strings (i.e. Is this a string and if so return its value):

Parsing Numbers:

Parsing a Number is a little tougher due to several ways they are presented in JavaScript:

Parsing other JavaScript Objects:

Install

Put files into Winlink Express Global Folders\Templates directory:

If you define the javascript in its own file copy it to the same directory as well.

Use

  1. Open Winlink Express.
  2. Click ‘New Message’
  3. Click ‘Select Template’
  4. Click tagged-template.txt in the Global (or callsign) directory.
  5. Click the ‘Select’ button and the default browser will launch, displaying the functional-form.html form.
  6. Complete the form by adding values to the text boxes.
  7. Click Save button to download the form data to a file.
  8. Click Submit button and the form data is transferred to the new message window.

Resources

Move on to Module 4 - Form Style

Return to Root README