You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+153-1Lines changed: 153 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -49,7 +49,7 @@ This is a quick summary of each commit. View the commit to look at code in isola
49
49
Section # | Commit | Description
50
50
------|------|------
51
51
1 | `12eb0e4` | Ran `mix phx.new petal_stack_tutorial`
52
-
1 | `TBD` | Added `.tool-versions`, wrote PETAL Stack section of README
52
+
1 | `f78aefb` | Added `.tool-versions`, wrote PETAL Stack section of README
53
53
54
54
## Tutorial
55
55
@@ -110,5 +110,157 @@ iex -S mix phx.server
110
110
```
111
111
112
112
### Section 2: Intro to LiveView
113
+
114
+
We're going to implement a simple counter.
115
+
116
+
The file `lib/petal_stack_tutorial_web/router.ex` contains the routing logic to associate controllers with verb-path pairs. Add the following line to add a new route that will point to our live view.
117
+
118
+
```elixir
119
+
live "/counter", Counter
120
+
```
121
+
122
+
Create a new file `lib/petal_stack_tutorial_web/live/counter.ex` with the following content
123
+
```elixir
124
+
defmodulePetalStackTutorialWeb.Counterdo
125
+
usePetalStackTutorialWeb, :live_view
126
+
127
+
# more code here
128
+
end
129
+
```
130
+
131
+
Note: `use PetalStackTutorialWeb, :live_view` inserts `use Phoenix.LiveView` which makes this a live view.
132
+
133
+
We need to implement minimum 3 callbacks.
134
+
-`mount/3` which is called when the websocket is mounted, and returns the inital state.
135
+
-`render/1` which takes the state and returns HEEx (HTML Embedded Elixir) template.
136
+
-`handle_update/3` which handles events and updates the state.
137
+
138
+
Implment the `mount/3` function.
139
+
```elixir
140
+
@impltrue
141
+
defmount(_params, _session, socket) do
142
+
{:ok, assign(socket, counter:0)}
143
+
end
144
+
```
145
+
146
+
Note: Add the `@impl true` to explicitly say you're implementing a callback.
147
+
148
+
`mount/3` must return a tuple with `:ok` being first and the socket object being the second. Inside the socket object is a hashmap called `assigns` which we can add state to. The helper function `assign` takes a socket and a key-value and returns a socket object with the updated state. There is other information about the websocket connection in the socket object but we will only care about our own custom state for this exercise.
Note: The `~H` sigil is a macro that turns the string into a HEEx template.
179
+
180
+
Note: The `@` is a macro that accesses the assigns map. `@counter` is equivalent to `assigns[:counter]`.
181
+
182
+
Note: When displaying a Elixir value in a HEEx template, use `<%= ... %>` for values in HTML and `{ ... }` for values as attributes.
183
+
184
+
We implement the `render/1` function that take the assigns map in the socket object. It returns an HEEx template. The template consists of a span to display the value of the counter, and two buttons to increment and decrement the counter. The two buttons have some similar functionality so we can implment another function that is private and returns the button. The `attr` and `slot` macros are helpers for declaring values in the assigns map. A `attr` can be a value that is required or have a default value. You will get a compiler error if a required `attr` is not given. A `slot` is nested HTML.
185
+
186
+
After we implmented this function, we can start the server and open http://localhost:4000/counter in out browser.
187
+
188
+
We see the following logs.
189
+
```
190
+
[info] GET /counter
191
+
[debug] Processing with PetalStackTutorialWeb.Counter.Elixir.PetalStackTutorialWeb.Counter/2
192
+
Parameters: %{}
193
+
Pipelines: [:browser]
194
+
[info] Sent 200 in 28ms
195
+
[info] CONNECTED TO Phoenix.LiveView.Socket in 12µs
3. A response is made in 28ms, which sends the LiveView response.
209
+
4. That response sends another request to establish a websocket connection.
210
+
5. Connect in 12µs
211
+
6. The `mount/3` callback is called to initialize the state.
212
+
7. The `render/1` callback is called rendering the HTML. This reply is sent in 87µs.
213
+
214
+
The web page displays "0+-"
215
+
216
+
Recall we set the `phx-click` and `phx-debounce` attributes on the buttons, which are specific Phoenix LiveView attributes. `phx-click` for the + button is set to the string "inc", which means when that element is clicked that string will be sent via the websocket as an event. The `phx-debounce` attribute is set to 20, which is how many milliseconds all events for this element are debounced for. When we click the + we see the following error.
217
+
218
+
```
219
+
[error] GenServer #PID<0.804.0> terminating
220
+
** (UndefinedFunctionError) function PetalStackTutorialWeb.Counter.handle_event/3 is undefined or private
221
+
```
222
+
223
+
What happens
224
+
1. We click the +
225
+
2. The "inc" event is sent via the websocket.
226
+
3. We try to call `handle_event/3` and get a function not defined error.
227
+
4. The Elixir process dies due to a unhandled exception.
228
+
5. The process is restarted by a supervisor and reconnects to our websocket.
229
+
230
+
You will see a small red flash alert telling you the page loses connection for a moment, then disappear when the connection is reestablished.
231
+
232
+
Let's implment `handle_event/3`
233
+
```elixir
234
+
@impltrue
235
+
defhandle_event("inc", _params, socket) do
236
+
{:noreply, update(socket, :counter, fn x -> x+1end)}
237
+
end
238
+
239
+
@impltrue
240
+
defhandle_event("dec", _params, socket) do
241
+
f =fn
242
+
x when x <=0->0
243
+
x -> x -1
244
+
end
245
+
{:noreply, update(socket, :counter, f)}
246
+
end
247
+
```
248
+
249
+
We use pattern matching to implment the function across 2 seperate clauses. Here if we were to emit an event besides "inc" or "dec" from our frontend we'd get a function undefined exception. Optionally we could add a thrid catch-all clause to log the result. In both callbacks we use the `update` helper function to take the socket, key of value we want to change, and a function that will transform the value. We use pattern matching to implment the callback so that it can't decrement below 0.
250
+
251
+
Now when we click the + button we see the log and the counter update.
252
+
```
253
+
[debug] HANDLE EVENT "inc" in PetalStackTutorialWeb.Counter
254
+
Parameters: %{"value" => ""}
255
+
[debug] Replied in 413µs
256
+
```
257
+
258
+
That is most everything you need to get started with LiveView but not all the functionality
259
+
260
+
- Implement `handle_info/2` to handle server-side events sent from other processes, for real-time updates to the UI.
0 commit comments