<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dennis Beatty</title>
    <description>The latest articles on DEV Community by Dennis Beatty (@dnsbty).</description>
    <link>https://dev.to/dnsbty</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F357885%2F6fe2e434-b875-48b4-a981-7e3c8c52ad56.jpg</url>
      <title>DEV Community: Dennis Beatty</title>
      <link>https://dev.to/dnsbty</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dnsbty"/>
    <language>en</language>
    <item>
      <title>Use the new Enum type in Ecto 3.5</title>
      <dc:creator>Dennis Beatty</dc:creator>
      <pubDate>Tue, 05 Jan 2021 17:20:33 +0000</pubDate>
      <link>https://dev.to/dnsbty/use-the-new-enum-type-in-ecto-3-5-2dl0</link>
      <guid>https://dev.to/dnsbty/use-the-new-enum-type-in-ecto-3-5-2dl0</guid>
      <description>&lt;p&gt;Booleans are one of the oldest constructs in programming. They're simple. Binary. And we use booleans everywhere. One of my earliest software projects had a database table called "users" with several columns used to specify role like &lt;code&gt;is_admin&lt;/code&gt;, &lt;code&gt;is_moderator&lt;/code&gt;, and &lt;code&gt;is_seller&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;is_admin:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;is_moderator:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;is_seller:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gets even worse when booleans are used to represent some sort of state. For example a payment service that stores its state in booleans could potentially have columns like &lt;code&gt;is_authorized&lt;/code&gt;, &lt;code&gt;is_captured&lt;/code&gt;, &lt;code&gt;is_failed&lt;/code&gt;, and &lt;code&gt;is_refunded&lt;/code&gt;. That's just &lt;strong&gt;complicated&lt;/strong&gt;. And that's just with a single piece of state. Many places support partial refunds, which means now you've got another boolean &lt;code&gt;is_partial_refund&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Payment&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ss"&gt;is_authorized:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;is_captured:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;is_failed:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;is_refunded:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;is_partial_refund:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But there's another issue: now you can have &lt;strong&gt;impossible states&lt;/strong&gt;. For instance if somehow a record has &lt;code&gt;is_partial_refund&lt;/code&gt; set to &lt;code&gt;true&lt;/code&gt; but &lt;code&gt;is_refunded&lt;/code&gt; set to&lt;br&gt;
&lt;code&gt;false&lt;/code&gt; what does that even mean? Or if a record has &lt;code&gt;is_failed&lt;/code&gt; set to &lt;code&gt;true&lt;/code&gt; but also &lt;code&gt;is_captured&lt;/code&gt; set to &lt;code&gt;true&lt;/code&gt;. Make impossible states impossible!&lt;/p&gt;
&lt;h2&gt;
  
  
  Enums
&lt;/h2&gt;

&lt;p&gt;Enums allow us to do just that. They enable a variable to contain a value from a set of predefined constants. In a statically-typed language, enum values are checked at compile time to eradicate bad values.  Elixir is dynamically-typed, so we don't get that safety guarantee, but that doesn't demean enums' usefulness.&lt;/p&gt;

&lt;p&gt;The user example above is made simpler with the use of an enum:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;role:&lt;/span&gt; &lt;span class="ss"&gt;:admin&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The role field would contain one of the following atoms: &lt;code&gt;:admin&lt;/code&gt;, &lt;code&gt;:moderator&lt;/code&gt;, &lt;code&gt;:seller&lt;/code&gt;, &lt;code&gt;:buyer&lt;/code&gt;. That simplifies our user struct immensely, but the difference is even bigger with the payment example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Payment&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;status:&lt;/span&gt; &lt;span class="ss"&gt;:pending&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of setting different combinations of five boolean flags, we can change the status to one of &lt;code&gt;:authorized&lt;/code&gt;, &lt;code&gt;:captured&lt;/code&gt;, &lt;code&gt;:failed&lt;/code&gt;, &lt;code&gt;:refunded&lt;/code&gt;, or &lt;code&gt;:partially_refunded&lt;/code&gt;. And there is no way for the payment to get into one of the impossible states mentioned above. But how does this work with a database?&lt;/p&gt;

&lt;h2&gt;
  
  
  Rolling your own enums with Strings
&lt;/h2&gt;

