Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
.Go Programming Blueprints

You're reading from   .Go Programming Blueprints Build real-world, production-ready solutions in Go using cutting-edge technology and techniques

Arrow left icon
Product type Paperback
Published in Oct 2016
Publisher Packt
ISBN-13 9781786468949
Length 394 pages
Edition 2nd Edition
Languages
Arrow right icon
Author (1):
Arrow left icon
Mat Ryer Mat Ryer
Author Profile Icon Mat Ryer
Mat Ryer
Arrow right icon
View More author details
Toc

Table of Contents (13) Chapters Close

Preface 1. Chat Application with Web Sockets 2. Adding User Accounts FREE CHAPTER 3. Three Ways to Implement Profile Pictures 4. Command-Line Tools to Find Domain Names 5. Building Distributed Systems and Working with Flexible Data 6. Exposing Data and Functionality through a RESTful Data Web Service API 7. Random Recommendations Web Service 8. Filesystem Backup 9. Building a Q&A Application for Google App Engine 10. Micro-services in Go with the Go kit Framework 11. Deploying Go Applications Using Docker Appendix. Good Practices for a Stable Go Environment

Building an HTML and JavaScript chat client

In order for the users of our chat application to interact with the server and therefore other users, we need to write some client-side code that makes use of the web sockets found in modern browsers. We are already delivering HTML content via the template when users hit the root of our application, so we can enhance that.

Update the chat.html file in the templates folder with the following markup:

<html> 
  <head> 
    <title>Chat</title> 
    <style> 
      input { display: block; } 
      ul    { list-style: none; } 
    </style> 
  </head> 
  <body> 
    <ul id="messages"></ul> 
    <form id="chatbox"> 
      <textarea></textarea> 
      <input type="submit" value="Send" /> 
       </form>  </body> 
</html> 

The preceding HTML will render a simple web form on the page containing a text area and a Send button this is how our users will submit messages to the server. The messages element in the preceding code will contain the text of the chat messages so that all the users can see what is being said. Next, we need to add some JavaScript to add some functionality to our page. Underneath the form tag, above the closing </body> tag, insert the following code:

<script  src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"> </script> 
    <script> 
      $(function(){ 
        var socket = null; 
        var msgBox = $("#chatbox textarea"); 
        var messages = $("#messages"); 
        $("#chatbox").submit(function(){ 
          if (!msgBox.val()) return false; 
          if (!socket) { 
            alert("Error: There is no socket connection."); 
            return false; 
          } 
          socket.send(msgBox.val()); 
          msgBox.val(""); 
          return false; 
        }); 
        if (!window["WebSocket"]) { 
          alert("Error: Your browser does not support web  sockets.") 
        } else { 
          socket = new WebSocket("ws://localhost:8080/room"); 
          socket.onclose = function() { 
            alert("Connection has been closed."); 
          } 
          socket.onmessage = function(e) { 
            messages.append($("<li>").text(e.data)); 
          } 
        } 
      }); 
    </script> 

The socket = new WebSocket("ws://localhost:8080/room") line is where we open the socket and add event handlers for two key events: onclose and onmessage. When the socket receives a message, we use jQuery to append the message to the list element and thus present it to the user.

Submitting the HTML form triggers a call to socket.send, which is how we send messages to the server.

Build and run the program again to ensure the templates recompile so these changes are represented.

Navigate to http://localhost:8080/ in two separate browsers (or two tabs of the same browser) and play with the application. You will notice that messages sent from one client appear instantly in the other clients:

Building an HTML and JavaScript chat client

Getting more out of templates

Currently, we are using templates to deliver static HTML, which is nice because it gives us a clean and simple way to separate the client code from the server code. However, templates are actually much more powerful, and we are going to tweak our application to make some more realistic use of them.

The host address of our application (:8080) is hardcoded at two places at the moment. The first instance is in main.go where we start the web server:

if err := http.ListenAndServe(":8080", nil); err != nil { 
  log.Fatal("ListenAndServe:", err) 
} 

The second time it is hardcoded in the JavaScript when we open the socket:

socket = new WebSocket("ws://localhost:8080/room"); 

Our chat application is pretty stubborn if it insists on only running locally on port 8080, so we are going to use command-line flags to make it configurable and then use the injection capabilities of templates to make sure our JavaScript knows the right host.

Update your main function in main.go:

func main() {   
  var addr = flag.String("addr", ":8080", "The addr of the  application.") 
  flag.Parse() // parse the flags 
  r := newRoom() 
  http.Handle("/", &templateHandler{filename: "chat.html"}) 
  http.Handle("/room", r) 
  // get the room going 
  go r.run() 
  // start the web server 
  log.Println("Starting web server on", *addr) 
  if err := http.ListenAndServe(*addr, nil); err != nil { 
    log.Fatal("ListenAndServe:", err) 
  } 
} 

You will need to import the flag package in order for this code to build. The definition for the addr variable sets up our flag as a string that defaults to :8080 (with a short description of what the value is intended for). We must call flag.Parse() that parses the arguments and extracts the appropriate information. Then, we can reference the value of the host flag by using *addr.

Note

The call to flag.String returns a type of *string, which is to say it returns the address of a string variable where the value of the flag is stored. To get the value itself (and not the address of the value), we must use the pointer indirection operator, *.

We also added a log.Println call to output the address in the terminal so we can be sure that our changes have taken effect.

We are going to modify the templateHandler type we wrote so that it passes the details of the request as data into the template's Execute method. In main.go, update the ServeHTTP function to pass the request r as the data argument to the Execute method:

func (t *templateHandler) ServeHTTP(w http.ResponseWriter, r  *http.Request) { 
  t.once.Do(func() { 
    t.templ =  template.Must(template.ParseFiles(filepath.Join("templates",
      t.filename))) 
  }) 
  t.templ.Execute(w, r) 
} 

This tells the template to render itself using data that can be extracted from http.Request, which happens to include the host address that we need.

To use the Host value of http.Request, we can then make use of the special template syntax that allows us to inject data. Update the line where we create our socket in the chat.html file:

socket = new WebSocket("ws://{{.Host}}/room"); 

The double curly braces represent an annotation and the way we tell our template source to inject data. The {{.Host}} is essentially equivalent of telling it to replace the annotation with the value from request.Host (since we passed the request r object in as data).

Tip

We have only scratched the surface of the power of the templates built into Go's standard library. The text/template package documentation is a great place to learn more about what you can achieve. You can find more about it at http://golang.org/pkg/text/template.

Rebuild and run the chat program again, but this time notice that the chatting operations no longer produce an error, whichever host we specify:

go build -o chat 
./chat -addr=":3000" 

View the source of the page in the browser and notice that {{.Host}} has been replaced with the actual host of the application. Valid hosts aren't just port numbers; you can also specify the IP addresses or other hostnames provided they are allowed in your environment, for example, -addr="192.168.0.1:3000".

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime