-
Notifications
You must be signed in to change notification settings - Fork 91
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add guide for on_mount and handle_info #599
Open
djcarpe
wants to merge
1
commit into
BeaconCMS:main
Choose a base branch
from
djcarpe:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,282 @@ | ||||||||||||||
# On Mount Handle Info Loop | ||||||||||||||
|
||||||||||||||
If you would like to continuously monitor or update a page, you can create an [on_mount](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#on_mount/1) callback, and corresponding [handle_info](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#c:handle_info/2) handler. | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And here we could add few words to highlight some problems this pattern can solve:
Suggested change
|
||||||||||||||
|
||||||||||||||
In this example we're going to update a `DateTime` every `1_000` milliseconds from the server, starting that loop `on_mount`, then continuously looping over a `handle_info`. | ||||||||||||||
|
||||||||||||||
## Creating the On Mount Handler Module | ||||||||||||||
|
||||||||||||||
After completing [your first site](../introduction/your-first-site.md) guide, create a module to handle | ||||||||||||||
your `on_mount` callbacks. This is an example, so find a place that works best | ||||||||||||||
for your project structure. Here, we're creating a `my_site_on_mount.ex` file | ||||||||||||||
in our web path. | ||||||||||||||
|
||||||||||||||
```bash | ||||||||||||||
├── lib | ||||||||||||||
│ ├── my_site | ||||||||||||||
│ ├── my_site_web | ||||||||||||||
│ │ ├── components | ||||||||||||||
│ │ │ ├── core_components.ex | ||||||||||||||
│ │ │ ├── layouts | ||||||||||||||
│ │ │ │ ├── app.html.heex | ||||||||||||||
│ │ │ │ └── root.html.heex | ||||||||||||||
│ │ │ ├── callbacks | ||||||||||||||
│ │ │ │ └── my_site_on_mount.ex | ||||||||||||||
│ │ │ └── layouts.ex | ||||||||||||||
│ │ ├── controllers | ||||||||||||||
│ │ │ ├── error_html.ex | ||||||||||||||
│ │ │ ├── error_json.ex | ||||||||||||||
│ │ │ ├── page_controller.ex | ||||||||||||||
│ │ │ ├── page_html | ||||||||||||||
│ │ │ │ └── home.html.heex | ||||||||||||||
│ │ │ └── page_html.ex | ||||||||||||||
│ │ ├── endpoint.ex | ||||||||||||||
│ │ ├── gettext.ex | ||||||||||||||
│ │ ├── router.ex | ||||||||||||||
│ │ └── telemetry.ex | ||||||||||||||
│ └── my_site_web.ex | ||||||||||||||
``` | ||||||||||||||
|
||||||||||||||
When the socket is connected, we will send a message to `self()` in 1_000 milliseconds | ||||||||||||||
to `:update_current_time`. | ||||||||||||||
|
||||||||||||||
```elixir | ||||||||||||||
defmodule MySiteWeb.OnMount do | ||||||||||||||
use MySiteWeb, :live_view | ||||||||||||||
|
||||||||||||||
def on_mount(_path, _params, _session, socket) do | ||||||||||||||
if connected?(socket) do | ||||||||||||||
Process.send_after(self(), :update_current_time, 1_000) | ||||||||||||||
end | ||||||||||||||
|
||||||||||||||
{:cont, socket} | ||||||||||||||
end | ||||||||||||||
end | ||||||||||||||
``` | ||||||||||||||
|
||||||||||||||
## Updating the Live Session | ||||||||||||||
|
||||||||||||||
In your `router.ex` file, add the `on_mount` option to your `my_site` route so it is handled in the live session. | ||||||||||||||
|
||||||||||||||
```elixir | ||||||||||||||
scope "/" do | ||||||||||||||
pipe_through :browser | ||||||||||||||
beacon_site "/", | ||||||||||||||
site: :my_site, | ||||||||||||||
on_mount: {MySiteWeb.OnMount, []} | ||||||||||||||
end | ||||||||||||||
``` | ||||||||||||||
## Accessing LiveAdmin to manage your site | ||||||||||||||
|
||||||||||||||
Let's customize the home page. Visit http://localhost:4000/admin and you should see the `my_site` that you just created listed on the admin interface. | ||||||||||||||
|
||||||||||||||
Now let's create the resources for our first site, and we'll starting by creating the resources used by the home page before we customize its template. | ||||||||||||||
|
||||||||||||||
## Live Data | ||||||||||||||
|
||||||||||||||
We want to display the current time on the home page as the assign `<%= @current_time %>`. In Beacon we use Live Data to create and modify assigns at runtime, let's do it. | ||||||||||||||
|
||||||||||||||
Go to http://localhost:4000/admin/my_site/live_data and navigate to the "/" path and create a new assign named `current_time` using the `elixir` format with the following value: | ||||||||||||||
|
||||||||||||||
```elixir | ||||||||||||||
DateTime.now!("Etc/UTC") | ||||||||||||||
``` | ||||||||||||||
|
||||||||||||||
Save the changes. | ||||||||||||||
|
||||||||||||||
## Handle the :update_current_time info callback | ||||||||||||||
|
||||||||||||||
Besides displaying some data, we will have a form to subscribe people to our newsletter, so we need to react to that event. Let's create an event handler for that. | ||||||||||||||
|
||||||||||||||
Go to http://localhost:4000/admin/my_site/info_handlers and create a new event handler named `update_current_time` with the following content: | ||||||||||||||
|
||||||||||||||
```elixir | ||||||||||||||
Process.send_after(self(), :update_current_time, 1000) | ||||||||||||||
|
||||||||||||||
{:noreply, assign(socket, current_time: DateTime.now!("Etc/UTC"))} | ||||||||||||||
``` | ||||||||||||||
## Update the home page template | ||||||||||||||
|
||||||||||||||
Finally, let's update the home page template to make use of the info handler we created. | ||||||||||||||
|
||||||||||||||
Go to http://localhost:4000/admin/my_site/pages and you should see a page already created for the `/` path. We're going to change it. | ||||||||||||||
|
||||||||||||||
Edit the template to replace with this content: | ||||||||||||||
|
||||||||||||||
```heex | ||||||||||||||
<div class="relative flex min-h-[100dvh] flex-col overflow-hidden bg-gradient-to-br from-[#0077b6] to-[#00a8e8] text-white"> | ||||||||||||||
<div class="absolute inset-0 z-[-1] bg-cover bg-center opacity-30 blur-[100px]"></div> | ||||||||||||||
<header class="container mx-auto flex items-center justify-between py-6 px-4 md:px-6"> | ||||||||||||||
<div class="flex items-center gap-2"> | ||||||||||||||
<.link patch="/" class="flex items-center gap-2"> | ||||||||||||||
<svg | ||||||||||||||
xmlns="http://www.w3.org/2000/svg" | ||||||||||||||
width="24" | ||||||||||||||
height="24" | ||||||||||||||
viewBox="0 0 24 24" | ||||||||||||||
fill="none" | ||||||||||||||
stroke="currentColor" | ||||||||||||||
stroke-width="2" | ||||||||||||||
stroke-linecap="round" | ||||||||||||||
stroke-linejoin="round" | ||||||||||||||
class="h-8 w-8" | ||||||||||||||
> | ||||||||||||||
<path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"></path> | ||||||||||||||
<line x1="4" x2="4" y1="22" y2="15"></line> | ||||||||||||||
</svg> | ||||||||||||||
<span class="text-2xl font-bold">CMS Platform</span> | ||||||||||||||
</.link> | ||||||||||||||
</div> | ||||||||||||||
<div class="flex items-center gap-4"> | ||||||||||||||
<.link patch="/blog" class="hidden md:inline-flex text-sm font-medium hover:underline"> | ||||||||||||||
Blog | ||||||||||||||
</.link> | ||||||||||||||
<button class="items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-secondary text-secondary-foreground hover:bg-secondary/80 h-10 px-4 py-2 hidden md:inline-flex"> | ||||||||||||||
Sign In | ||||||||||||||
</button> | ||||||||||||||
</div> | ||||||||||||||
</header> | ||||||||||||||
<main class="container mx-auto flex-1 px-4 md:px-6"> | ||||||||||||||
<div class="mx-auto max-w-6xl space-y-6 py-12 md:py-24 lg:py-32"> | ||||||||||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-8"> | ||||||||||||||
<div class="space-y-6"> | ||||||||||||||
<h1 class="text-4xl font-bold leading-tight md:text-5xl lg:text-6xl"> | ||||||||||||||
<span class="bg-gradient-to-r from-[#00a8e8] to-[#0077b6] bg-clip-text text-[#333] dark:text-white"> | ||||||||||||||
Unlock the power | ||||||||||||||
</span> | ||||||||||||||
of your content with our CMS platform | ||||||||||||||
</h1> | ||||||||||||||
<p class="text-lg text-gray-300 md:text-xl"> | ||||||||||||||
Streamline your content management with our intuitive, high-performance CMS built on Phoenix LiveView. | ||||||||||||||
</p> | ||||||||||||||
<Phoenix.Component.form :let={f} for={%{}} as={:waitlist} phx-submit="join"> | ||||||||||||||
<div class="flex w-full max-w-2xl items-center space-x-2"> | ||||||||||||||
<input | ||||||||||||||
id={Phoenix.HTML.Form.input_id(f, :email)} | ||||||||||||||
name={Phoenix.HTML.Form.input_name(f, :email)} | ||||||||||||||
class="flex h-10 w-full border border-input text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 flex-1 rounded-md border-none bg-white/10 py-3 px-4 text-white placeholder:text-gray-300 focus:ring-2 focus:ring-[#00a8e8]" | ||||||||||||||
placeholder="Enter your email" | ||||||||||||||
type="email" | ||||||||||||||
/> | ||||||||||||||
<button | ||||||||||||||
class="inline-flex items-center justify-center whitespace-nowrap text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 text-primary-foreground h-10 rounded-md bg-[#00a8e8] py-3 px-6 font-medium transition-colors hover:bg-[#0077b6]" | ||||||||||||||
type="submit" | ||||||||||||||
> | ||||||||||||||
Join Waitlist | ||||||||||||||
</button> | ||||||||||||||
</div> | ||||||||||||||
</Phoenix.Component.form> | ||||||||||||||
<span :if={Map.get(assigns, :joined)} class="text-sm text-gray-300"> | ||||||||||||||
Congrats! You joined the watchlist. | ||||||||||||||
</span> | ||||||||||||||
</div> | ||||||||||||||
<div class="flex items-center justify-center"> | ||||||||||||||
<.image | ||||||||||||||
site={@beacon.site} | ||||||||||||||
name="image.webp" | ||||||||||||||
alt="My Page Image" | ||||||||||||||
width="600" | ||||||||||||||
height="600" | ||||||||||||||
style="aspect-ratio: 600 / 600; object-fit: cover;" | ||||||||||||||
/> | ||||||||||||||
</div> | ||||||||||||||
</div> | ||||||||||||||
</div> | ||||||||||||||
</main> | ||||||||||||||
<footer class="container mx-auto border-t border-white/20 py-6 px-4 text-sm text-gray-300 md:px-6"> | ||||||||||||||
<div class="flex items-center justify-between"> | ||||||||||||||
<p>©<%= @current_time %> <%= @current_year %> CMS Platform. All rights reserved.</p> | ||||||||||||||
<div class="flex space-x-4 items-center"> | ||||||||||||||
<a class="hover:underline" href="#"> | ||||||||||||||
<svg | ||||||||||||||
xmlns="http://www.w3.org/2000/svg" | ||||||||||||||
width="24" | ||||||||||||||
height="24" | ||||||||||||||
viewBox="0 0 24 24" | ||||||||||||||
fill="none" | ||||||||||||||
stroke="currentColor" | ||||||||||||||
stroke-width="2" | ||||||||||||||
stroke-linecap="round" | ||||||||||||||
stroke-linejoin="round" | ||||||||||||||
class="h-5 w-5" | ||||||||||||||
> | ||||||||||||||
<path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z"></path> | ||||||||||||||
</svg> | ||||||||||||||
<span class="sr-only">Facebook</span> | ||||||||||||||
</a> | ||||||||||||||
<a class="hover:underline" href="#"> | ||||||||||||||
<svg | ||||||||||||||
xmlns="http://www.w3.org/2000/svg" | ||||||||||||||
width="24" | ||||||||||||||
height="24" | ||||||||||||||
viewBox="0 0 24 24" | ||||||||||||||
fill="none" | ||||||||||||||
stroke="currentColor" | ||||||||||||||
stroke-width="2" | ||||||||||||||
stroke-linecap="round" | ||||||||||||||
stroke-linejoin="round" | ||||||||||||||
class="h-5 w-5" | ||||||||||||||
> | ||||||||||||||
<path d="M22 4s-.7 2.1-2 3.4c1.6 10-9.4 17.3-18 11.6 2.2.1 4.4-.6 6-2C3 15.5.5 9.6 3 5c2.2 2.6 5.6 4.1 9 4-.9-4.2 4-6.6 7-3.8 1.1 0 3-1.2 3-1.2z"> | ||||||||||||||
</path> | ||||||||||||||
</svg> | ||||||||||||||
<span class="sr-only">Twitter</span> | ||||||||||||||
</a> | ||||||||||||||
<a class="hover:underline" href="#"> | ||||||||||||||
<svg | ||||||||||||||
xmlns="http://www.w3.org/2000/svg" | ||||||||||||||
width="24" | ||||||||||||||
height="24" | ||||||||||||||
viewBox="0 0 24 24" | ||||||||||||||
fill="none" | ||||||||||||||
stroke="currentColor" | ||||||||||||||
stroke-width="2" | ||||||||||||||
stroke-linecap="round" | ||||||||||||||
stroke-linejoin="round" | ||||||||||||||
class="h-5 w-5" | ||||||||||||||
> | ||||||||||||||
<rect width="20" height="20" x="2" y="2" rx="5" ry="5"></rect> | ||||||||||||||
<path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"></path> | ||||||||||||||
<line x1="17.5" x2="17.51" y1="6.5" y2="6.5"></line> | ||||||||||||||
</svg> | ||||||||||||||
<span class="sr-only">Instagram</span> | ||||||||||||||
</a> | ||||||||||||||
<a class="hover:underline" href="#"> | ||||||||||||||
<svg | ||||||||||||||
xmlns="http://www.w3.org/2000/svg" | ||||||||||||||
width="24" | ||||||||||||||
height="24" | ||||||||||||||
viewBox="0 0 24 24" | ||||||||||||||
fill="none" | ||||||||||||||
stroke="currentColor" | ||||||||||||||
stroke-width="2" | ||||||||||||||
stroke-linecap="round" | ||||||||||||||
stroke-linejoin="round" | ||||||||||||||
class="h-5 w-5" | ||||||||||||||
> | ||||||||||||||
<path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"> | ||||||||||||||
</path> | ||||||||||||||
<rect width="4" height="12" x="2" y="9"></rect> | ||||||||||||||
<circle cx="4" cy="4" r="2"></circle> | ||||||||||||||
</svg> | ||||||||||||||
<span class="sr-only">LinkedIn</span> | ||||||||||||||
</a> | ||||||||||||||
<a class="hover:underline" href="#"> | ||||||||||||||
Privacy | ||||||||||||||
</a> | ||||||||||||||
<a class="hover:underline" href="#"> | ||||||||||||||
Terms | ||||||||||||||
</a> | ||||||||||||||
<a class="hover:underline" href="#"> | ||||||||||||||
Contact | ||||||||||||||
</a> | ||||||||||||||
</div> | ||||||||||||||
</div> | ||||||||||||||
</footer> | ||||||||||||||
</div> | ||||||||||||||
``` | ||||||||||||||
|
||||||||||||||
Save the changes and Publish the page. Go to http://localhost:4000 to check the final result! | ||||||||||||||
|
||||||||||||||
You should see `DateTime` updating every `1_000` milliseconds from the server. | ||||||||||||||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe change the title to be a bit more generic to capture more use cases that
handle_info
is able to solve?Something like:
Wdyt?