LiveView 채팅 자습서 
우리는 전체 코드, 테스트 및 인증이있는 무료 및 오픈 소스 실제 예제를 정말로 원했습니다.
우리는 이것을 팀/커뮤니티 학습 Phoenix LiveView 의 사람들을 지적 할 수 있도록 이것을 썼습니다.
이 LiveView 예제/튜토리얼은 20 분 안에 0에서 완전히 작동하는 앱 으로 이동합니다.
다음은이 예제/튜토리얼에서 다룰 수있는 내용의 표입니다.
LiveView 채팅 자습서Phoenix 앱 생성live 디렉토리, LiveView 컨트롤러 및 템플릿을 만듭니다router.ex 업데이트mount/3 기능mount/3handle_event/3handle_info/2 만듭니다AUTH_API_KEY 만듭니다auth_plug 설치하십시오router.ex 에서 옵션 인증 파이프 라인을 만듭니다AuthController 만듭니다on_mount/4 함수를 만듭니다 Setup Phoenix LiveView Testing , Authentication , Presence ,
필요하지는 않지만 LiveView 카운터 튜토리얼을 따르는 것이 좋습니다 . 적어도 전제 조건 목록을 확인 하여이 모험을 시작하기 전에 컴퓨터에 설치해야 할 사항을 알 수 있습니다!
Elixir , Phoenix 및 Postgres 설치되어 있으면 가면 좋습니다!
Phoenix 앱 생성 새로운 liveview_chat Phoenix 응용 프로그램을 작성하여 시작하십시오.
mix phx.new liveview_chat --no-mailer --no-dashboard email 이나 dashboard 기능이 필요하지 않으므로 앱에서 제외하고 있습니다. 실행을 통해 새로운 Phoenix 앱 생성에 대해 더 많이 배울 수 있습니다 : mix help phx.new
종속성을 검색하려면 mix deps.get 실행하십시오. 그런 다음 명령을 실행하여 liveview_chat_dev Postgres 데이터베이스를 작성하십시오.
mix ecto.setup다음과 유사한 출력이 표시됩니다.
The database for LiveviewChat.Repo has been created
14:20:19.71 [info] Migrations already up그 명령이 성공하면 이제 명령을 실행하여 응용 프로그램을 시작할 수 있어야합니다.
mix phx.server다음과 유사한 터미널 출력이 표시됩니다.
[info] Running LiveviewChatWeb.Endpoint with cowboy 2.9.0 at 127.0.0.1:4000 (http)
[debug] Downloading esbuild from https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.29.tgz
[info] Access LiveviewChatWeb.Endpoint at http://localhost:4000
[watch] build finished, watching for changes... 웹 브라우저에서 URL : http://localhost:4000 열면 다음과 유사한 내용이 표시됩니다.

live 디렉토리, LiveView 컨트롤러 및 템플릿을 만듭니다 lib/liveview_chat_web/live 폴더와 lib/liveview_chat_web/live/message_live.ex 에서 컨트롤러를 만듭니다.
defmodule LiveviewChatWeb.MessageLive do
use LiveviewChatWeb , :live_view
def mount ( _params , _session , socket ) do
{ :ok , socket }
end
def render ( assigns ) do
LiveviewChatWeb.MessageView . render ( "messages.html" , assigns )
end
end참고 : 파일 이름이나 코드에는 " 컨트롤러 "라는 단어가 없습니다. 바라건대 혼란스럽지 않기를 바랍니다. 그것은 앱에서 일어나는 일을 제어한다는 의미에서 "컨트롤러"입니다.
LiveView 컨트롤러는 함수 mount/3 및 render/1 정의해야합니다.
컨트롤러를 단순하게 유지하려면 mount/3 변경없이 {:ok, socket} 튜플을 반환합니다. render/1 LiveviewChatWeb.MessageView.render/2 ( Phoenix 포함)를 호출하여 messages.html.heex template 아래에서 정의 할 템플릿을 호출합니다.
lib/liveview_chat_web/views/message_view.ex 파일 생성 :
defmodule LiveviewChatWeb.MessageView do
use LiveviewChatWeb , :view
end 이것은 일반 Phoenix view 와 유사합니다. 여기 특별/흥미로운 것은 없습니다.
다음으로 lib/liveview_chat_web/templates/message directory를 작성한 다음 작성하십시오
lib/liveview_chat_web/templates/message/messages.html.heex 파일을 추가하고 다음 HTML 을 추가하십시오.
< h1 > LiveView Message Page </ h1 > 마지막으로 루트 레이아웃을 더 간단하게 만들려면 lib/liveview_chat_web/templates/layout/root.html.heex 파일을 열고 <body> 의 내용을 다음과 같이 업데이트하십시오.
< body >
< header >
< section class =" container " >
< h1 > LiveView Chat Example </ h1 >
</ section >
</ header >
< %= @inner_content % >
</ body > router.ex 업데이트 필요한 파일을 만들었으므로 라우터 lib/liveview_chat_web/router.ex 기본 경로 교체 PageController Controller :
get "/" , PageController , :index MessageLive Controller :
scope "/" , LiveviewChatWeb do
pipe_through :browser
live "/" , MessageLive
end이제 페이지를 새로 고치면 다음이 표시됩니다.

