Hangman is a popular game in which users have to guess the characters in a word with missing letters. Each time they guess a character that does not exist – a man who is being hanged is slowly drawn on the screen. When they get hanged or they complete the word, a new word is given for them and they start anew.
Node.js
First, we load Express which is a framework for Node.js that provides routing and other highly useful utility functions Require just loads some functions, strings, objects or whatever the JavaScript file that we require wants to share.
var express = require("express");
var app = express();
In this case, requiring express returns a function which we call and save the results in the app variable. To add the express module to our server, we need npm(which was probably installed when you downloaded Node.js). In the Terminal/Command line just type npm install express –save while being situated in the directory where your project is.
Thereafter, we configure our application to use the ejs view/templating engine. Ejs uses JavaScript syntax and it is really easy to use. You load an ejs view using app.render(“viewName”, {variable1:variable1Value, variableN:variableNValue}).
Inside the view (.ejs file), we use <%= variable1 %> to print the contents of the variable in the markup and <% //if(variable1 === “Hello”) {%> to handle different JavaScript logic using JavaScript syntax (such as loops, conditionals and so on).
app.set("view engine", "ejs");
Thereafter, we tell Express that our public files (front-end JavaScript, stylesheets, images) would be located in the public folder.
app.use(express.static('public'));
Afterwards, we load a built-in module called fs (short for file system). Using it,we can read or write to files on our server.
var fs = require('fs');
Then, we define a route that will handle GET requests to the /api/words URL. The route just returns as response the contents of the enWords.txt file converted into an array of words which itself contains thousands of words, organized in a one word-per-line principle. That is why we use split to get each word as a separate array item.
app.get("/api/words", function(req,res) {
fs.readFile("./data/enWords.txt", function(err,data) {
res.send(data.toString().split("\n"));
})
})
Finally, we define a GET route our home address and display the index view (Express would search for views in the views folder) and set the server to listen for HTTP connections on a specific port as defined by the process.env.PORT property (it is used for environment variables).
app.get("/" , function(req,res) {
res.render("index", {
})
})
app.listen(process.env.PORT);
The Angular View
In the head element of our HTML view, we load Angular.js/Twitter Bootstrap and a Bootswatch theme for Bootstrap.
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<link rel="stylesheet" href="http://bootswatch.com/darkly/bootstrap.min.css">
We proceed by using the hangman module for everything in our body tag:
<body ng-app="hangman">
In it, we create a div which uses the hangIt controller. Then, inside that div we create another div where we immediately call the loadWords() function defined in our controller. This function would retrieve all words, pick a random one and save it in our controller in order to be used in the game.
<div ng-controller="hangIt" class="container-fluid">
<div ng-init="loadWords()" class='col-md-4 theGame'>
Then, we display the word as processed by our controller (it would contain missing letters) to the user in an alert paragraph.
<p class="alert alert-danger">{{ theGameWord }} </p>
After the word, we use <pre> to display the current level of completion of the ASCII drawing of the hanged man. We use the pre tag because we want to keep the formatting of the ASCII drawing string that would be in currentDrawing exactly as it is (as HTML will usually maintain only one space and ignore all newlines when displaying the page to the user if we do not use the pre tag).
<pre>{{ currentDrawing }}</pre>
<form novalidateonclick="return false;">
Thereafter, we create a form which would not be validated with HTML5 and prevent its submission.
In the form, we create an input which would be required (and validated by Angular) with the ng-required directive, we define it a maxlength of 1 (only one character can be entered) with ng-maxlength and set it as a model called guess. Thus, we can use $scope.guessin our controller to get the current value of the input as typed by the user.
<input type="text" ng-required maxlength="1" ng-maxlength="1" ng-model="guess" class="form-control">
Then, we create a button which when clicked would check if the user has entered a character that exists in the word or not, draw a new line in the ASCII drawing, update the scores or whatever.
<button ng-click="checkGuess();" class="btnbtn-lgbtn-default">Submit</button>
Thereafter, we create a paragraph which would show to the user the complete word (without any missing characters) only if the complete word is already set in the controller (showCorrectWord). We would add the completed word whenever the current round finishes and the paragraph will immediately show. We achieve this with the ng-show=”condition” directive.
<p ng-show="showCorrectWord !== '' " class="alert alert-danger"> The correct word was: {{ showCorrectWord }}</p>
Finally, we have the number of mistaken or successfully guessed words for this browsing session displayed in a paragraph at the bottom of the page.
<p class="lead"><span class="label label-danger">{{ numFailedWords }}</span> failed words and <span class="label label-success">{{ numCompletedWords}}</span> completed words</p>
Conclusion
By now, you should have a pretty clear of what Angular views are composed of. It is important to stress that if you want to validate your markup as valid HTML5 instead of typing directive attributes like ng-show, you should use data-ng-show(the directive would still be valid but it will also be considered valid markup when checking with a HTML5 validation tool such as https://validator.w3.org/).