Building a Facebook-like Comment Box: A React Tutorial in Elm
I. Introduction
Elm is a new programming language that compiles to JavaScript. It is a functional programming language that features static typing, ensuring type-safety in your web apps and catching common errors.
React is a library for creating user interfaces in JavaScript. What makes it cool is how fast and performant it is when rendering, thanks to its use of the concept of the Virtual DOM (Document Object Model). The virtual DOM lets changes be made in batches before they are applied to the DOM.
While the performance of React is awesome, at a certain point, you will notice that having React just isn't enough. You'll also want Redux, Flux, or some other data flow library installed. You will want Webpack and Babel installed so that you can use the latest features of ES6 (also known as EMCAScript 2016). Then you'll probably want to add type-checking and static types at that point, so you will also need to install Flow, a JavaScript type-checker.
Elm will give all the goodies that React, Redux/Flux, and Flow give you but as a collective programming language rather than as individual libraries.
II. What you will learn
In this tutorial, you will learn how to use Elm to accomplish the same thing as Facebook's React tutorial. That means we will be creating a CommentBox component where a user can enter a new comment and see a list of existing comments.
You will learn how to do the following in Elm:
- How to use Elm Reactor to compile and run an Elm program
- How to create a module
- How to create a view
- Update the HTML based on user input
III. Installing Elm
The quickest way to get started with Elm is to install it using NPM, the Node Package Manager.
You can do that with this command:
npm install -g elm
IV. Running Elm apps
A. Elm reactor
The Elm reactor will run a web server and host the Elm files in your directory. It will compile each Elm file when it is run.
To run it, do this:
elm reactor
The web server will be running on localhost:8000
and will display a list of Elm files when you go to that URL in a web browser. When you click a file, it will be compiled from Elm into JavaScript and run in the browser. This makes it fast to iterate through the development of a component or of a whole web application.
B. Elm REPL, interactive command-line interface
You can also run Elm in a REPL, a Read-Evaluate-Print-Loop, which is also known as an interactive command-line interface. You may be familiar with this in other languages like Python and Ruby's IRB. The REPL lets you experiment with the language on the command-line, typing in various expressions to see what happens.
V. Setting up the project
We will be creating a comment box component using Elm, it is similar to the comment box you see on blogs such as Wordpress and websites such as Facebook. It will show a list of comments about an article along with a form for a user to enter their own comment and submit it.
First, let's create a new directory named elm-tutorial
. On the command-line, when you are in the directory, you will need to create the Elm project with the base Elm packages.
You can do this by typing in:
elm package install
You will be asked whether you want to install three elm-lang
packages (core, html and virtual-dom), enter "Y" and press Enter to install those packages. This will create a new file named elm-package.json
and a directory named elm-stuff
. In elm-package.json
you will see something that looks like a typical NPM-based package.json
file. You can fill this file out later. The elm-stuff
directory is similar to the node_modules
directory when using NPM.
VI. Creating the CommentBox component
Open up the file CommentBox.elm
. This is where we're doing to define our comment box component and display it to the user.
A. Importing what we need
We begin by importing the modules and libraries we will need.
A.1. Importing HTML elements for rendering
First up is the HTML library which is used for rendering HTML DOM elements such as H1
(heading level 1) and P
(paragraphs):
import Html exposing (..)
A.2. Importing HTML attributes
Then we import the attributes for the HTML elements, things like href
and class
:
import Html.Attributes exposing (..)
A.3. Importing HTML events
Finally, we also import the events that will be handled like onClick
and onInput
:
import Html.Events exposing (..)
B. Representing a Comment as an object
In Elm, as in other functional languages like Haskell and Scheme, it has something called a record type. It is similar to a class but without any methods of its own, it is more similar to a struct in the C programming language.
To represent a comment on an article, we will define the Comment record type. It will contain a user's name and some text:
type alias Comment =
{ name : String
, description : String
}
C. Getting the name of the author of a comment
When displaying comments, we want to let the user know if they've already commented on the article. We want to check this by comparing the author of the comment with the current user's name. When the name of the comment's author matches the name of the current user, then their name will appear as "[NAME] (You)" to show that they've already posted a comment on the article.
We can define a function, author
, that does this, like so:
author : String -> Comment -> String
author currentUser { name } =
if currentUser == name then
name ++ " (You)"
else
name
The first part is the type signature of the function, the second part is the function definition itself. The type signature of a function doesn't always need to be defined but it is a very good habit and gives clarity to the writing of code.
D. Elm components display a model
Every Elm component that is displayed has a model definition. This model contains all the information needed to render the component. These are similar to the properties and state of a React component.
Our comment box is going to need three pieces of data; the text for a new comment, the name of the author of the new comment, and the list of existing comments that are being displayed:
type alias Model =
{ newCommentText : String
, newCommentUser : String
, comments : List Comment
}
The model is defined as a type alias because…
E. How to add new comments with Messages and Events
To get something to happen in Elm, you send messages. Messages are a type that you define for each Elm component; they are the events that your component will be able to handle and respond to.
In our case, the comment box component has these events:
- The user has entered a username for a new comment;
- the user has entered some text for a new comment;
- or, the user has pressed "add comment".
Let's define the Message types for our component:
type Msg
= AddComment
| ChangeNewCommentUser String
| ChangeNewCommentText String
Next, we define the update
function. In this function, we will be checking which type of message has been sent and then updating the model in particular ways.
When the AddComment
message is received, we will be creating a new comment from the username and text entered by the user and adding it as the first element of the comments list. We also clear the comment text so that the user can add a new comment.
When the ChangeNewCommentUser
message is received, we update the new comment username based on the value received.
When the ChangeNewCommentText
message is received, we update the new comment's text based on the value received.
update : Msg -> Model -> Model
update msg model =
case msg of
AddComment ->
let
comment = { name = ( "User " ++ model.newCommentUser)
, description = model.newCommentText
}
in
{ model
| comments = comment :: model.comments
, newCommentText = ""
}
ChangeNewCommentUser username ->
{ model | newCommentUser = username }
ChangeNewCommentText description ->
{ model | newCommentText = description }
F. Displaying the components and sending messages to trigger events
Now we define some view functions to display the pieces of the page.
F.1. Displaying a list of comments
The first view function will display the existing list of comments. And since we're displaying a list, we also need another helper function to display each individual comment:
commentView : Comment -> Html Msg
commentView comment =
div [ class "comment" ]
[ text (author "User A" comment)
, text " - "
, text comment.description
]
commentList : Model -> Html Msg
commentList model =
div [ class "comment-list" ]
(List.map commentView model.comments)
F.2. Displaying the input for a username
The second view function will display the input field for a user to enter their username. Whenever the user types into the box, the ChangeNewCommentUser
message will be sent to the update
function.
inputUsername : Model -> Html Msg
inputUsername model =
div []
[ label[] [ text "User:" ]
, input [ class "comment-input-user"
, type' "text"
, value model.newCommentUser
, onInput ChangeNewCommentUser
]
[]
]
F.3. Displaying the input for the comment text
The third view function will display the text area for a user to enter their new comment. Whenever the user types into the box, the ChangeNewCommentText
message will be sent to the update
function.
inputComment : Model -> Html Msg
inputComment model =
div []
[ label [] [ text "Comment:" ]
, textarea [ class "comment-input-text"
, onInput ChangeNewCommentText
, value model.newCommentText
]
[]
]
F.4. Displaying the button to add a comment
The fourth view function will display a button. When the button is pressed it will send the AddComment
message. It will trigger the adding of the new comment to the list of comments:
inputButton : Html Msg
inputButton =
div []
[ button [ class "comment-button-add"
, onClick AddComment
]
[ text "Add Comment" ]
]
F.5. Combining the view functions into one view
Now we define the view
function which will use the view functions to put together the components for displaying to the user:
view : Model -> Html Msg
view model =
div [ class "comment-box" ]
[ commentList model
, div [ class "comment-input" ]
[ inputUsername model
, inputComment model
, inputButton
]
]
G. Putting It All Together
Finally, we need to display the comment list and comment input box on the screen. To do this, we're going to define the main entry point of this module so that Elm knows what to render in the browser, how to start up the web application, which function does the updating and what data flow subscriptions exist.
At the top of the file, we need to import the Html.App module which provides the entry point definition function:
import Html.App
Then at the bottom of the file, we define the initial state of the app. We're going to start with blank fields for new comment text and user name, and we're going to display a list of two comments:
model =
{ newCommentText = ""
, newCommentUser = ""
, comments = [ { name = "User A" , description = "Hi!" }
, { name = "User B" , description = "Hello world!" } ]
}
Then we define main entry point using the Html.App.beginnerProgram
function:
main =
Html.App.beginnerProgram
{ model = model
, view = view
, update = update
}
The beginnerProgram
function is a simplified way to define the entry point of an Elm program. It lets you specify the model of the component, the view that will display the model and the function that will be updating the model. In contrast, a typical Elm program will define the initial state of the model and any data flow subscriptions. The simplified beginnerProgram
function lets us get started more quickly.
This is how it looks when we put it all together:
1 -- react-tutorial.elm
2 import Html exposing (..)
3 import Html.Attributes exposing (..)
4 import Html.Events exposing (..)
5 import Html.App
6
7 type alias Comment =
8 { name : String
9 , description : String
10 }
11
12 author : String -> Comment -> String
13 author currentUser { name } =
14 if currentUser == name then
15 name ++ " (You)"
16 else
17 name
18
19 type alias Model =
20 { newCommentText : String
21 , newCommentUser : String
22 , comments : List Comment
23 }
24
25 type Msg
26 = AddComment
27 | ChangeNewCommentUser String
28 | ChangeNewCommentText String
29
30 update : Msg -> Model -> Model
31 update msg model =
32 case msg of
33 AddComment ->
34 let
35 comment = { name = ( "User " ++ model.newCommentUser)
36 , description = model.newCommentText
37 }
38 in
39 { model
40 | comments = comment :: model.comments
41 , newCommentText = ""
42 }
43
44 ChangeNewCommentUser username ->
45 { model | newCommentUser = username }
46
47 ChangeNewCommentText description ->
48 { model | newCommentText = description }
49
50 commentView : Comment -> Html Msg
51 commentView comment =
52 div [ class "comment" ]
53 [ text (author "User A" comment)
54 , text " - "
55 , text comment.description
56 ]
57
58 commentList : Model -> Html Msg
59 commentList model =
60 div [ class "comment-list" ]
61 (List.map commentView model.comments)
62
63 inputUsername : Model -> Html Msg
64 inputUsername model =
65 div []
66 [ label [] [ text "User:" ]
67 , input [ class "comment-input-user"
68 , type' "text"
69 , value model.newCommentUser
70 , onInput ChangeNewCommentUser
71 ]
72 []
73 ]
74
75 inputComment : Model -> Html Msg
76 inputComment model =
77 div []
78 [ label [] [ text "Comment:" ]
79 , textarea [ class "comment-input-text"
80 , onInput ChangeNewCommentText
81 , value model.newCommentText
82 ]
83 []
84 ]
85
86 inputButton : Html Msg
87 inputButton =
88 div []
89 [ button [ class "comment-button-add"
90 , onClick AddComment
91 ]
92 [ text "Add Comment" ]
93 ]
94
95 view : Model -> Html Msg
96 view model =
97 div [ class "comment-box" ]
98 [ commentList model
99 , div [ class "comment-input" ]
100 [ inputUsername model
101 , inputComment model
102 , inputButton
103 ]
104 ]
105
106 model =
107 { newCommentText = ""
108 , newCommentUser = ""
109 , comments = [ { name = "User A" , description = "Hi!" }
110 , { name = "User B" , description = "Hello world!" } ]
111 }
112
113 main =
114 Html.App.beginnerProgram
115 { model = model
116 , view = view
117 , update = update
118 }
VII. Using an Elm Component on a regular web page
By far, the coolest thing about Elm is how easy it is to insert Elm components into existing web pages and web applications. It doesn't matter if you're using Ember.js, AngularJS, or some other front-end web framework, you can still use Elm components. What makes this cool is that when you're working with existing legacy code, you can create new components in Elm and take advantage of the performance and type-checking safety of Elm.
A. Compiling the Elm file
Take the Elm file that we wrote, react-tutorial.elm and compile it using elm:
elm make react-tutorial.elm
The file generated will be index.html
and it will contain the component and all the JavaScript code needed for the component. The file generated for me was around 8000 lines of code. While this is great for playing around with Elm and sharing the HTML file will let you share the component, it isn't the same as real-world production settings.
Let's try that again, this time by compiling the Elm component into a JavaScript file:
elm make react-tutorial.elm --output comment-box.js
When you open up comment-box.js
it will also be around 8000 lines of code. It includes all of Elm, all the Elm modules we used and the main component code.
B. Inserting the component
First we're going to set up our basic HTML page to have a div whose id is "comment-box". This is the element that will be taken over by Elm to display the comment box component:
<h2>Hello Elm!</h2>
<p>This is an article, below is the comment box.</p>
<div id="comment-box"></div>
To insert the Elm component onto the page, we're going to use the script
tag to import the JavaScript file that we just generated.
<script src="comment-box.js"></script>
Then we're going to insert the comment box into the element whose id is "comment-box". The steps will be similar when integrating Elm into frameworks like AngularJS and Ember.js:
<script>
var node = document.getElementById("comment-box");
var app = Elm.Main.embed(node);
</script>
C. Just for fun: Styling the comment box the Facebook way
Just to match the style of Facebook, let's add some CSS for the Elm component.
Here's how it looks:
.comment-box {
border-radius: 3px;
border: 1px solid #ccccff;
padding: 10px 3px;
}
.comment-input {
margin-top: 10px;
}
.comment-input label {
color: #5555cc;
}
.comment {
padding: 10px;
margin-bottom: 5px;
background-color: #eeeeff;
.comment-button-add {
border-radius: 2px;
border: 1px solid #555555;
color: #333333;
background-color: #ffffff;
box-shadow: 0px 1px 5px 0px #aaa;
}
D. Putting together the final HTML file
And finally, let's see how it looks when it's all put together. We're setting the style of the component, then including the JavaScript file that was compiled by Elm.
1 <html>
2 <style>
3 .comment-box {
4 border-radius: 3px;
5 border: 1px solid #ccccff;
6 padding: 10px 3px;
7 }
8
9 .comment-input {
10 margin-top: 10px;
11 }
12
13 .comment-input label {
14 color: #5555cc;
15 }
16
17 .comment {
18 padding: 10px;
19 margin-bottom: 5px;
20 background-color: #eeeeff;
21 }
22
23 .comment-button-add {
24 border-radius: 2px;
25 border: 1px solid #555555;
26 color: #333333;
27 background-color: #ffffff;
28 box-shadow: 0px 1px 5px 0px #aaa;
29 }
30 </style>
31 <body>
32 <h2>Hello Elm!</h2>
33 <p>This is an article, below is the comment box.</p>
34 <div id="comment-box"></div>
35 <script src="comment-box.js"></script>
36 <script>
37 var node = document.getElementById("comment-box");
38 var app = Elm.Main.embed(node);
39 </script>
40 </body>
41 </html>
VIII. Let's see how it turned out
Here's how it looks in the browser:
Thanks for reading this tutorial on using the Elm functional programming language for front-end web development. It's an excellent alternative to other languages that compile to JavaScript, like TypeScript or Babel/ES6. Elm has the ideas of immutability and data flow subscriptions built into, along with type-checking. It's a strong alternative to JavaScript and as shown in this tutorial, it's also an easy-to-learn alternative to libraries and frameworks like React.js.
Try out Elm for a small component like a comment box or a survey form in your next website. Happy hacking!
I’m running the Elm app for Fnaf . Thank you so much for the tutorial, it’s really helpful
This is a great tutorial, thanks for sharing. It’s amazing how popular React is these days, totally worth learning, especially considering the salary rates - source. The majority of web developers have a good impression of React and either use it already or would like to learn it. Vue.js is also very popular and devs are really satisfied with it. Do you happen to have any tutorial on Vue?
if any one want help in completing these tasks, they can contact customer care service. they will resolve your issue, as soon as possible. I am sharing here some links of tech support services.
http://www.gonetech.net/facebook-helpline-number/
http://quicksupportservice.com/facebook-customer-support-number-australia.html/