Good News! We've launched an all new Chat Resource Center.
We recommend checking out our new Chat Resource Center, which includes overviews, tutorials, and design patterns for building and deploying mobile and web chat.
In this tutorial, we'll continue talking about creating multiple channels to spawn chatrooms on demand. In this case, we'll walk you through how to enable users to spawn private chat with another user on demand. This way, users can select who they want to chat with, then initiate a private chat with that user.
We've now covered both building a multiplayer game lobby with a chatroom and the different ways we can use matchmaking to connect two different users. Here's what we've covered so far:
- Part One: Series Overview and Building a Multiplayer Game Lobby
- Part Two: Adding Users and Usernames
- Part Three: Getting a List of Online Users
- Part Four: Random Matchmaking of Users
- Part Five: Skill-based Matchmaking of Users
- Part Six: Matchmaking Algorithm: Enabling Users to Challenge Other Players
- Part Seven: Create Chatrooms and Multiple Channels On Demand Tutorial
- Part Eight: Preparing for Private Chatrooms and Refactoring via Private Channels
- Part Nine: Creating Private Chat Requests with Popup Alerts
- Part Ten: JavaScript Private Chat API with Access Control
This blog post is Part Eight of PubNub Developer Evangelist Ian Jennings‘s series on creating a multiplayer game with JavaScript.
Refactoring to Object Oriented Patterns
Now that we can spawn chatrooms on demand, let's allow one user to spawn a new private chat on a private channel with another user.
You’ll first need to sign up for a PubNub account. Once you sign up, you can get your unique PubNub keys in the PubNub Developer Portal. Once you have, clone the GitHub repository, and enter your unique PubNub keys on the PubNub initialization, for example:
Up until our most recent tutorial, our code has been only functional. Like last time, our code this time will be object oriented. If we added the ability to spawn chatrooms to our old functional code, things would get confusing very quickly. We'll start by converting our functional code into object oriented code. This will make it easier for us to manage user to user communications.
Object Constructor Pattern and the “this” JavaScript Keyword
We're going to make use of what's known as the Object Constructor Pattern. Let's start with our Alarm closure example from earlier.
What if we wanted to expose the name
variable from outside the Alarm()
function? We use the this
keyword to expose the variable outside of our function.
- In JavaScript this always refers to the “owner” of the function we're executing, or rather, to the object that a function is a method of. Read more about the “this” JavaScript keyword here.
The “new” Keyword
Notice the changes made to our last lines. Specifically how we changed Alarm()
to new Alarm()
.
- The new operator creates an instance of a user-defined object type or of one of the built-in object types that has a constructor function.
Instead of simply executing Alarm()
, we call new_alarm = new Alarm()
which assigns the instance of the function to the variable new_alarm
.
We need to use the word new
because it instructs the the value of this
to be bound to the context of the function. If you forget to use the new
keyword, this will be assigned to the global object.
- If you forget to include the new prefix when calling a constructor function, then this will not be bound to the new object. Sadly, this will be bound to the global object, so instead of augmenting your new object, you will be clobbering global variables. That is really bad. There is no compile warning, and there is no runtime warning.
Douglas Crockford has a great writeup of the object constructor pattern, this
, private and public methods and more.
So now let's start refactoring our old challenge example into an Object Constructor pattern.
Self = This
Notice that instead of using this
directly, we assign var self = this
. This is because the this
variable will have a different value within nested functions. We want to ensure that we're always referring to the User
context, so we store the context of this
in self
so we can access it from inside any function.
Calling return self;
at the end of the function allows us to string function methods together. For example, we could call a_user.chat().leave()
.
Remove Data Tied to DOM
We also gain a huge organizational advantage switching to an object oriented pattern. We're able to store references to the DOM elements within the objects themselves, allowing us to modify the page without actually selecting nodes by hand.
Instead of calling $('#' + data.uuid).remove();
we can write:
The variable a_user
has stored the DOM node in $tpl
so we can access it directly instead of looking it up.
Remove pubnub.here_now()
Remember that we've been using PubNub Presence with JavaScript for user detection of user state. Notice the second parameter, state
. Now that we keep users in objects, we can keep the value of their state as a property. This means when we need to find an opponent for matchmaking, we can avoid using the pubnub.here_now()
call. We'll already have a list of users and their state ready!
We're going to support an unlimited number of users, so creating objects one at a time will quickly get messy. Instead, we'll create a factory pattern that will keep track of ourUser()
s in an array.
Object Extension
The largest change to our app are the changes to me
. Instead of me
just representing a username, we're going to make it into an object that turns the browser window into an extension of a User
.
We start out by assigning the variable self
to a new User()
with a randomName()
. This means Client()
will be an extended from User()
and have all it's properties likeUser.chat()
and User.leave()
.
We add a couple more methods to the Client: onRequest()
and onResponse()
. These handle our challenge requests and responses. We only add them to our Client instead of every User as we don't want to accidentally challenge or respond on behalf of another user.
Also, because the Client()
extends the User()
we can still get the client username from me.uuid
. We bind to the DOM events inside of the App()
object and then act on behalf of the me
object when fired.
Routing PubNub Requests
Last, we create an App()
object to initialize our application and handle incoming traffic.
Scoping
It's worth noting a couple additional changes to scope. me
turned into a global variable as is Users
and some of our global template elements. The function randomName()
is still available globally and our skill generator has been turned into randomSkill()
.
Full Refactored Code
Put it all together and we have the following.
Private Chat with Private Channels Demo
Check out the live demo below. It looks and behaves exactly as our previous example did, but the code has been organized into an object oriented pattern.
See the Pen Memewarz – Private Chat #2 by Ian Jennings (@ianjennings) on CodePen.0
In our next blog post, we'll talk about how to spawn private chatroom popups.