Write Once, Run Everywhere (Part 1 of N)

Write Once, Run Everywhere (Part 1 of N)

Step-by-step guidance to creating a simple web app which allows us to store and preview images (Dynamic Image Catalog).

·

11 min read

Getting People To Use Your App

When you write an App the most important thing is that people use it.

Number One Barrier

The number one barrier to getting people to use your App is that everyone is running a different Operating System (Windows, Linux, macOS, Android, iOS).

All of those Operating Systems run different kind of native executables.

Which Technology

Depending upon which technology you use to build your app you may exclude many people from running your app.

For example, if you write a native Android app it means that only people on Android phones can run your app. Same thing when you write an native iOS app -- only people on iPhones can run your app.

This Leads Us To Browser Technology

The one thing that brings all of those Operating Systems (and devices) together is The Web Browser.

Browser Technology is Ubiquitous

All of those Operating Systems and devices have numerous Web Browsers (FireFox, Brave, Chrome, Safari, Edge, Opera) available to them.

Browser Technology Creates Common Platform

Since all Web Browsers support the same standards which are realized in HTML5* it means that we (Software Developers) have a way to:

Write Once, Run Everywhere!

*The HTML5 Standard is a set of technologies which includes HTML, JavaScript & CSS3 (Cascading StyleSheets) and other technologies (for more info, take a look at this graphic ).

That's the dream. You write your app and anyone on any device (desktop, laptop, Chromebook, Android Phone, iPhone, iPad, etc.) on any Operating System can run it. But, how real is that dream?

The Challenge of Browser Technology

The challenge is that there are things modern Web Browsers do not support (many are related to Security issues) which cause limitations in the Apps we want to write. What are some of those things? The one major limitation that springs to mind, which impacts App Development the most is :

  • The Browser's Inability to Write to a File**

**This is designed into Browser technology as a protection for the user.

Inability To Save Data On User's Machine

At first glance, the inability to read or write files on a user's machine may seem small. However, if you're writing your App using Browser Technologies (HTML, JavaScript, CSS) as soon as you begin to want to create a custom experience for the user you will find that it is difficult to save your App's data so the user can retrieve it.

Isn't there some way around this?

The Way Around Data Storage

There is a way (and we will look at it in depth as we create our Image Catalog App). Spoiler Alert: We can use the JavaScript localStorage API. You can take a sneak peak at this MDN (Mozilla Developer Network) entry if you want to see what localStorage can do.

There Are Limitations

The nature of The Web is that once a user stores data in your app she can return and retrieve it from any other device. localStorage however (as the name suggests) means that the data is only stored in this the one Browser where the user originally ran your App. If the user moves to another Browser on the same device or a browser on another device then she cannot retrieve her App data.

This Series of Articles Will Show You How To Solve This Problem

In this series of articles we will walk through the creation of our Image Catalog App (ImageCat) and discover how to completely resolve the issue of saving a user's data so that it can be retrieved again.

What Will We Learn?

Along the way we will learn the following:

  1. Basic HTML
  2. Simple JavaScript
  3. Simple CSS
  4. How to save user data using the localStorage API
  5. Display images as thumbnails
  6. Load full size image when user clicks any image in catalog
  7. How to copy text from Browser to the clipboard (so user can paste).
  8. Creation of a ContextMenu (menu displays when user right-clicks an image)
  9. Removing items (data) from localStorage.
  10. How to use Browser Developer Console to view contents of localStorage.
  11. Learn how to encrypt the user's data using AES256 via JavaScript
  12. Finally, this will all culminate in How to save a user's data to "The Cloud" -- we will write a C# ASP.Net Core MVC Web API which will allow us to post our user's data to a server for storage.

It's a lot of work -- but you'll see that you'll learn a lot along the way -- so let's get started.

Get The Code For This First Article

First of all, if you want to get the code for this first article you can get it from my GitHub repo.

Only Three Files

There are only three files that you need for this first step of the solution (index.htm, main.js, main.css). It's all very simple.

Try Version 1.x In Your Browser

You can try the app right now (and get source code) in your browser at: https://codepen.io/raddevus/pen/WNZZRRR

What I Want From My ImageCat App

Here's what I want to my Image Catalog to do:

App Requirements

  • Allow me to save a catalog of images (screenshots, etc)
  • Image catalog would be easily update-able so I can add new images very quickly / easily (more about this later).
  • Would be accessible from any of my computers or devices (desktop (running Linux), laptop (running Win10), iPad (iOS), Mac Mini (macOS), Amazon Fire (Android), phone running Android, etc).

What Will This Article Cover?

This article is going to set up the base app which will attempt to show that we can fulfill all three of those requirements using only HTML5 technology. Again, however, we will see the limitation of data storage.

Let's go write the code now.

First, The User Interface

All I need for the main interface is a way to allow the user to include a link to where the image is stored.

It's just a simple HTML input type text and a button the users clicks to add the image.

1a.png

That's it. Here's the simple HTML for that:

<p><input type="text" id="imageUrl">
      <button onclick="addImage()">Add Image(URL)</button>
</p>

Now the user can paste in an image link and click the button and it'll be added to our app.

Well, we need to write some code to handle all of that.

What I Want The App To Do

I want the app to :

  • Provide a thumbnail view of each image I add
  • Make it so when the user clicks an image it opens in a new window (tab) and displays the full-size image
  • Additionally, I'll do a little hover styling so when the user floats over a particular image it will display a subtle box around it to show the user that something is happening and that the image is clickable.

Here's a basic snapshot of what I'm thinking it will look like after four images are added:

2.png

What Are the Things We Need To Build In Code?

  • Write code to handle adding the image URL.
  • Write code to display all thumbnails for images the user has added.

    Note: Handling Bad Input

    For this article I will leave it as an exercise for the reader to handle bad input (no text in Image URL textbox, etc.).

If the user clicks the button with no URL an image tag will be added to the DOM but the link will be invalid and an bad image tag will be displayed. We will cover removing images from localStorage, but for now I won't cover it.

Here's the code we need to allow the user to add an image:

function addImage(){
  let allImages = JSON.parse(localStorage.getItem("allImg"));
  if (allImages === null){
    allImages = [];
  }
  let localUrl = document.querySelector("#imageUrl").value;
  allImages.push(localUrl);
  localStorage.setItem("allImg",JSON.stringify(allImages));
  document.querySelector("#imageUrl").value = "";
  removeMainDiv();
  displayImages();
}

Code Explanation

The first thing we try to do is retrieve an item from the user's browser localStorage which is named allImg.

We are expecting that to be an array of strings (URLs) so we wrap it in a call to JSON.parse() which will parse the localStorage data into an object -- in our case an array. If we didn't parse() the data then it would just be a string of data.

If the allImg item does not exist in the user's localStorage then it will return a null.

If the allImages variable is null then we initialize it to an empty array.

Next we get the new value that the user entered into URL textbox:

let localUrl = document.querySelector("#imageUrl").value;

Next we push the new image URL onto the array of images (that was previously empty or contained the images were already stored in localStorage).

Before we continue on talking about what this code does, let's take a moment and talk a bit more about Browser API localStorage.

About localStorage

Basically it is Browser storage that is based on name/value pairs. The name and the value are stored as strings.

You can simply provide a string name and then any value and store it. Your data will be stored as a string also so you'll need to convert it to your expected type.

localStorage is bound to the web site's TLD (top level domain). Values stored at one domain are completely inaccessible by any other domain.

That means if you store anything at http://localhost/ then you cannot retrieve those values from http://<anyOtherDomain>

The data is also stored in each browser instance. That means on the same desktop user's account but different browser you will not be able to access the same data even if you're on the same domain.

For example, if you save data to localStorage using the Google Chrome browser on http://mySite.com and then access the same site using your FireFox browser, the data is not accessible. This is true even if it is the same desktop user.

Data Access

Data stored in localStorage is only retrievable on the same browser on the same domain where it was initially saved.

This a security feature but also makes it difficult to share your data or make it so the data is available to the same user no matter where she is using your Single Page App.

What This Means to Us For Our App

This means that as long as you (and your user) is aware of this limitation then it may be valid to write a simple app like this which requires the user to always use the same browser from the same device to retrieve her data.

This is a frustrating limitation however and one that must be overcome in order to make it so our image catalog can be used across our many devices.

Running the App Locally

This can provide a little utility app by running the code locally.

For example, follow these steps:

  1. get the source code (from github) and save it into a directory.
  2. double-click the index.htm
  3. Add an image URL.
  4. Any images you add will be added to your browser's localStorage and the next time you double-click the index.htm from that folder they will be reloaded. Here's mine running from a file location in the Brave browser:

3.png

All Data In localStorage Is A String

Continuing with our explanation of our code, you'll now see that when I store our JavaScript array of strings that I actually call JSON.stringify() on the array so that it is turned into a pure string. Since everything that is stored in localStorage is a string this helps with escape characters etc. being handled properly.

Since we pushed the new image URL onto our array, it will show up last in our list of URLs.

It also means we have the entire list (any previously loaded ones and our new one) ready to be saved to localStorage. We then make the call to turn the array into a string and then wrap that with a call to save the data into localStorage:

localStorage.setItem("allImg",JSON.stringify(allImages));

This insures that the next time the page is refreshed (or the user visits the site) that all the images will be in localStorage & can be loaded in and displayed again.

Using Your Browser's Console To Examine localStorage

If you load our app and then open up your browser's console window (F12 in most browsers) then you can examine localStorage.

Once you open the console, just type: localStorage.getItem("allImg") and press enter.

If you've added any images then you'll see a list of URLs. Here's what mine looks like (I partially blurred that last one out since it is a link to some proprietary source code.)

4.png

You can see that it is a stringified (serialized) array, because it has outer single quotes that wrap the array braces [ ].

What Does the Rest of the Code Do?

The rest of the code simply:

  1. iterates through any images which are in localStorage
  2. Creates a new link tag for each (to make the image clickable)
  3. Creates a new img tag for each
  4. Sets the src value of the img tag to the URL found in localStorage

All of that work displays the thumbnail images and makes them clickable so they open in a new window at their original size.

Summary / Looking Forward / RFC

All of this has been to start a conversation about two main points:

  1. HTML can make building User Interfaces very easy -- very little code here for the dynamic view that we've created.
  2. Storing a user's data so it is easily retrievable is far more difficult with HTML5 technologies.

Alternate Ways of Storing This Simple User Data

Going forward I will provide some various ideas for storing user data and I would like to hear from readers about what they've found.

In the next article I will look at a few easy ways of posting data to a remove server where the data will be stored.

Request For Comments

What have you found that is useful to do that?

In the past I've used Google Firebase but there are some limitations and challenges there.

The Pipe Dream

A remote persistent storage that allows me to post data via WebAPI and retrieve it using a secret / strong key. Data would be encrypted using AES256. All the user would have to do is call one API and the data would be stored.

I have an idea for this and will be writing it up in one of these future articles.

Article History

2022-01-07 : Published First release