&lt;p&gt;The simplest way to store "enums" in a database with Elixir and Ecto has been to roll your own enums. By creating a new varchar column on your table, you can then write the appropriate string to it when creating or updating a record.&lt;br&gt;
&lt;code&gt;Ecto.Changeset.validate_inclusion/4&lt;/code&gt; will allow you to check that the string being provided contains one of the supported values so that you're not inputting bad data.&lt;/p&gt;

&lt;p&gt;But that safety only exists at the application layer. It ignores the potential that someone accessing the database directly could input a bad value by bypassing the application. If a bad string gets in and the application doesn't handle it, that bad string will crash the process. But you shouldn't have to waste time validating values coming from your data layer. &lt;strong&gt;Your application deserves better.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  EctoEnum
&lt;/h2&gt;

&lt;p&gt;Postgres and MySQL have the ability to create enum types that can be used for columns and will validate them at the data layer rather than the application layer. This means that your data store would be protected from bad values. For&lt;br&gt;
a long time, the &lt;a href="https://github.com/gjaldon/ecto_enum"&gt;EctoEnum library&lt;/a&gt; has been the best way to set up custom enum types for Postgres:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/my_app/accounts/user_role.ex&lt;/span&gt;
&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Accounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;UserRole&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;EctoEnum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type:&lt;/span&gt; &lt;span class="ss"&gt;:user_role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;enums:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:admin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:moderator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:seller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:buyer&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# lib/my_app/accounts/user.ex&lt;/span&gt;
&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Accounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;
  &lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Accounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;UserRole&lt;/span&gt;

  &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="s2"&gt;"users"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;UserRole&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# priv/repo/migrations/20210102193646_add_role_to_users.exs&lt;/span&gt;
&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;AddRoleToUsers&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;
  &lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Accounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;UserRole&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;UserRole&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_type&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;alter&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;add&lt;/span&gt; &lt;span class="ss"&gt;:role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:user_role&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It requires creating a new module for the enum type, but it has some nice conveniences for using the type in your schema and for creating the type in your migrations in the first place. But using one more dependency also means &lt;strong&gt;one more thing in the application that has to be maintained.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ecto 3.5
&lt;/h2&gt;

&lt;p&gt;Ecto 3.5 was released in October 2020, and it included the new &lt;a href="https://hexdocs.pm/ecto/Ecto.Enum.html"&gt;&lt;code&gt;Ecto.Enum&lt;/code&gt;&lt;/a&gt; module. The module's documentation expects the Enum type to be a string when stored in the database, but we can also make it work with a Postgres enum. This means you can now use enums without needing another library. &lt;code&gt;Ecto SQL&lt;/code&gt; still doesn't have convenience functions for creating enums in migrations (yet) so you'll have to drop down to raw SQL and do that manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;AddRoleToUsers&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"CREATE TYPE user_role AS ENUM ('admin', 'moderator', 'seller', 'buyer')"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;alter&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;add&lt;/span&gt; &lt;span class="ss"&gt;:role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:user_role&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;down&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;alter&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;remove&lt;/span&gt; &lt;span class="ss"&gt;:role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:user_role&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"DROP TYPE user_role"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you can add an enum to your schema without having to create a new module and Ecto type though:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Accounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;

  &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="s2"&gt;"users"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;values:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:admin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:moderator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:seller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:buyer&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just like the EctoEnum library, Ecto handles all of the casting for changesets, and the role is available as an atom rather than a string. This provides some &lt;strong&gt;safety&lt;/strong&gt; because you will be able to more easily differentiate between the&lt;br&gt;
unsafe string values provided by users and the safer atoms that have been validated by the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  What do you think?
&lt;/h2&gt;

&lt;p&gt;Do you like the new Ecto.Enum API, or do you wish the API was different? Are you going to stick to the &lt;code&gt;EctoEnum&lt;/code&gt; library or to just using Strings?  &lt;a href="https://twitter.com/dnsbty"&gt;Let me know on Twitter!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if you liked this post, hopefully you'll like the others I write. &lt;a href="https://tinyletter.com/dnsbty"&gt;Subscribe to my email newsletter&lt;/a&gt; and get a notification whenever I write a new one!&lt;/p&gt;

&lt;p&gt;Happy New Year!&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>postgres</category>
      <category>programming</category>
      <category>refactorit</category>
    </item>
    <item>
      <title>How to Create a Todo List with Phoenix LiveView</title>
      <dc:creator>Dennis Beatty</dc:creator>
      <pubDate>Mon, 02 Nov 2020 17:03:20 +0000</pubDate>
      <link>https://dev.to/dnsbty/how-to-create-a-todo-list-with-phoenix-liveview-5c32</link>
      <guid>https://dev.to/dnsbty/how-to-create-a-todo-list-with-phoenix-liveview-5c32</guid>
      <description>&lt;p&gt;&lt;em&gt;Update Nov 2, 2020: It's been over a year and a half since I first published this Phoenix LiveView tutorial. Since then a lot has changed with the library. I've now updated the entire tutorial to work with Phoenix 1.5 and Phoenix LiveView 0.14. The new generators will let you get up and running a lot faster. As always &lt;a href="https://twitter.com/dnsbty"&gt;let me know on Twitter&lt;/a&gt; if there's anything you don't understand or want me to cover more in depth!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I previously released a pretty basic &lt;a href="https://dennisbeatty.com/how-to-create-a-counter-with-phoenix-live-view.html"&gt;Phoenix LiveView tutorial&lt;/a&gt; where we made a simple counter that you could increment and decrement. Phoenix LiveView is a great solution for anyone who wants to get a real-time app out to the world quickly without spending a ton of time building out both frontend and backend. Today I want to show you just how simple it can be to build something that is a little more useful than a counter: a todo list. Because we're building it to be database backed, you could use this same pattern for business applications to add real-time functionality without much overhead.&lt;/p&gt;

&lt;p&gt;Since this is a tutorial for a bigger project, I've created the entire thing on GitHub as well. If you prefer you can see the code there. If you click the computer emoji next to any of the section titles, it will take you to the corresponding commit so you can check what you have against the full codebase there.&lt;/p&gt;

&lt;h2&gt;
  
  
  New LiveView Project &lt;a href="https://github.com/dnsbty/live_view_todos/commit/bbfa7f709c8798470c354201cdc67d0a6ab95e55"&gt;💻&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;To start out, set up a new project with Phoenix LiveView. Thanks to the changes to the generator in Phoenix 1.5, all of the LiveView boilerplate will be added for you when you generate a new project. First, make sure you have the latest version of the generator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix archive.install hex phx_new
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As of the time of writing, version 1.5.4 is the latest. With it installed, you can create a new project. Including the &lt;code&gt;live&lt;/code&gt; flag will include all the boilerplate necessary to get LiveView up and running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix phx.new live_view_todos &lt;span class="nt"&gt;--live&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;Y&lt;/code&gt; to download and install dependencies, and then &lt;code&gt;cd&lt;/code&gt; into the new project directory and setup its database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix ecto.setup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now you can start your server, and go to &lt;a href="http://localhost:4000"&gt;http://localhost:4000&lt;/a&gt; in your browser to see your application running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix phx.server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Generate the Schema &lt;a href="https://github.com/dnsbty/live_view_todos/commit/187d6df73c36a6c5928f2ac39dad5dce9ec5f4d3"&gt;💻&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;With your basic project up and running, you'll want to create the table for the todos. Since there are already plenty of great Phoenix tutorials out there, I'm going to skip over how to build out our data schema manually. Just use the Phoenix generator to do that quickly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix phx.gen.context Todos Todo todos title:string &lt;span class="k"&gt;done&lt;/span&gt;:boolean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then migrate your database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix ecto.migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That should be everything you'll need from the database standpoint, as well as all the functions you'll need to work with your database. Now you'll create an interface for interacting with the todo list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up the page &lt;a href="https://github.com/dnsbty/live_view_todos/commit/02988abb750f8a3889d446c50ae483420534b157"&gt;💻&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I always like to start from the top level when creating a page and work down to the lowest level. So let's start at the router. You should have a route that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;live&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;PageLive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:index&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace that route with a new one for the todo list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;live&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;TodoLive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:index&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you try to run your app, you'll see a compilation error because TodoLive doesn't exist yet. Go ahead and create a new file at &lt;code&gt;lib/live_view_todos_web/live/todo_live.ex&lt;/code&gt;. Implement the standard &lt;code&gt;mount&lt;/code&gt; callback, and use the &lt;code&gt;render&lt;/code&gt; callback to render an example string inline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;LiveViewTodosWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;TodoLive&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;LiveViewTodosWeb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:live_view&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="sx"&gt;~L"Rendering LiveView"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if you start your server again with &lt;code&gt;mix phx.server&lt;/code&gt; and open your browser to &lt;a href="http://localhost:4000"&gt;http://localhost:4000&lt;/a&gt;, you should see "Rendering LiveView" on the page. Awesome! Except now let's do something with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Display the Todo List &lt;a href="https://github.com/dnsbty/live_view_todos/commit/1c36431c103aa00acc288115e5cbdaf752971ac2"&gt;💻&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;On mount, you should load in your todo list so that the live view can render them. Update your mount function to look like this (make sure you don't forget the alias):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;LiveViewTodos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Todos&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;todos:&lt;/span&gt; &lt;span class="no"&gt;Todos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list_todos&lt;/span&gt;&lt;span class="p"&gt;())}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the live view process will have the list of todos, but it isn't actually displaying them.  Create a LiveEEx file to do that. Add a new template file at &lt;code&gt;lib/live_view_todos_web/live/todo_live.html.leex&lt;/code&gt;. In it you'll just add code to display the list of todos from the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;for&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nv"&gt;@todos&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Previously you would have needed to also create a view module and change your &lt;code&gt;render&lt;/code&gt; callback to call the view and tell it to render the template. Thanks to some new Phoenix magic though, a template in the same folder as the live view code that has the same name will be used automatically. For a large project with many live views and templates, this might not be the easiest way to organize things, but because this project is smaller, go ahead and take advantage of it by removing the &lt;code&gt;render&lt;/code&gt; implementation from your live view module so that only the &lt;code&gt;mount&lt;/code&gt; callback remains.&lt;/p&gt;

&lt;p&gt;Now if you refresh your page, you'll see that that it's blank below the header. That's because there aren't currently any todos to display. So let's enable adding new todos to the list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Todo &lt;a href="https://github.com/dnsbty/live_view_todos/commit/e97c0007fa544afeaeeb65a60ee5c8ce635a54ce"&gt;💻&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;To start add a new form to the top of your template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"#"&lt;/span&gt; &lt;span class="n"&gt;phx&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"add"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;text_input&lt;/span&gt; &lt;span class="ss"&gt;:todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;placeholder:&lt;/span&gt; &lt;span class="s2"&gt;"What do you want to get done?"&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;submit&lt;/span&gt; &lt;span class="s2"&gt;"Add"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;phx_disable_with:&lt;/span&gt; &lt;span class="s2"&gt;"Adding..."&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's a few things to break down there. The first is the &lt;code&gt;phx-submit&lt;/code&gt; attribute in the &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; element. This instructs LiveView to send an &lt;code&gt;add&lt;/code&gt; action to the server that contains the contents of the form when it is submitted. The &lt;code&gt;phx_disable_with&lt;/code&gt; attribute on the &lt;code&gt;submit&lt;/code&gt; button is a nice LiveView feature that will disable the button when the form is submitted and change its text to &lt;code&gt;Adding...&lt;/code&gt; so that the user won't submit it more than once. If you try clicking that button now, nothing will happen in the browser, and you'll see an error in the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UndefinedFunctionError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="no"&gt;LiveViewTodosWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;TodoLive&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handle_event&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;undefined&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;private&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;live_view_todos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="no"&gt;LiveViewTodosWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;TodoLive&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handle_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"add"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"todo"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"title"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt; &lt;span class="c1"&gt;#Phoenix.LiveView.Socket&amp;lt;...&amp;gt;)&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's because you haven't set up a handler for the form. You can use the second line of the stacktrace to set up the new function in the live view. Create a new &lt;code&gt;handle_event&lt;/code&gt; function that's watching for an &lt;code&gt;add&lt;/code&gt; event and then takes the todo information that's passed in and creates a todo. Then you'll tell it to load the list of todos again so that the list will be re-rendered. Since you're already doing that in mount, go ahead and refactor that into a private function called &lt;code&gt;fetch&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"add"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"todo"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Todos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_todo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:noreply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;todos:&lt;/span&gt; &lt;span class="no"&gt;Todos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list_todos&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if you type a new title into the input and hit enter or click the &lt;code&gt;Add&lt;/code&gt; button, you'll see that it shows up in the list below! What's cooler than a list that updates itself without us writing any Javascript? You can make it work&lt;br&gt;
across multiple sessions so that if you and I are both looking at the same todo list, you can add a new item and it will show up for us both.&lt;/p&gt;
&lt;h2&gt;
  
  
  Updating in Real-Time with PubSub &lt;a href="https://github.com/dnsbty/live_view_todos/commit/fcee4eb0a5cab66e6f41337a969dc0df5b6202c0"&gt;💻&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;While Phoenix LiveView is the star of the show in this tutorial, its older sister &lt;a href="https://hexdocs.pm/phoenix_pubsub"&gt;Phoenix PubSub&lt;/a&gt; is doing a lot of the heavy lifting behind the scenes. In case you haven't used Phoenix PubSub before, it allows you to set up subscriptions in your app to listen to topics and handle events when they occur. Since the process that manages the LiveView components won't necessarily be the process updating the todo list in the database, we'll want to have the LiveView process subscribe to updates to the todos table.&lt;/p&gt;

