$ mix new chatter --umbrella
$ cd chatter
$ cd apps
$ mix phx.new.web chatter_web
Note: choose Y
for fetch or install dependencies question.
Go into your application by running:
$ cd chatter_web
Start your Phoenix app with:
$ mix phx.server
You can also run your app inside IEx (Interactive Elixir) as:
$ iex -S mix phx.server
Navigate to localhost:4000
to see server running with initial setup provided by Phoenix.
Let's create a chat app view, which will display messages posted to the chat as well as fields to type your username and message.
Replace content of the chatter/apps/chatter_web/lib/chatter_web/templates/page/index.html.eex
file with
<div id="messages"></div><br/>
<div class="col-md-2 form-group">
<label>Username</label>
<input id="username" type="text" class="form-control" placeholder="username" />
</div>
<div class="col-md-10 form-group">
<label>Message</label>
<input id="message" type="text" class="form-control" />
</div>
In chatter/apps/chatter_web/lib/chatter_web/channels/user_socket.ex
uncomment line which sets up channel.
channel "room:*", ChatterWeb.RoomChannel
Note: ChatterWeb.RoomChannel
is the name of the module where channel logic will go.
Create new file
(chatter/apps/chatter_web)$ touch lib/chatter_web/channels/room_channel.ex
and add channel logic:
defmodule ChatterWeb.RoomChannel do
use Phoenix.Channel
def join("room:lobby", _payload, socket) do
{:ok, socket}
end
def handle_in("new_message", payload, socket) do
broadcast! socket, "new_message", payload
{:noreply, socket}
end
end
Replace all content in the file chatter/apps/chatter_web/assets/js/app.js
with:
import {Socket} from "phoenix"
const socket = new Socket("/socket")
socket.connect()
const init = (socket, document) => {
let channel = socket.channel("room:lobby", {})
channel.join()
.receive("ok", resp => { console.log("Joined successfully", resp) })
.receive("error", resp => { console.log("Unable to join", resp) })
let username = document.getElementById("username")
let message = document.getElementById("message")
let messages = document.getElementById("messages")
message.addEventListener("keypress", keyPressHandler(username, message, channel))
channel.on("new_message", renderMessage(messages))
}
const keyPressHandler = (username, message, channel) => {
return (event) => {
if (event.charCode == 13) {
channel.push("new_message", {
user: username.value,
body: message.value
})
message.value = ""
}
}
}
const renderMessage = (messages) => ({user, body}) => {
const new_message = `<p><b>[${user}]</b>:${body}</p>`
messages.insertAdjacentHTML('beforeend', new_message)
}
init(socket, document)
Voila! Enjoy your chat app. Open few tabs for multiuser experience.
By creating an umbrella application in step 1, we left some room to extend current functionality. What could be next steps:
- create another application within the umbrella
- add new functionality to newly created application
- add newly created application with new functionality as a dependency to our
chat
application.
Further ideas...