이 시점에서 우리는 자동화 된 테스트 스위트가 더 이상 통과하지 못한다는 것을 의미하는 몇 가지 변경을 수행했습니다. 다음 명령과 함께 명령 줄에서 테스트를 실행합니다.
mix test다음과 유사한 출력이 표시됩니다.
Generated liveview_chat app
..
1) test GET / (LiveviewChatWeb.PageControllerTest)
test/liveview_chat_web/controllers/page_controller_test.exs:4
Assertion with = ~ failed
code: assert html_response(conn, 200) = ~ " Welcome to Phoenix! "
left: " <!DOCTYPE html><html lang= " en " > <head> <meta charset= " utf-8 " > <meta http-equiv= " X-UA-Compatible " content= " IE=edge " >
<title data-suffix= " · Phoenix Framework " >LiveviewChat · Phoenix Framework</title> <link phx-track-static rel= " stylesheet " href= " /assets/app.css " > <script defer phx-track-static type= " text/javascript " src= " /assets/app.js " ></script> </head>
<body> <header> <section class= " container " >
<h1>LiveView Chat Example</h1></section> </header>
<h1>LiveView Message Page</h1></main></div> </body></html> "
right: " Welcome to Phoenix! "
stacktrace:
test/liveview_chat_web/controllers/page_controller_test.exs:6: (test)
Finished in 0.03 seconds (0.02s async, 0.01s sync)
3 tests, 1 failure page_controller_test.exs 여전히 홈페이지가 "Welcome to Phoenix!" 포함 할 것으로 기대하고 있기 때문입니다. 텍스트.
테스트를 업데이트합시다! test/liveview_chat_web/live 폴더를 만들고 message_live_test.exs 파일을 만들기 : test/liveview_chat_web/live/message_live_test.exs
다음 테스트 코드를 추가하십시오.
defmodule LiveviewChatWeb.MessageLiveTest do
use LiveviewChatWeb.ConnCase
import Phoenix.LiveViewTest
test "disconnected and connected mount" , % { conn: conn } do
conn = get ( conn , "/" )
assert html_response ( conn , 200 ) =~ "LiveView Message Page"
{ :ok , _view , _html } = live ( conn )
end
end / 엔드 포인트에 액세스 할 수 있고 페이지에 "LiveView Message Page" 텍스트가 있음을 테스트하고 있습니다.
테스트 및 LiveView에 대한 자세한 내용은 LiveViewTest 모듈도 참조하십시오.
마지막으로 PageController 에 연결된 모든 기본 생성 코드를 삭제할 수 있습니다.
rm test/liveview_chat_web/controllers/page_controller_test.exsrm lib/liveview_chat_web/controllers/page_controller.exrm test/liveview_chat_web/views/page_view_test.exsrm lib/liveview_chat_web/views/page_view.exrm -r lib/liveview_chat_web/templates/page 이제 mix test 명령으로 테스트를 다시 실행할 수 있습니다. 다음 (테스트 통과)을 볼 수 있습니다.
Generated liveview_chat app
...
Finished in 0.1 seconds (0.06s async, 0.1s sync)
3 tests, 0 failures
Randomized with seed 841084 LiveView 구조가 정의되면 메시지 작성에 중점을 둘 수 있습니다. 데이터베이스는 메시지와 발신자의 이름을 저장합니다. 새로운 스키마와 마이그레이션을 만들어 봅시다.
mix phx.gen.schema Message messages name:string message:string참고 :
mix ecto.migrate실행하여 데이터베이스에서 새messages테이블을 작성하는 것을 잊지 마십시오.
이제 Message 스키마를 업데이트하여 새 메시지를 작성하고 기존 메시지를 나열하기위한 기능을 추가 할 수 있습니다. 또한 메시지 텍스트에 요구 사항 및 유효성 검사를 추가하기 위해 Changeet을 업데이트합니다. lib/liveview_chat/message.ex 파일을 열고 다음과 함께 코드를 업데이트하십시오.
defmodule LiveviewChat.Message do
use Ecto.Schema
import Ecto.Changeset
import Ecto.Query
alias LiveviewChat.Repo
alias __MODULE__
schema "messages" do
field :message , :string
field :name , :string
timestamps ( )
end
@ doc false
def changeset ( message , attrs ) do
message
|> cast ( attrs , [ :name , :message ] )
|> validate_required ( [ :name , :message ] )
|> validate_length ( :message , min: 2 )
end
def create_message ( attrs ) do
% Message { }
|> changeset ( attrs )
|> Repo . insert ( )
end
def list_messages do
Message
|> limit ( 20 )
|> order_by ( desc: :inserted_at )
|> Repo . all ( )
end
end 메시지 입력에 validate_length 함수를 추가하여 메시지 에 2 개의 문자가 있는지 확인했습니다. 이것은 LiveView 페이지의 양식과 함께 changeset 유효성 검사가 어떻게 작동하는지 보여주는 예일뿐입니다.
그런 다음 create_message/1 및 list_messages/0 함수를 만들었습니다. Phoenix-Chat-Amexample과 마찬가지로 우리는 최신 20 으로 반환 된 메시지 수를 limit .
mount/3 기능 lib/liveview_chat_web/live/message_live.ex 파일을 열고 3 행에서 다음 줄을 추가하십시오.
alias LiveviewChat.Message 다음으로 list_messages 함수를 사용하려면 lib/liveview_chat_web/live/message_live.ex 파일의 mount/3 함수를 업데이트하십시오.
def mount ( _params , _session , socket ) do
messages = Message . list_messages ( ) |> Enum . reverse ( )
changeset = Message . changeset ( % Message { } , % { } )
{ :ok , assign ( socket , changeset: changeset , messages: messages ) }
end mount/3 이제 messages 목록을 가져 와서 메시지 양식에 사용될 changeset 만듭니다. 그런 다음 changeset 과 messages 소켓에 할당하여 LiveView 페이지에 표시됩니다.
messages.html.heex 템플릿을 다음 코드로 업데이트하십시오.
< ul id =' msg-list ' phx-update =" append " >
< %= for message < - @messages do % >
< li id = { "msg-#{message.id}"} >
< b > < %= message.name % > : </ b >
< %= message.message % >
</ li >
< % end % >
</ ul >
< .form let={f} for={@changeset} id="form" phx-submit="new_message" phx-hook="Form" >
< %= text_input f, :name, id: "name", placeholder: "Your name", autofocus: "true" % >
< %= error_tag f, :name % >
< %= text_input f, :message, id: "msg", placeholder: "Your message" % >
< %= error_tag f, :message % >
< %= submit "Send"% >
</ .form > 먼저 새 메시지를 표시 한 다음 사람들이 새 메시지를 create 수있는 양식을 제공합니다.
페이지를 새로 고치면 다음이 표시됩니다.

