Creating the Channel

It’s time to write some code to process connections. To review what you know so far, a channel is a conversation on a topic. Our topic has an identifier of videos:video_id, where video_id is a dynamic ID matching a record in the database. In our application, we want a user to get all events for a topic, which to us means a user will get all annotations for a given video, regardless of who created them.

More generally, at their most basic level, topics are strings that serve as identifiers. They often take the form of topic:subtopic, where topic is often a resource name and subtopic is often an ID, but any string is a valid topic.

Since topics are organizing concepts, we’ll include topics where you’d expect: as parameters to functions and in our URLs to identify conversations. Just as the client passes a URL with an :id parameter to represent a resource for a controller, we’ll provide a topic ID to scope our channel connections.

Joining a Channel

Now that we’ve established a socket connection, our users can join a channel. In general, when clients join a channel, they must provide a topic. They’ll be able to join any number of channels and any number of topics on a channel.

We need a VideoChannel for our application, so let’s start by including a channel definition in our UserSocket:

 defmodule​ RumblWeb.UserSocket ​do
 use​ Phoenix.Socket
 
 ## Channels
  channel ​"​​videos:*"​, RumblWeb.VideoChannel

Transports route events into your UserSocket, where they’re dispatched into your channels based on topic patterns that you declare with the channel macro. Our videos:* convention categorizes topics with a resource name, followed by a resource ID.

Let’s move on to the code that will process each incoming event.

Building the Channel Module

Now, it’s time to create the module that will handle our specific VideoChannel. It’ll allow connections through join and also let users disconnect and send events. For consistency with OTP naming conventions, this book sometimes refers to these features as callbacks. Let’s start with join. Create a file called lib/rumbl_web/channels/video_channel.ex, like this:

 defmodule​ RumblWeb.VideoChannel ​do
 use​ RumblWeb, ​:channel
 
 def​ join(​"​​videos:"​ <> video_id, _params, socket) ​do
  {​:ok​, assign(socket, ​:video_id​, String.to_integer(video_id))}
 end
 end

Here we see the first of our channel callbacks: join. Clients can join topics on a channel. We return {:ok, socket} to authorize a join attempt or {:error, socket} to deny one.

For now, we let all clients join any video topic. We extract the video ID using pattern matching: "videos:" <> video_id will match all topics starting with "videos:" and assign the rest of the topic to the video_id variable. We then add the video ID to socket.assigns. Remember, sockets will hold all of the state for a given conversation. Each socket can hold its own state in the socket.assigns field, which typically holds a map.

For channels, the socket is transformed in a loop rather than a single pipeline. In fact, the socket state will remain for the duration of a connection. That means the socket state we add in join will be accessible later as events come into and out of the channel. This small distinction leads to an enormous difference in efficiency between the channels API and the controllers API.

With our channel in place, let’s join it from the client. Open up assets/js/video.js and update your listing:

1: onReady(videoId, socket){
2: let​ msgContainer = document.getElementById(​"msg-container"​)
3: let​ msgInput = document.getElementById(​"msg-input"​)
4: let​ postButton = document.getElementById(​"msg-submit"​)
5: let​ vidChannel = socket.channel(​"videos:"​ + videoId)
6: 
7:  vidChannel.join()
8:  .receive(​"ok"​, resp => console.log(​"joined the video channel"​, resp) )
9:  .receive(​"error"​, reason => console.log(​"join failed"​, reason) )
10: }

On lines 5 through 9, we create a new channel object, vidChannel, from our socket and give it our topic. We build the topic by joining the "videos:" string with our video ID, which we plucked from the div element in our WatchView’s show.html.eex template.

We see our joined message in the JavaScript web console output:

 transport: connected to ws://localhost:4000/socket/websocket...
 push: videos:1 phx_join (1, 1) – {}
 receive: ok videos:1 phx_reply (1) – {response: {}, status: "ok"}
 joined the video channel – {}

Likewise, our server output confirms that we’ve established our conversation:

 [info] JOIN "videos:1" to RumblWeb.VideoChannel
  Transport: :websocket
  Serializer: Phoenix.Socket.V2.JSONSerializer
  Parameters: %{}
 [info] Replied videos:1 :ok

And we’re joined!

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

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