&lt;p&gt;Phoenix PubSub comes bundled in Phoenix by default, so all you have to do is use it. To start off, add a function in &lt;code&gt;lib/live_view_todos/todos.ex&lt;/code&gt; to enable subscribing. Use the module name as our PubSub topic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="nv"&gt;@topic&lt;/span&gt; &lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;subscribe&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PubSub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;LiveViewTodos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PubSub&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;@topic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now go ahead and add a call to that function into the &lt;code&gt;mount&lt;/code&gt; callback of your live view so that processes will get subscribed on mount:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Todos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that the process is subscribed, it will need to handle the events that come in. This can be implemented with a &lt;code&gt;handle_info&lt;/code&gt; callback that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_info&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="no"&gt;Todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:todo&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:noreply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the callback is matching on an event from the &lt;code&gt;Todos&lt;/code&gt; topic since you used the module name for the topic name when you created it. Then it's expecting an event key that has &lt;code&gt;:todo&lt;/code&gt; as the first element in a list. If you want to get&lt;br&gt;
fancy, you could try diffing the todos yourself. With the intent of keeping this tutorial as simple as possible, we'll stick to just loading them from the database each time though.&lt;/p&gt;

&lt;p&gt;Now that the live views are subscribed to the topic and handling events, you need to actually broadcast events to them. Go back to the &lt;code&gt;Todos&lt;/code&gt; context and create a new function that will broadcast whenever a change occurs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;broadcast_change&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;Phoenix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PubSub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;LiveViewTodos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PubSub&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;@topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And to make this all work, modify all of the functions in the context that will mutate data in the database so that they will call your new &lt;code&gt;broadcast_change&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;create_todo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="p"&gt;\\&lt;/span&gt; &lt;span class="p"&gt;%{})&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;broadcast_change&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="ss"&gt;:todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:created&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;update_todo&lt;/span&gt;&lt;span class="p"&gt;(%&lt;/span&gt;&lt;span class="no"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;todo&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;broadcast_change&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="ss"&gt;:todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:updated&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;delete_todo&lt;/span&gt;&lt;span class="p"&gt;(%&lt;/span&gt;&lt;span class="no"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;todo&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;broadcast_change&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="ss"&gt;:todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:deleted&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's all there is to it! Now if you open the app in a second window, you can add new todos to the list and see that they're being added in real-time in both windows. Any time there is a change in the &lt;code&gt;Todos&lt;/code&gt; context, it's being&lt;br&gt;
broadcasted out. The live view processes listen to those changes, and then send the update down to the client in real-time. I don't know if there has ever been a way to build something like this with so little effort.&lt;/p&gt;
&lt;h2&gt;
  
  
  Marking Items as Done &lt;a href="https://github.com/dnsbty/live_view_todos/commit/12812b35a8e47dd8fd2deb7563c49f7a85bbaaae"&gt;💻&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Now that you're properly creating and reading the list of todos, let's give updating a shot. Since it's a todo list, you really ought to be able to mark items as done right? To start with, modify the LiveEEx template to show a checkbox next to each todo item. Swap out this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;checkbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:done&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;value:&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll notice that the checkbox value will be specifically handled by &lt;code&gt;todo.done&lt;/code&gt;.  This will now display whether or not an item is done. Try toggling one or two via psql (&lt;code&gt;UPDATE todos SET done = TRUE WHERE id = 1;&lt;/code&gt;) and refresh&lt;br&gt;
the page; you'll see that its box will get checked. But you want to be able to update those values using the checkboxes. Modify that checkbox line in your template to add some more attributes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;checkbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:done&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;phx_click:&lt;/span&gt; &lt;span class="s2"&gt;"toggle_done"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;phx_value_id:&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;value:&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your checkbox will have a &lt;code&gt;phx_click&lt;/code&gt; as well as a &lt;code&gt;phx_value&lt;/code&gt; attribute. You may remember this from my last tutorial, but in case you don't, &lt;code&gt;phx_click&lt;/code&gt; adds a click handler to an item. Now when the checkbox is clicked, LiveView will send an event to the server. I didn't previously cover &lt;code&gt;phx_value_*&lt;/code&gt; though. It will allow you to specify values that get sent to the server with the event. To see this in action, go ahead and save your new template and then click on one of our checkboxes. You should see an error like this in your terminal where your application is running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;FunctionClauseError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="n"&gt;clause&lt;/span&gt; &lt;span class="n"&gt;matching&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="no"&gt;LiveViewTodosWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;TodoLive&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handle_event&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;live_view_todos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;live_view_todos_web&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;live&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;todo_live&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="ss"&gt;ex:&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;LiveViewTodosWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;TodoLive&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handle_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"toggle_done"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;#Phoenix.LiveView.Socket&amp;lt;...&amp;gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you look at that second line, you'll see that it's trying to call &lt;code&gt;LiveViewTodosWeb.TodoLive.handle_event&lt;/code&gt; and pass the parameters &lt;code&gt;"toggle_done"&lt;/code&gt;, a map of values including one called &lt;code&gt;"id"&lt;/code&gt; set to the ID of the task you clicked on, and the socket. Go ahead and implement a handler for this in your live view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"toggle_done"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Todos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_todo!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;Todos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update_todo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;done:&lt;/span&gt; &lt;span class="n"&gt;!todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:noreply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things are happening there. First the live view uses the ID it receives from the &lt;code&gt;phx_value_*&lt;/code&gt; attribute and fetches the todo from the database. Then it updates that todo and sets its &lt;code&gt;done&lt;/code&gt; property to the opposite of whatever it was before (if it was false, it's now true, and vice versa). Then you'll notice that the process reloads the list of todos from the database and sends them back down to the application. One more thing though: the PubSub subscription should already be telling our socket to fetch all the new todos, so you don't need to do that here. Go ahead and remove the call to &lt;code&gt;fetch&lt;/code&gt; from the new event handler. You can do the same with the &lt;code&gt;add&lt;/code&gt; event too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:noreply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if you try checking (and unchecking) a few boxes, you'll see that they update in real-time, even across multiple browser windows!&lt;/p&gt;

&lt;h1&gt;
  
  
  What's Next?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BCeUe9vG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dennisbeatty.com/content/2019-04-24-how-to-create-a-todo-list-with-phoenix-liveview/todos.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BCeUe9vG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dennisbeatty.com/content/2019-04-24-how-to-create-a-todo-list-with-phoenix-liveview/todos.gif" alt="Todo list updating in real-time in side by side browser windows"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well there you have it! A simple todo list with Phoenix LiveView that didn't take too long to implement. Like I said before, LiveView is awesome, and Phoenix PubSub is pretty amazing too. It was super simple to subscribe to events and&lt;br&gt;
publish those when you update the database, and that allowed you to get this all set up really easily. I would highly recommend spending some time getting more familiar with PubSub because it can be a very powerful tool in your arsenal as you build real-time applications.&lt;/p&gt;

&lt;p&gt;If you want to keep going with this project, the next thing I would recommend is adding a delete functionality. The &lt;code&gt;Todos&lt;/code&gt; context already has a &lt;code&gt;delete_todo&lt;/code&gt; function that you can make use of, so you can probably use what you just did with the checkbox as a template. If you do anything further, please &lt;a href="https://twitter.com/dnsbty"&gt;reach out to me on Twitter&lt;/a&gt; and let me know what you did. I'd love to see the things you're building as a result of these tutorials.&lt;/p&gt;

&lt;p&gt;If you liked this, please &lt;a href="https://twitter.com/intent/retweet?tweet_id=1323283654685020160"&gt;retweet it so others can find&lt;br&gt;
it&lt;/a&gt; and&lt;br&gt;
&lt;a href="https://www.youtube.com/channel/UC9l_1J8y_au9Nlgrfz0rduw?sub_confirmation=1"&gt;subscribe to my YouTube&lt;br&gt;
channel&lt;/a&gt;&lt;br&gt;
so you get notified of my next post. I plan to continue releasing new YouTube videos with accompanying blog posts whenever I can make time. Let me know on Twitter or in the comments for &lt;a href="https://youtu.be/xgz-HTot3R8"&gt;the video above&lt;/a&gt; if there is anything further you'd like to see me cover. Thanks for reading!&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