<.form></.form> 구문은 양식 함수 구성 요소를 사용하는 방법입니다.
함수 구성 요소는
assigns맵을 인수로 수분하고~H시설로 내장 된 렌더링 된struct반환하는 모든 함수입니다.
마지막으로 test/liveview_chat_web/live/message_live_test.exs 파일에서 assert 업데이트하여 테스트가 여전히 전달되는지 확인하십시오.
assert html_response ( conn , 200 ) =~ "LiveView Chat" LiveView Message Page H1 제목을 삭제 했으므로 루트 레이아웃에서 제목을 테스트하고 페이지가 여전히 올바르게 표시되어 있는지 확인할 수 있습니다.
현재 Phoenix App mix phx.server 실행하고 브라우저에 양식을 제출하면 아무것도 일어나지 않을 것입니다. 서버 로그를 보면 다음을 볼 수 있습니다.
** (UndefinedFunctionError) function LiveviewChatWeb.MessageLive.handle_event/3
is undefined or private
(liveview_chat 0.1.0) LiveviewChatWeb.MessageLive.handle_event("new_message",
%{"_csrf_token" => "fyVPIls_XRBuGwlkMhxsFAciRRkpAVUOLW5k4UoR7JF1uZ5z2Dundigv",
"message" => %{"message" => "", "name" => ""}}, #Phoenix.LiveView.Socket
제출시 양식을 phx-submit 하고 있습니다.
< . form let = { f } for = { @ changeset } id = "form" phx - submit = "new_message" >
그러나이 이벤트는 아직 서버에서 관리되지 않았으므로 lib/liveview_chat_web/live/message_live.ex 에서 handle_event/3 함수를 추가 하여이 문제를 해결할 수 있습니다.
def handle_event ( "new_message" , % { "message" => params } , socket ) do
case Message . create_message ( params ) do
{ :error , changeset } ->
{ :noreply , assign ( socket , changeset: changeset ) }
{ :ok , _message } ->
changeset = Message . changeset ( % Message { } , % { "name" => params [ "name" ] } )
{ :noreply , assign ( socket , changeset: changeset ) }
end
end create_message 함수는 양식의 값으로 호출됩니다. 데이터베이스에 정보를 저장하는 동안 error 발생하면 예를 들어 이름이나 message 비어 있거나 message 너무 짧은 경우 changeset 오류를 반환 할 수 있습니다. changeset 이 소켓에 다시 할당됩니다. 이렇게하면 양식이 error 정보를 표시 할 수 있습니다.

오류없이 메시지가 저장되면, 사람들이 양식에 다시 이름을 입력 해야하는 것을 피하기 위해 양식의 이름을 포함하는 새 Changes 세트를 작성하고 새로운 Changes 셋을 소켓에 할당합니다.

이제 양식이 표시되어 다음 테스트를 test/liveview_chat_web/live/message_live_test.exs 에 추가 할 수 있습니다.
import Plug.HTML , only: [ html_escape: 1 ]
test "name can't be blank" , % { conn: conn } do
{ :ok , view , _html } = live ( conn , "/" )
assert view
|> form ( "#form" , message: % { name: "" , message: "hello" } )
|> render_submit ( ) =~ html_escape ( "can't be blank" )
end
test "message" , % { conn: conn } do
{ :ok , view , _html } = live ( conn , "/" )
assert view
|> form ( "#form" , message: % { name: "Simon" , message: "" } )
|> render_submit ( ) =~ html_escape ( "can't be blank" )
end
test "minimum message length" , % { conn: conn } do
{ :ok , view , _html } = live ( conn , "/" )
assert view
|> form ( "#form" , message: % { name: "Simon" , message: "h" } )
|> render_submit ( ) =~ "should be at least 2 character(s)"
end form/3 함수를 사용하여 양식을 선택하고 이름과 메시지에 대해 다른 값으로 제출 이벤트를 트리거합니다. 오류가 올바르게 표시되는 것을 테스트하고 있습니다.
새로 생성 된 메시지를 보려면 페이지를 다시로드하는 대신 PubSub ( Pub Lish Sub Scribe)를 사용하여 모든 연결된 클라이언트에게 새 메시지가 작성되었음을 알리고 새 메시지를 표시하기 위해 UI를 업데이트 할 수 있습니다.
lib/liveview_chat/message.ex 파일을 열고 상단 근처의 다음 줄을 추가하십시오.
alias Phoenix.PubSub다음으로 다음 3 가지 기능을 추가하십시오.
def subscribe ( ) do
PubSub . subscribe ( LiveviewChat.PubSub , "liveview_chat" )
end
def notify ( { :ok , message } , event ) do
PubSub . broadcast ( LiveviewChat.PubSub , "liveview_chat" , { event , message } )
end
def notify ( { :error , reason } , _event ) , do: { :error , reason } 클라이언트가 LiveView 페이지를 올바르게 표시하고 새 메시지를 듣는 경우 subscribe/0 호출됩니다. Phoenix.pubsub.subscribe의 래퍼 함수 일뿐입니다.
notify/2 새 메시지가 생성 될 때마다 호출됩니다. Repo.insert {:ok, message} 또는 {:error, reason} 를 반환 할 수 있으므로 두 경우 모두 notify/2 를 정의해야합니다.
message.ex 에서 create_message/1 함수를 업데이트하여 새로 작성된 notify/2 함수를 호출하십시오.
def create_message ( attrs ) do
% Message { }
|> changeset ( attrs )
|> Repo . insert ( )
|> notify ( :message_created )
endmount/3 LiveView 페이지가 렌더링되면 이제 클라이언트를 연결할 수 있습니다. lib/liveview_chat_web/live/message_live.ex 파일의 상단에 다음 줄을 추가하십시오.
alias LiveviewChat.PubSub 그런 다음 mount/3 함수를 다음과 같이 업데이트하십시오.
def mount ( _params , _session , socket ) do
if connected? ( socket ) , do: Message . subscribe ( )
messages = Message . list_messages ( ) |> Enum . reverse ( )
changeset = Message . changeset ( % Message { } , % { } )
{ :ok , assign ( socket , messages: messages , changeset: changeset ) }
end mount/3 이제 소켓이 연결되어 있는지 확인한 다음 새 Message.subscribe/0 호출합니다.
handle_event/3 create_message/1 의 반환 값이 변경되었으므로 handle_event/3 다음과 같이 업데이트해야합니다.
def handle_event ( "new_message" , % { "message" => params } , socket ) do
case Message . create_message ( params ) do
{ :error , changeset } ->
{ :noreply , assign ( socket , changeset: changeset ) }
:ok -> # broadcast returns :ok (just the atom!) if there are no errors
changeset = Message . changeset ( % Message { } , % { "name" => params [ "name" ] } )
{ :noreply , assign ( socket , changeset: changeset ) }
end
endhandle_info/2 만듭니다 마지막 단계는 lib/liveview_chat_web/live/message_live.ex 에서 handle_info/2 함수를 정의하여 다음 :message_created 이벤트를 처리하는 것입니다.
def handle_info ( { :message_created , message } , socket ) do
messages = socket . assigns . messages ++ [ message ]
{ :noreply , assign ( socket , messages: messages ) }
end이벤트가 접수되면 새 메시지가 기존 메시지 목록에 추가됩니다. 그런 다음 새 목록은 소켓에 할당되어 UI를 업데이트하여 새 메시지를 표시합니다.
test/liveview_chat_web/live/message_live_test.exs 에 다음 테스트를 추가하여 페이지에 메시지가 올바르게 표시되도록하십시오.
test "message form submitted correctly" , % { conn: conn } do
{ :ok , view , _html } = live ( conn , "/" )
assert view
|> form ( "#form" , message: % { name: "Simon" , message: "hi" } )
|> render_submit ( )
assert render ( view ) =~ "<b>Simon:</b>"
assert render ( view ) =~ "hi"
end
test "handle_info/2" , % { conn: conn } do
{ :ok , view , _html } = live ( conn , "/" )
assert render ( view )
# send :created_message event when the message is created
Message . create_message ( % { "name" => "Simon" , "message" => "hello" } )
# test that the name and the message is displayed
assert render ( view ) =~ "<b>Simon:</b>"
assert render ( view ) =~ "hello"
end 이제 LiveView를 사용하여 기능적 채팅 응용 프로그램이 있어야합니다! 다음과 같이 Phoenix 앱을 실행하십시오.
mix phx.server 앱 localhost:4000 에서 2 개 이상의 브라우저를 방문하여 메시지를 보내십시오!

우리가 알아 차릴 수있는 한 가지 문제는 입력 필드에서 Enter 키를 사용하여 메시지 입력이 빈 값으로 항상 재설정되지 않는다는 것입니다. 이를 통해 새 메시지를 작성하고 보내기 전에 이전 메시지를 수동으로 제거해야합니다.
그 이유는 :
JavaScript 클라이언트는 항상 현재 입력 값에 대한 진실의 원천입니다. 초점이있는 모든 입력 의 경우
LiveView서버의 렌더링 된 업데이트에서 벗어나더라도 입력의 현재 값을 덮어 쓰지 않습니다. https://hexdocs.pm/phoenix_live_view/form-bindings.html#javaScript-client-specifics를 참조하십시오
우리의 솔루션은 phx-hook 사용하여 LiveView Life-Cycle Callbacks 중 하나 후 클라이언트에서 일부 JavaScript를 실행하는 것입니다 (마운트, 업데이트, 업데이트, 파괴, 연결 해제, 재 연결).
메시지 양식이 updated 될 때 모니터링하기 위해 후크를 추가합시다. message.html.heex 파일에서 phx-hook 속성을 <.form> 요소에 추가합니다.
< .form let={f} for={@changeset} id="form" phx-submit="new_message" phx-hook="Form" > 그런 다음 assets/js/app.js 파일에서 다음 JavaScript 로직을 추가하십시오.
// get message input element
let msg = document . getElementById ( 'msg' ) ;
// define "Form" hook, the name must match the one
// defined with phx-hoo="Form"
let Hooks = { }
Hooks . Form = {
// Each time the form is updated run the code in the callback
updated ( ) {
// If no error displayed reset the message value
if ( document . getElementsByClassName ( 'invalid-feedback' ) . length == 0 ) {
msg . value = '' ;
}
}
}
let csrfToken = document . querySelector ( "meta[name='csrf-token']" ) . getAttribute ( "content" )
let liveSocket = new LiveSocket ( "/live" , Socket , { params : { _csrf_token : csrfToken } , hooks : Hooks } ) // Add hooks: Hooks 메시지 값을 재설정하기위한 기본 논리는 updated() 콜백 함수 내에 포함되어 있습니다.
if ( document . getElementsByClassName ( 'invalid-feedback' ) . length == 0 ) {
msg . value = '' ;
} 빈 문자열로 값을 설정하기 전에 먼저 invalid-feedback CSS 클래스를 확인하여 양식에 오류가 표시되지 않는지 확인합니다. (피드백에 대해 자세히 알아보십시오.
마지막 단계는 hooks: Hooks 로 liveSocket 의 hooks 설정하는 것입니다. 새 메시지가 추가되면 메시지 입력이 재설정되어야합니다!
현재 mount/3 함수는 먼저 데이터베이스에서 최신 20 개의 메시지를로드하여 메시지 목록을 초기화합니다.
def mount ( _params , _session , socket ) do
if connected? ( socket ) , do: Message . subscribe ( )
messages = Message . list_messages ( ) |> Enum . reverse ( ) # get the list of messages
changeset = Message . changeset ( % Message { } , % { } )
{ :ok , assign ( socket , messages: messages , changeset: changeset ) } ## assigns messages to socket
end 그런 다음 새 메시지가 생성 될 때마다 handle_info 함수 메시지를 메시지 목록에 추가합니다.
def handle_info ( { :message_created , message } , socket ) do
messages = socket . assigns . messages ++ [ message ] # append new message to the existing list
{ :noreply , assign ( socket , messages: messages ) }
end모든 메시지가 서버에서 메모리에 유지되면 메시지 목록이 너무 길어지면 문제가 발생할 수 있습니다.
메모리 사용을 최소화하기 위해 메시지를 임시 assign 으로 정의 할 수 있습니다.
def mount ( _params , _session , socket ) do
if connected? ( socket ) , do: Message . subscribe ( )
messages = Message . list_messages ( ) |> Enum . reverse ( )
changeset = Message . changeset ( % Message { } , % { } )
{ :ok , assign ( socket , messages: messages , changeset: changeset ) ,
temporary_assigns: [ messages: [ ] ] }
end메시지 목록은 한 번 검색 된 다음 빈 목록으로 재설정됩니다.
이제 handle_info/2 새 메시지를 소켓에 할당하면됩니다.
def handle_info ( { :message_created , message } , socket ) do
{ :noreply , assign ( socket , messages: [ message ] ) }
end 마지막으로 heex 메시지 템플릿은 phx-update 가있는 메시지 목록의 변경 사항에 대해서는 리턴트하고 새 메시지를 기존 표시된 목록에 추가합니다.
< ul id =' msg-list ' phx-update =" append " >
< %= for message < - @messages do % >
< li id = {message.id} >
< b > < %= message.name % > : </ b >
< %= message.message % >
</ li >
< % end % >
</ ul > Phoenix temporary-assigns 문서 페이지 : https://hexdocs.pm/phoenix_live_view/dom-patching.html#temporary-assigns를 참조하십시오
현재 name 필드는 메시지를 보내기 전에 수동으로 정의하도록 사람에게 맡겨집니다. 이것은 기본 데모 앱에서는 괜찮지 만 우리는 더 잘할 수 있다는 것을 알고 있습니다. 이 섹션에서는 auth_plug 사용하여 인증을 추가합니다. 이를 통해 앱을 사용하는 사람들은 GitHub 또는 Google 계정으로 인증 한 다음 메시지 양식에 name 미리 채우실 수 있습니다.
AUTH_API_KEY 만듭니다지침에 따라 먼저 https://authdemo.fly.dev/ eG에서 새 API 키를 만듭니다.

그런 다음 .env 파일을 만들고 새 생성 된 API 키를 추가하십시오.
export AUTH_API_KEY = 88SwQGzaZoJYXs6ihvwMy2dRVtm6KVeg4tSCjRKtwDvMUYUbi/88SwQDatWtSTMd2rKPnaZsAWFNpbf4vv2ZK7JW2nwuSypMeg/authdemo.fly.dev참고 : 보안상의 이유로 유효한 API 키가 아닙니다. 나만의 것을 만들고 무료이며 1 분도 채 걸리지 않습니다.
auth_plug 설치하십시오 Auth_Plug 패키지를 종속성에 추가하십시오. mix.exs 파일에서 deps 함수를 업데이트하고 추가하십시오.
{ :auth_plug , "~> 1.4.10" } 이 종속성은 귀하를위한 새로운 세션을 만들고 Dwyl auth 응용 프로그램과 통신합니다.
잊지 마세요 :
source .envmix deps.get 새 종속성이 컴파일되기 전에 AUTH_API_KEY 에 액세스 할 수 있는지 확인하십시오.
mix deps.compile --force 로 종속성을 다시 컴파일 할 수 있습니다.
이제 인증 기능을 추가 할 수 있습니다.
router.ex 에서 옵션 인증 파이프 라인을 만듭니다 [승인 된] "게스트"사용자가 채팅에 액세스 할 수 있도록 AuthPlugOptional 플러그를 사용합니다. 옵션 인증에서 자세히 알아보십시오.
router.ex 파일에서 새 Plug 파이프 라인을 만듭니다.
# define the new pipeline using auth_plug
pipeline :authOptional , do: plug ( AuthPlugOptional ) 다음으로 scope "/", LiveviewChatWeb do .
scope "/" , LiveviewChatWeb do
pipe_through [ :browser , :authOptional ]
live "/" , MessageLive
get "/login" , AuthController , :login
get "/logout" , AuthController , :logout
end이제 우리는 라우터의 모든 경로에 대한 인증이 선택 사항이 될 수 있습니다. 쉬워요?
AuthController 만듭니다 login/2 및 logout/2 함수로 AuthController 만듭니다.
새 파일 생성 : lib/liveview_chat_web/controllers/auth_controller.ex 추가하고 다음 코드를 추가하십시오.
defmodule LiveviewChatWeb.AuthController do
use LiveviewChatWeb , :controller
def login ( conn , _params ) do
redirect ( conn , external: AuthPlug . get_auth_url ( conn , "/" ) )
end
def logout ( conn , _params ) do
conn
|> AuthPlug . logout ( )
|> put_status ( 302 )
|> redirect ( to: "/" )
end
end login/2 기능은 DWYL AUTT 앱으로 리디렉션합니다. AuthPlug.get_auth_url/2 기능을 사용하는 방법에 대해 자세히 알아보십시오. 인증 일단 인증 일단 인증이 발생하면 사용자를 / 엔드 포인트로 리디렉션하고 클라이언트에서 jwt 세션이 생성됩니다.
logout/2 함수는 (JWT) 세션을 제거하고 홈페이지로 다시 리디렉션되는 AuthPlug.logout/1 호출합니다.
on_mount/4 함수를 만듭니다 LiveView on_mount 콜백을 제공하여 mount 전에 코드를 실행할 수 있습니다. 이 콜백을 사용하여 jwt 세션을 확인하고 person ( Map ) 및 loggedin ( boolean ) 값을 socket 에 할당합니다.
lib/liveview_chat_web/controllers/auth_controller.ex 파일에서 다음 코드를 추가하여 두 가지 버전의 mount/4 정의합니다.
# import the assign_new function from LiveView
import Phoenix.LiveView , only: [ assign_new: 3 ]
# pattern match on :default auth and check session has jwt
def on_mount ( :default , _params , % { "jwt" => jwt } = _session , socket ) do
# verify and retrieve jwt stored data
claims = AuthPlug.Token . verify_jwt! ( jwt )
# assigns the person and the loggedin values
socket =
socket
|> assign_new ( :person , fn ->
AuthPlug.Helpers . strip_struct_metadata ( claims )
end )
|> assign_new ( :loggedin , fn -> true end )
{ :cont , socket }
end
# when jwt is not defined just returns the current socket
def on_mount ( :default , _params , _session , socket ) do
socket = assign_new ( socket , :loggedin , fn -> false end )
{ :cont , socket }
endantart_new/3이 존재하지 않으면 소켓에 값을 할당합니다.
on_mount/2 콜백이 정의되면 lib/liveview_chat_web/live/message_live.ex 파일에서 호출 할 수 있습니다.
defmodule LiveviewChatWeb.MessageLive do
use LiveviewChatWeb , :live_view
alias LiveviewChat.Message
# run authentication on mount
on_mount LiveviewChatWeb.AuthController
이제 사람들이 인증 할 수있는 모든 논리가 있습니다. 루트 레이아웃 파일 lib/liveview_chat_web/templates/layout/root.html.heex login (또는 logout ) 링크를 업데이트하면됩니다.
< body >
< header >
< section class =" container " >
< nav >
< ul >
< %= if @loggedin do % >
< li >
< img width =" 40px " src = {@person.picture}/ >
</ li >
< li > < %= link "logout", to: "/logout" % > </ li >
< % else % >
< li > < %= link "Login", to: "/login" % > </ li >
< % end % >
</ ul >
</ nav >
< h1 > LiveView Chat Example </ h1 >
</ section >
</ header >
< %= @inner_content % >
</ body > 사람이 아직 loggedin 되지 않은 경우 login 링크가 표시됩니다. 그렇지 않으면 logout 링크가 표시됩니다.
마지막 단계는 메시지 양식의 이름 필드에 로그인 한 사람의 이름을 표시하는 것입니다. 이를 위해 mount 함수의 양식 변경 세트를 업데이트하여 이름 매개 변수를 설정할 수 있습니다.
def mount ( _params , _session , socket ) do
if connected? ( socket ) , do: Message . subscribe ( )
# add name parameter if loggedin
changeset =
if socket . assigns . loggedin do
Message . changeset ( % Message { } , % { "name" => socket . assigns . person [ "givenName" ] } )
else
Message . changeset ( % Message { } , % { } )
end
messages = Message . list_messages ( ) |> Enum . reverse ( )
{ :ok , assign ( socket , messages: messages , changeset: changeset ) ,
temporary_assigns: [ messages: [ ] ] }
end이제 응용 프로그램을 실행하고 로그인/로그 아웃 할 수 있습니다!

이 섹션에서는 Phoenix의 존재를 사용하여 현재 응용 프로그램을 사용하는 사람들의 목록을 표시합니다.
첫 번째 단계는 lib/liveview_chat/presence.ex 파일을 만드는 것입니다.
defmodule LiveviewChat.Presence do
use Phoenix.Presence ,
otp_app: :liveview_chat ,
pubsub_server: LiveviewChat.PubSub
end 그런 다음 lib/liveview_chat/application.ex 에서 감독자가 시작할 수 있도록 새로 생성 된 Presence 모듈을 응용 프로그램 목록에 추가합니다.
def start ( _type , _args ) do
children = [
# Start the Ecto repository
LiveviewChat.Repo ,
# Start the Telemetry supervisor
LiveviewChatWeb.Telemetry ,
# Start the PubSub system
{ Phoenix.PubSub , name: LiveviewChat.PubSub } ,
# Presence
LiveviewChat.Presence ,
# Start the Endpoint (http/https)
LiveviewChatWeb.Endpoint
# Start a worker by calling: LiveviewChat.Worker.start_link(arg)
# {LiveviewChat.Worker, arg}
]
...
우리는 이제 LiveView 엔드 포인트의 존재 기능을 사용할 준비가되었습니다.
lib/liveview_chat_web/live/message_live.ex 파일에서 다음과 함께 mount 함수를 업데이트하십시오.
@ presence_topic "liveview_chat_presence"
def mount ( _params , _session , socket ) do
if connected? ( socket ) do
Message . subscribe ( )
{ id , name } =
if socket . assigns . loggedin do
{ socket . assigns . person [ "id" ] , socket . assigns . person [ "givenName" ] }
else
{ socket . id , "guest" }
end
{ :ok , _ } = Presence . track ( self ( ) , @ presence_topic , id , % { name: name } )
Phoenix.PubSub . subscribe ( PubSub , @ presence_topic )
end
changeset =
if socket . assigns . loggedin do
Message . changeset ( % Message { } , % { "name" => socket . assigns . person [ "givenName" ] } )
else
Message . changeset ( % Message { } , % { } )
end
messages = Message . list_messages ( ) |> Enum . reverse ( )
{ :ok ,
assign ( socket ,
messages: messages ,
changeset: changeset ,
presence: get_presence_names ( )
) , temporary_assigns: [ messages: [ ] ] }
end mount/3 함수의 주요 변경 사항을 요약하겠습니다.
먼저, 존재 함수와 함께 사용할 topic 정의하기 위해 모듈 속성 @presence_topic 만듭니다.
코드의 다음 부분은 사람의 id 와 이름을 포함하는 튜플을 정의합니다. 사람이 로그인 하지 않은 경우 이름이 "게스트"로 기본값을받습니다.
{ id , name } =
if socket . assigns . loggedin do
{ socket . assigns . person [ "id" ] , socket . assigns . person [ "givenName" ] }
else
{ socket . id , "guest" }
end둘째, 우리는 트랙/4 함수를 사용하여 새로운 클라이언트가 응용 프로그램을보고 있음을 알 수 있도록합니다.
{ :ok , _ } = Presence . track ( self ( ) , @ presence_topic , id , % { name: name } )셋째, PubSub를 사용하여 PRESENTION 변경 사항을 듣습니다 (신청서에 가입하거나 떠나는 사람) :
Phoenix.PubSub . subscribe ( PubSub , @ presence_topic ) 마지막으로 우리는 소켓에서 새로운 presence 할당을 만듭니다.
presence : get_presence_names ( ) get_presence_names 함수는 로그인 한 사용자 목록과 "게스트"사용자 번호가있는 경우 반환됩니다.
MessageLive 모듈 끝에 다음 코드를 추가하십시오.
defp get_presence_names ( ) do
Presence . list ( @ presence_topic )
|> Enum . map ( fn { _k , v } -> List . first ( v . metas ) . name end )
|> group_names ( )
end
# return list of names and number of guests
defp group_names ( names ) do
loggedin_names = Enum . filter ( names , fn name -> name != "guest" end )
guest_names =
Enum . count ( names , fn name -> name == "guest" end )
|> guest_names ( )
if guest_names do
[ guest_names | loggedin_names ]
else
loggedin_names
end
end
defp guest_names ( 0 ) , do: nil
defp guest_names ( 1 ) , do: "1 guest"
defp guest_names ( n ) , do: " #{ n } guests" 위의 코드에서 중요한 기능 호출은 Presence.list(@presence_topic) 입니다. 목록/1 함수는 응용 프로그램을 사용하여 사용자 목록을 반환합니다. group_names 및 guest_names 함수는 list 에 의해 반환 된 존재 데이터를 조작하기 위해 여기에 있습니다 (https://hexdocs.pm/phoenix/phoenix.presence.html#c:list/1presence-data-structure 참조).
지금까지 우리는 mount Function에서 채팅 페이지를 사용하여 새로운 사람들을 추적했으며 Pubsub를 사용하여 Presence 변경 사항을 들었습니다. 마지막 단계는 handle_info 함수를 추가하여 이러한 변경 사항을 처리하는 것입니다.
def handle_info ( % { event: "presence_diff" , payload: _diff } , socket ) do
{ :noreply , assign ( socket , presence: get_presence_names ( ) ) }
end마지막으로, PRESENTION_DIFF "이벤트로 실시간으로 발생하면서 고객에게 실시간으로 진행되는 Exten and Leave Events의 차이가 있습니다.
handle_info 함수는 presence_diff 이벤트를 포착하고 get_presence_names 함수 호출의 결과로 presence 값을 소켓에 재 할당합니다.
이름을 표시하려면 lib/liveview_chat_web/templates/message/messages.html.heex 템플릿 파일에 다음을 추가합니다.
< b > People currently using the app: </ b >
< ul >
< %= for name < - @presence do % >
< li >
< %= name % >
</ li >
< % end % >
</ ul >이제 응용 프로그램을 실행하고 로그인 한 사용자 및 게스트 사용자 수를 볼 수 있어야합니다.
test/liveview_chat_web/live/message_live_test.exs 에이 두 가지 테스트를 추가하여 템플릿이 올바르게 업데이트되었음을 테스트 할 수 있습니다.
test "1 guest online" , % { conn: conn } do
{ :ok , view , _html } = live ( conn , "/" )
assert render ( view ) =~ "1 guest"
end
test "2 guests online" , % { conn: conn } do
{ :ok , _view , _html } = live ( conn , "/" )
{ :ok , view2 , _html } = live ( conn , "/" )
assert render ( view2 ) =~ "2 guests"
end Tailwind 처음 사용하는 경우 https://github.com/dwyl/learn-tailwind를 참조하십시오
lib/liveview_chat_web/templates/layout/root.html.heex 의 내용을 : :
<!DOCTYPE html >
< html lang =" en " >
< head >
< meta charset =" utf-8 " />
< meta http-equiv =" X-UA-Compatible " content =" IE=edge " />
< meta name =" viewport " content =" width=device-width, initial-scale=1.0 " />
< meta name =" csrf-token " content = {csrf_token_value()} >
< %= live_title_tag assigns[:page_title] || "LiveviewChat", suffix: " · Phoenix Framework" % >
< script defer phx-track-static type =" text/javascript " src = {Routes.static_path(@conn, "/assets/app.js")} > </ script >
< script src =" https://cdn.tailwindcss.com " > </ script >
</ head >
< body >
< header class =" bg-slate-800 w-full min-h-[15%] pt-5 pb-1 mb-2 " >
< section >
< nav >
< div class =" text-white width-[10%] float-left ml-3 -mt-5 align-middle " >
< b > People in Chat: </ b >
< ul >
< %= for name < - @presence do % >
< li >
< %= name % >
</ li >
< % end % >
</ ul >
</ div >
< ul class =" float-right mr-3 " >
< %= if @loggedin do % >
< li >
< img width =" 42px " src = {@person.picture} class =" -mt-3 " />
</ li >
< li class =" text-white " >
< %= link "logout", to: "/logout" % >
</ li >
< % else % >
< li class =" bg-green-600 text-white rounded-xl px-4 py-2 w-full mb-2 font-bold " >
< %= link "Login", to: "/login" % >
</ li >
< % end % >
</ ul >
</ nav >
< h1 class =" text-3xl mb-4 text-center font-mono text-white " > LiveView Chat Example </ h1 >
</ section >
</ header >
< %= @inner_content % >
</ body >
</ html > 그런 다음 lib/liveview_chat_web/templates/message/messages.html.heex 의 내용을 다음과 같이 바꾸십시오.
< ul id =' msg-list ' phx-update =" append " >
< %= for message < - @messages do % >
< li id = { "msg-#{message.id}"} class="px-5" >
< small class =" float-right text-xs align-middle " >
< %= message.inserted_at % >
</ small >
< b > < %= message.name % > : </ b >
< %= message.message % >
</ li >
< % end % >
</ ul >
< footer class =" fixed bottom-0 w-full bg-slate-300 pb-2 px-5 pt-2 " >
< .form let={f} for={@changeset} id="form" phx-submit="new_message" phx-hook="Form" >
< %= if @loggedin do % >
< %= text_input f, :name, id: "name", value: @person.givenName,
class: "hidden" % >
< % else % >
< %= text_input f, :name, id: "name", placeholder: "Name", autofocus: "true",
class: "border p-2 w-9/12 mb-2 mt-2 mr2" % >
< span class =" italic text-2xl ml-4 " > or </ span >
< span class =" bg-green-600 text-white rounded-xl px-4 py-2 mb-2 mt-3 float-right " >
< %= link "Login", to: "/login" % >
</ span >
< %= error_tag f, :name % >
< % end % >
< %= text_input f, :message, id: "msg", placeholder: "Message",
class: "border p-2 w-10/12 mb-2 mt-2 float-left" % >
< p class =" text-amber-600 " >
< %= error_tag f, :message % >
</ p >
< %= submit "Send", class: "bg-sky-600 text-white rounded-xl px-4 py-2 mt-2 float-right" % >
</ .form >
</ footer >이제 다음과 같은 것처럼 보이는 UI/레이아웃이 있어야합니다.

사용 된 Tailwind 클래스에 대해 궁금한 점이 있으면 2 분에 인터넷 검색을 보내고 여전히 고정 된 경우 문제를 열어주십시오.
이 예제가 유용하다는 것을 알게되면 Github 리포지토리를 보내주십시오.
다음은 읽을 수있는 몇 가지 다른 저장소입니다.
질문이나 제안이 있습니까? 새로운 문제를 열어 주시기 바랍니다!
감사합니다!