Sending messages on the socket

For us to be able to start sending messages and continue testing the overall functionality of our channels, we'll need some sort of input from the user to initiate the message across. To do that, we'll just add a dummy button to the page that allows us to ping the server from the client across the WebSocket. Add the following button code to that same template:

<button id="polls-ping" class="btn btn-primary">Ping Websocket</button>

We'll next need to hop back over to socket.js and add a new few lines of code that will be responsible for hooking this button up to be able to broadcast something out to the server. Open up assets/js/socket.js and add this to the bottom of the conditional (the enable-polls-channel conditional):

  document.getElementById("polls-ping").addEventListener("click", () => {
channel
.push("ping")
.receive("ok", res => console.log("Received PING response:", res.message))
.receive("error", res => console.log("Error sending PING:", res));
});

There's a little bit going on here, so let's take a few minutes to explain. First off, we grab the document out of the page and look for an element on the page with an ID of "polls-ping". Next, we add an event listener to it that will register every time a user clicks on the button. addEventListener takes a function as the second argument, so we'll use the fat arrow syntax to create a new function in-line.

Next, we use the push function on the channel object that we created. Push takes in arguments for the message we want to send out, and optionally, any additional payload information we want included as part of that message. In our case, we don't care about any payload; we just care that we're sending the "ping" message across. If we receive a successful message, we send it back out to the developer console! If we get an error message back from the server, we'll deal with that accordingly as well!

Now we'll need to actually implement the server side of this. Heading back to lib/vocial_web/channels/polls_channel.ex, you'll want to add the following function:

  def handle_in("ping", _payload, socket) do
{:reply, {:ok, %{message: "pong"}}, socket}
end

Again, we're keeping our code VERY simple to start. We use the handle_in/3 function to deal with incoming messages from the server. If we want to send something back, we can do that too. The help documentation from running h Phoenix.Channel in IEx says this about handle_in/3:

After a client has successfully joined a channel, incoming events from the
client are routed through the channel's handle_in/3 callbacks. Within these
callbacks, you can perform any action. Typically you'll either forward a
message to all listeners with broadcast!/3, or push a message directly down the
socket with push/3. Incoming callbacks must return the socket to maintain
ephemeral state.

Since this is a ping message, the server should reply back to the client that it received and processed the message, so we need to include a reply from the server. The same help page has this to say about replies:

In addition to pushing messages out when you receive a handle_in event, you can
also reply directly to a client event for request/response style messaging.
This is useful when a client must know the result of an operation or to simply
track messages.

We include a reply, include the state and payload of the reply back to the client, and include the socket as the final piece of the tuple. This should now be enough code for us to actually test this out. Go back to your page, refresh, and if you followed all of the instructions, you should be able to click on the button on your page at the top, send out a ping message to the server, and get a reply back from the server, all through our socket.js code:

In addition to this, if we start looking at the messages showing up in our Phoenix logs, we should see the successful results we need to be confident that this whole thing is functioning from both sides:

[debug] INCOMING "ping" on "polls:lobby" to VocialWeb.PollsChannel
Transport: Phoenix.Transports.WebSocket
Parameters: %{}

We still need a way to test out receiving messages from the server, since WebSockets are a bidirectional communication method! Let's head back to lib/vocial_web/channels/polls_channel.ex and add some code to broadcast a message out to all of the clients that someone has pinged the server:

  def handle_in("ping", _payload, socket) do
broadcast socket, "pong", %{message: "pong"}
{:reply, {:ok, %{message: "pong"}}, socket}
end

And we'll need to open up assets/js/socket.js as well to add code that will handle these broadcasts from the server (again, add this code inside of the same enable-polls-channel conditional):

channel.on('pong', payload => {
console.log("The server has been PONG'd and all is well:", payload);
});

This is where the Phoenix WebSocket stuff goes from being, Hey, this is neat, to Wow, this is amazing! Open up another web browser, also at /polls for your local server. When you click the Ping button on one browser, you should see the resulting broadcast on all of the browser windows that have this page/socket/channel open:


You can clearly see that the left browser window is the one that actually initiated the ping (and this is the only one getting the "Received PING response: pong" message in its developer console), yet both broadcast messages are being received nearly simultaneously! You can try this with one extra window or a hundred; the results will ultimately be the same. Congratulations! You are now successfully broadcasting messages bidirectionally on Phoenix channels!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset