(This guest blog was written by Wolfgang Loder, February 2017.)
Whenever web developers are
asked about a language or framework they want to try, most likely the language Elm will be mentioned.
If you have not had the chance
to look deeply into Elm, have a look at the following points:
• Elm is a functional language with many
advantages like immutable variables and higher order functions.
• The Elm platform (language, tools and libraries)
is geared towards SPA (Single Page Applications), although it is possible to
create multi-page applications with routing.
• Elm code is compiled into JavaScript, and the
resulting code is bundled with a JavaScript runtime.
• The Elm architecture is similar to a
Model-View-Controller architecture, with the wiring between parts done in the
aforementioned JavaScript runtime.
This is not an Elm primer,
so I assume you have a little experience with the Elm basics. If not, the
following paragraphs will give you an idea what can be achieved with the Elm
platform.
As much as Elm libraries try to
cover everything needed for web applications development, there are gaps and
inadequate solutions. This is why pure Elm applications may be good for demos
and simple applications, but are not always feasible for production code. We
will often have to enhance Elm code with JavaScript libraries.
What we want to build
For example, take forms. Almost
every web application needs a form in one way or another, especially if the
application is in the field of e-commerce. Let's assume we have to develop a
form for an online pizza order web app. This form is for the internal use of
employees who take orders over the phone. The requirements are:
• Choose a pizza from a drop down list.
• Once a pizza is chosen, additional toppings can
be added. This data is dependent on the pizza selection.
• The price updates dynamically depending on the pizza
and toppings selection.
• It is possible to choose more than one pizza for
each order.
• There are name, address, and phone fields, with
validation for the phone field.
• Depending on the address and pizza selection, an
approximate delivery time will be displayed.
• We assume payment on delivery.
• Submitting the form updates an order list that
is displayed on the right side of the form.
The wireframe design looks
like this:
The form is not too
complicated and could probably be coded with Elm alone, but we do not want to
re-invent the wheel. We will use Synfusion's Essential Studio for
JavaScript to make our lives easier.
Implementation
Let's look first at the
finished form - you can find the code for this article here..[DW1]
If you want to run the
example, download all files and then download Essential Studio for
JavaScript and claim a free community license
if you are eligible.
Once you have Essential Studio
on your computer, copy the files as shown in the following image into a folder
called static in the root of the project:
All other files are
downloaded into the root and the command
will install the needed Elm
packages and compile all code files into one JavaScript file (advancedform.js). The application can
then be run with elm reactor and opened at localhost:8000.
I have divided the code into
different files for easier handling. It could be all in one file, but even for
a simple project like this it is difficult to maneuver through the code. The
advantage of Elm is that the wiring between parts of the Elm architecture is
done in the background, and we only need to know how the parts fit together:
In advancedform.elm we define the function main and provide other functions to be called by the runtime for init, update, view, and subsciptions.
We won't focus on the actual Elm
implementation of this form, but on the way we can integrate Essential Studio.
In the HTML file we have to link to several stylesheet and javascript files
which are necessary to get the theming and features of Essential Studio.
The same HTML file also has
a script section to initialize components like listviews or text boxes. One
example is the code for the pizza dropdown list at the top of the form:
Essential Studio uses
jQuery, so the expression $('#pizzadd')
looks for the DOM element with the id pizzadd
- which is an input element in our case - and attaches the code for the
dropdown list to it by calling ejDropDownList.
Its parameters define, amongst others, a callback for the change event and a targetID
that defines the list that should appear in the dropdown. The markup for these
elements can be found in the Elm code in advancedformview.elm
– here is an excerpt from this file:
The targetID we defined above points to the div-element with the id pizzaddlist. In this case we have hard
coded the list.
Elm Ports
The most important
integration task is to establish communication between our Elm code and outside
JavaScript code.
The change callback function we
saw above has these contents:
This article is not the
place to explain Elm ports in detail. The gist is that ports are well defined
comunication channels either called from JavaScript code to Elm (send) or from Elm to JavaScript code (subscribe):
The first port call is
sending updated values to the Elm code. The second port call means that the
JavaScript code subscribes to an event raised by the Elm code, and whatever
function is defined will be called.
So why do we need this? The Elm
code needs to maintain the application state, and this is most easily done with
events. Whenever something changes, an event is raised and the runtime will
pass the message to the update
function in our Elm code. Most of the time this happens automatically, but the
Essential Studio components have their own event handling, and we need to push
updated values with a port call. For example, the numerical textbox changes the
value of the textbox, but does not raise an input
event. The same applies to the drop down list. Remember that all code is run on
the client, so raising an additional event does not cause huge time delays.
The second reason for using
ports is that sometimes the Elm code needs to tell the JavaScript code to
update. When we add an order to the list, the Elm runtime pushes the model
changes to the form without having to code anything. In our case, we want to
run the Essential Studio functions like ejListView
as well. This is why the Elm code sends an event to say, “Update the lists now.”
When you want to integrate components like Essential Studio, you will have to
use this technique to achieve the functionality you need.
Markup
More work needs to be done in
the view function. Defining markup
and integrating it with data is done in various ways in different frameworks,
with templates or custom attributes. Elm uses code to describe markup,
including attributes, and compiles this into HTML markup. The view code for our
simple form is almost 150 lines. Bigger projects need to be broken into
separate functions to define the markup, but it can be difficult to see exactly
what is going on, especially when markup needs to be changed, added, or
removed.
One pitfall when integrating
with Essential Studio is to know when the document is rendered and scripts can
be applied. We have wrapped all script calls to initialize elements with the
JQuery $(document).ready function to
make sure that elements are all rendered. If this is not done, then the page
won't look as intended, although it may work because the Essential Studio is
smart enough not to raise exceptions by default.
This article has shown that we
can integrate Elm code with frameworks like Essential Studio for JavaScript to
create appealing applications quickly. Our example is simple and did not save
data to a backend or retireve data from a backend database. The principles
shown in this article are valid nevertheless, and a future article may go
deeper into the code to show how to integrate a backend, for example .NET, into
an Elm application.
Read more from Wolfgang on his blog where you can also find information about his books and online courses.