loading...
Cover image for How to build a Web App, Part 3

How to build a Web App, Part 3

gtanyware profile image Graham Trott Updated on ・12 min read

<<< Introduction | <<< Setting Up | Words, words, words >>>

Telling Stories

(for which the anagram finder gave me I snort gilt eels. It's a lie, I tell you!)

In this article I'll describe how to code the anagram finder web app. As before, my target audience is people with levels of technical skill ranging from near-beginner to intermediate.

In a previous article (not in this series) I wrote about Story Driven Design. Stories are written by customers who want a website built. These people are usually not coding experts but they know what they want and how to describe it. Here we have a pretty simple web app so the stories should be simple too. Here's the app:

Anagram Finder

and here are the stories that describe how it looks and functions:

"The app presents a text field in which to type a line of text, a button labeled Run and another labeled Clear."

"When the app starts, the text field is preloaded with the text it held the last time it was used (if this is not the first time)."

"When the user clicks Run the program calls the anagram finder to look for dictionary words that together use up all the letters in the text given. The label of the button changes to Stop."

"When Stop is clicked, the search stops and the button label changes to Continue."

"When Continue is clicked, the search resumes."

"When Clear is clicked the list of results is cleared."

"The page displays the list of anagrams as they are found, in a panel below the text and buttons, sorted alphabetically."

and so on.

How to code stories

Stories of this kind form the basis of a contract between the customer and the programmer and are what the delivered product will be verified against. The stories themselves are rarely cast in stone; they frequently undergo changes in the light of experience and feedback from users, but by contrast the anagram finder algorithm is likely to be fixed for all time. So it makes little sense to combine both these items into the same coding structure. Instead, we have two distinct programming paradigms:

At the heart of the web app is the anagram finder; a component that takes a string and returns a list of strings. This works best as a standalone JavaScript module that exposes whatever API is required to do that job. You may have written it yourself or you may have found a suitable component in a repository somewhere. It's quite likely that neither you nor your customer will ever have the need to poke about inside it.

Around this is the user interface, implementing the user stories, which are likely to be subject to regular change and refinement. You can do this in regular JavaScript but the likelihood is that only you, the original author, will be able to read and understand it at a glance. Anyone else will have to pick their way through it each time. I argue regularly that this represents a potential maintenance liability and an addition to the Total Cost of Ownership (TCO) of the project, so it's here that we should be willing to consider alternative techniques.

Why EasyCoder?

There are few web pages so complex they can only be described using advanced computer code. Every one I can think of can be - and usually is - described in English, and it's very helpful if the code keeps its stories visible so the owners of products can look inside them and verify they do as expected.

I have met dedicated JavaScript/React programmers who disagree strongly with this. They believe that their customers should be discouraged from looking into their code, but I think they are missing a key point. If your customers - the domain experts - are able to read your code then they may spot something you overlooked. Customers are not fools and many take a keen interest in seeing what they have paid for. They have insights that we as programmers may lack.

EasyCoder is an English-like programming language, designed for browsers and delivered as a WordPress plugin, that looks very unlike most other programming languages. It's designed to let you code with scripts that visibly correspond to the original stories; scripts that can be read by the domain expert who wrote the stories as well as by the programmer who turned them into code. When changes are needed it's much easier to do it at this level than where the stories are buried in a huge mass of JavaScript that only a skilled programmer can read.

At the very least you can regard the following as pseudo-code that expresses user stories in a verifiable form, even if you then choose to re-implement it with your favorite tools.

The EasyCoder script

EasyCoder scripts live in the web page or are loaded from somewhere else. They are simply chunks of text that must be compiled by the EasyCoder plugin before they can do anything.

Because we're building the app as a WordPress page we don't have access to the root of the DOM tree (the <body> element), so to provide somewhere for the app to live we need to add a <div> to the page, as follows, with an id the script will look for and attach one of its own variables:

<div id="anagrams"></div>

The EasyCoder script lives in a special <pre> block:

<pre id="easycoder-script">
...
</pre>

When the page has loaded, the EasyCoder plugin looks for the <pre> block, compiles its contents and runs them.

The stories tell us we need an input box for the user to type some text. We'll also have some buttons and a panel to hold the anagrams as they arrive. Here's the complete EasyCoder script that corresponds to the stories:

  h2 Title
  div Root
  div InputDiv
  div ResultsDiv
  div ResultDiv
  div Label
  div Padding
  input Text
  button RunButton
  button ClearButton
  variable Anagrams
  variable Phrase
  variable Words
  variable Index
  variable Running
  variable Results
  variable Keys

! Attach to the DOM element on the page
  attach Root to `anagrams`
! Styling is different for mobile
  if mobile set the style of Root to `width:100%`
  else set the style of Root to `width:100%;margin:1em`

  create Title in Root
  set the style of Title to `text-align:center`
  set the content of Title to `Anagram Finder`

! Create a separate DIV for all the form components
  create InputDiv in Root
  if mobile set the style of InputDiv to `display:flex;margin: 0.5em`
  else set the style of InputDiv to `display:flex;margin-top:1em`

! "Padding" is reused every time we want a separator
  create Padding in InputDiv
  set the style of Padding to `flex:2`

  create Text in InputDiv
  set the style of Text to `flex:76`
! Retrieve the text from last time
  get Phrase from storage as `anagram-text`
  set the text of Text to Phrase

  create Padding in InputDiv
  set the style of Padding to `flex:2`

  create RunButton in InputDiv
  set the style of RunButton to `flex:10`
  set the text of RunButton to `Run`

  create Padding in InputDiv
  set the style of Padding to `flex:2`

  create ClearButton in InputDiv
  set the style of ClearButton to `flex:10`
  set the text of ClearButton to `Clear`
  on click ClearButton
  begin
    clear ResultsDiv
    clear Results
  end

! A label to go under the form
  create Label in Root
  set the style of Label to `margin: 0.5em 0.5em 0 0.5em`
  set the content of Label to `Loading a word list...`

! The DIV to hold all the found anagrams
  create ResultsDiv in Root
  set the style of ResultsDiv to `margin: 0.5em 0.5em 0 0.5em`

! Load the anagram finder JS file
  require `https://cors.io/?https://raw.githubusercontent.com/gtanyware/EasyCoder/master/demo/anagrams.js`
! Load a list of 33,300 English words
  require `https://cors.io/?https://raw.githubusercontent.com/gtanyware/EasyCoder/master/demo/words.js`

  set the content of Label to `No anagrams found (yet):`
  on click RunButton go to Run

! Wait for the user to do something
  stop

! When the user clicks Run...
Run:
  put Text into storage as `anagram-text` ! Remember it for next time
  json set Results to object ! Make Results an empty JSON variable
Continue:
! Reprogram the button
  set the text of RunButton to `Stop`
  on click RunButton
  begin
    clear Running
!   Reprogram it again
    set the text of RunButton to `Continue`
    on click RunButton go to Continue
  end

! Set the running flag and keep going as long as it remains set
  set Running
  while Running
  begin
!   This is where we call the anagram finder library module
    put anagrams of Text into Anagrams
!   It returns JSON properties "status" and "words"
    if property `status` of Anagrams is `found`
    begin
      put property `words` of Anagrams into Words
      json sort Words
!     Build a phrase with the words of this anagram
      put empty into Phrase
      put 0 into Index
      while Index is less than the json count of Words
      begin
        put Phrase cat element Index of Words cat ` ` into Phrase
        add 1 to Index
      end
!     Check if this phrase is already a property of Results
      if property Phrase of Results is empty
      begin
!       It isn't, so add it, with an arbitrary value (that we don't use)
        set property Phrase of Results to true
!       Extract the phrases as an array
        put the json keys of Results into Keys
        json sort Keys
!       Show how many results we have
        set the content of Label to the json count of Keys cat ` anagrams found:`
!       Rebuild the list of phrases
        clear ResultsDiv
        put 0 into Index
        while Index is less than the json count of Keys
        begin
          create ResultDiv in ResultsDiv
          set the content of ResultDiv to element Index of Keys
          add 1 to Index
        end
      end
    end
!   Allow the CPU to cool off (critical!)
    wait 2 ticks
  end
  stop

Anyone who was around in the '80s and came across HyperCard on the early Macintosh may find this slightly familiar-looking. This is not a coincidence; EasyCoder was inspired by HyperTalk, the language inside that "insanely great" product that lives on in part as AppleScript. If there's a guiding principle it's that readability is best ensured by minimal use of symbols and maximum adherence to plain English syntax.

EasyCoder looks very different to modern computer languages, having few symbols and lacking concepts like block structuring and parameterization that are familiar to experts but which confuse everyone who isn't a programmer. Like SQL it sits in the middle ground between the machine and the human and is understandable by both.

Now for how it works. I've annotated it pretty heavily with comments (the exclamation marks); more than is generally regarded as necessary for good documentation, but this is the first time you'll have seen this syntax. The first half of the script sets up the screen, starting with a list of variables. The convention is for names to all start with capital letters, like they do in English. Many of these are types that correspond to DOM elements; the rest are plain variables to hold numbers or text.

The first action is to attach the Root variable to the <div> we set up earlier. From now on, anything we do with or to Root will act on the <div> itself. We then test if the app is running in a smartphone browser, and apply suitable styling. As you can see, styles can be applied inline. Purists who object to this way of working are free to set a class attribute on each element and create a separate stylesheet, but it's far simpler for scripted elements to have styles set as they are created.

There then follows a series of commands that create the various parts of the display, item by item. In EasyCoder, when we create DOM elements we have to specify a parent element that already exists. I've put the text field and the buttons into their own <div> and called it InputDiv, then all the rest go straight into Root. I'm hoping that even readers who are not programmers will find it easy to follow - that's the way it's intended to be.

The InputDiv uses a flex display attribute and I find it helps when distributing components to use percentages that add up to 100. There's a line that takes a value from storage and puts it into the text field, and another that saves the current field value when the Run button is clicked. These use browser storage, that lets you persist data between visits to a site.

Having set up the screen elements we now have a couple of require lines. These commands load JavaScript files and insert them into the HEAD of the document, and the strange URLs are needed to avoid cross-domain request refusals that exist to prevent browsers from accepting potentially dangerous executable code. One of these files is the anagram finder component we will be building in the next part of this series; the other is a dictionary - a list of words. It looks like this:

const EasyCoder_words = [
  `a`,
  `aah`,
  `aardvark`,
  `abacus`,
  `abacuses`,
  `abalone`,
  ...

and continues for another 33,000 or so lines. If you substitute a list of French, German or Spanish words it will work just as well but in the chosen language (though I have no idea how to deal with accented characters). Word lists can be found on the Internet with a bit of hunting.

These two items can take a while to download, so the text of Label is used to inform the user that something is happening.

At the end of the screen setup code there's an on click command to detect when the user clicks the Run button and transfer control to the program label Run:.

When it finishes setting up the program stops and wait for something to happen. When the click occurs, first there's the rather odd-looking command json set Results to object. This initializes the variable to be an empty JavaScript object (i.e. not an array).

The script reprograms the Run button so we can use it to stop the run, then enters a while loop, where there's the line

put anagrams of Text into Anagrams

which is the syntax we'd like to use to call our anagram finder library, but here we have a problem. This syntax is unknown to EasyCoder so we're going to have to do something special to handle it. Before I say any more about that I'll just finish describing the code.

When we ask the anagram finder to look for anagrams, using the command above, it comes back with a list of words and a success/fail flag. We're only interested when the search succeeds, so when it fails (the more common occurrence) the whole of the rest of the code is skipped and we ask it to try again. It's most important that we put a short delay in, though, otherwise the computer, with no chance to rest, will rapidly overheat and fail to respond to anything except the power button. EasyCoder monitors its own code to prevent this kind of thing happening, but here all the work is being done by an external module. A delay of 20ms is long enough for it to cool a little, and just as importantly, because of JavaScript's single threading model this is the only opportunity for button clicks to be acted upon.

On the less frequent occasions where the search succeeds we take the list of words, sort them into alphabetical order and put them all on one line with a space between each one. Then we check the Results object to see if the phrase we have was already there. If not, we add it as a property of that object, extract the object keys, sort them and write them to the results panel in the display, with a line at the top saying how many anagrams were found. Each anagram goes in its own <div> on the off-chance we might want in the future to be able to click each one and do something with it.

You'll see the word json occurring quite frequently. This is because the handling of JSON-formatted strings isn't standard EasyCoder functionality and is done by a plugin language package. To avoid the syntax becoming ambiguous, the plugin prefixes lesser-used JSON features with the word json.

Extending the language

I promised I'd deal with the command to call the anagram finder module, which is unrecognized by standard EasyCoder. Other languages do this with functions but EasyCoder demands that all additions extend the language seamlessly. The language is comprised of plugin packages, each one handling a particular vocabulary and syntax, such as for Google Maps, SVG graphics or JSON, so this is a case for adding a new package, to implement a single command that calls the anagram finder. (Or you could add the command to an existing package, if you have one.)

It's not difficult to add packages but it's completely specific to EasyCoder. Since these articles are mostly about web apps I'll leave it to the end and put it in an appendix article. It's only going to be of interest to people who actually use EasyCoder. If anyone has been following the series but coding the UI directly in JavaScript they will have no trouble interfacing to the anagram finder that's coming up in the next part of the series.

Coming up...

In the next part of this series I'll show you what the anagram finder JavaScript module looks like. If in the meantime you'd like to play with anagrams you can find the app at https://easycoder.software/anagrams.

Title photo by Γ‰mile Perron on Unsplash

Posted on by:

gtanyware profile

Graham Trott

@gtanyware

Software Engineering relic with a keen interest in making programming more accessible to ordinary people.

Discussion

markdown guide
 

Thank you for this show case! Having put the code and then the explanation was a very good way to show that even without any understanding of the EasyCoder syntax it remains actually understandable. Great job! Cannot wait for the next part of this serie :)

 

Thank you K. The hardest part about writing is to figure who your audience is so you can get the level right for them without boring others with more experience. There's often no way to tell how well you do, so feedback is soooo useful.