View Source LiveNavigator behaviour (live_navigator v0.1.5)
This library improves Phoenix LiveView navigation and adds application state to your application. Often when user refreshes browser or presses back button you loose current live view state. Also when user navigates through live views you can't get this navigation path (for example from which page user reached current page). This library solves both of these problems.
WARNGING! The
0.1.x
version is not production ready yet. The nearest production ready version would be0.2.x
usage
Usage
Add library to dependencies
{:live_navigator, "~> 0.1"}
Add LiveNavigator
to your application supervisor
children = [
...
LiveNavigator,
...
]
Add LiveNavigator.Plug
to your browser pipeline:
defmodule YourWebApp.Router do
pipeline :browser do
...
LiveNavigator.Plug
end
end
To detect different browser tabs LiveNavigator needs to run some code on the
client side. So add the following to your assets/package.json
:
"live_navigator": "file:../../../deps/live_navigator",
And to your assets/app.js
:
import { initNavigator } from './live_navigator';
const liveSocket = new LiveSocket('/live', Socket, initNavigator({
// your phoenix LiveSocket params here
}));
Then use LiveNavigator
in your live views and LiveNavigator.Component
in
your live components (LiveNavigator.Component
is necessary only in those
components that are doing any kind of redirection or navigator/page
assignments)
defmodule YourWebApp.ExampleLive do
use YourWebApp, :live_view
use LiveNavigator
...
end
defmodule YourWebApp.Components.NavBar do
use YourWebApp, :live_component
use LiveNavigator.Component
...
end
In case you are going to use LiveNavigator for entire application it's better to place usage into your app definitions:
defmodule YourWebApp do
def liver_view do
quote do
use Phoenix.LiveView
use LiveNavigator
end
end
def live_component do
quote do
use Phoenix.LiveComponent
use LiveNavigator.Component
end
end
end
Now you have few additional assign functions in your live views:
&assign_page/3
, &assign_nav/3
, &assign_page_new/3
, &assign_nav_new/3
,
&clear_page/2
and &clear_nav/3
. All of them works with assigns.
LiveNavigator introduces two application states: navigator state and page
state. Navigator state is a global application state that is unique for live
views opened from deffierent browser tab and different HTTP session (that is
set up via cookies usualy). Page state is a state that unique for different
live view module and live view action and with above conditions for navigator
state. In other words when user opens in browser one of your pages, lets say
page A
the new navigator and page states created. That he navigates to
different page B
new page state created (but page state A
is not deleted).
If he then backs to page A
it's state will be loaded into your assigns.
While navigator state stay same for all above operations. If user then opens
any page in new browser tab then all states including navigator state will be
created from sсratch.
To control user navigation you now has several additional callbacks in your
live views: &handle_page_refresh/2
, &handle_page_leave/4
and
&handle_page_enter/4
. &handle_page_refresh/2
will be called if user
refreshes browser and stay on the same page. &handle_page_enter/4
called
when user enters the page from other location. And &handle_page_leave/4
called when user going to another location. Note that while
&handle_page_refresh/2
and &handle_page_enter/4
are called before standard
handle_params
callback and executed in live view process, but
&handle_page_leave/4
called in process of live view where user went and the
only save functions here is the assign-related functions provided by
LiveNavigator.
Link to this section Summary
Callbacks
Called when user moves to this page from another. The first argument is the
action that leaded the event (see action_spec
for details), the second
argument is the view specification of the page the user comes from, the
third argument is the view specification of the page the user comes to
(which is current view in this case) and the fourth is the socket. This
function must return {:noreply, Socket.t}
tuple.
Called when user moves from another page to this one. This function unlike
&handle_page_refresh/2
and &handle_page_enter/4
called from another
process and have no access to socket. The only safe functions here is the
assets-related functions provided by LiveNavigator
. Arguments are the same
as in &handle_page_enter/4
except that the last argument is the
LiveNavigator
object instead of Socket
. This function must return
{:noreply, LiveNavigator.t}
tuple
Called when user refreshes the page in browser. The first argument is the
page view specification and the second is the socket. This function must
return {:noreply, Socket.t}
tuple.
Functions
Returns a specification to start this module under a supervisor.
Link to this section Types
@type action() :: atom()
@type action_method() :: :navigate | :patch | :redirect | :browse
@type action_spec() :: %{ :method => action_method(), :action => history_action(), optional(:as) => LiveNavigator.History.name(), optional(:to) => non_neg_integer() | atom() }
@type assigns() :: map()
@type back_index() :: LiveNavigator.History.index() | LiveNavigator.History.spec() | {LiveNavigator.History.index() | LiveNavigator.History.spec(), boolean()}
@type changes() :: [atom()]
@type field() ::
:session_id | :tab | :url | :view | :action | :awaiting | :history | :assigns
@type history_action() :: :put | :stack | :stack_add | {:replace, LiveNavigator.History.index(), boolean()}
@type session_id() :: binary()
@type t() :: %LiveNavigator{ __changed__: changes(), action: action() | nil, assigns: assigns(), awaiting: tuple() | nil, history: LiveNavigator.History.t(), session_id: session_id(), tab: tab(), url: url() | nil, view: view() | nil }
@type tab() :: integer()
@type url() :: binary()
@type view() :: module()
Link to this section Callbacks
@callback handle_page_enter( action_spec(), LiveNavigator.History.spec() | nil, LiveNavigator.History.spec(), Phoenix.LiveView.Socket.t() ) :: {:noreply, Phoenix.LiveView.Socket.t()}
Called when user moves to this page from another. The first argument is the
action that leaded the event (see action_spec
for details), the second
argument is the view specification of the page the user comes from, the
third argument is the view specification of the page the user comes to
(which is current view in this case) and the fourth is the socket. This
function must return {:noreply, Socket.t}
tuple.
@callback handle_page_leave( action_spec(), LiveNavigator.History.spec(), LiveNavigator.History.spec(), t() ) :: {:noreply, t()}
Called when user moves from another page to this one. This function unlike
&handle_page_refresh/2
and &handle_page_enter/4
called from another
process and have no access to socket. The only safe functions here is the
assets-related functions provided by LiveNavigator
. Arguments are the same
as in &handle_page_enter/4
except that the last argument is the
LiveNavigator
object instead of Socket
. This function must return
{:noreply, LiveNavigator.t}
tuple
@callback handle_page_refresh(LiveNavigator.History.spec(), Phoenix.LiveView.Socket.t()) :: {:noreply, Phoenix.LiveView.Socket.t()}
Called when user refreshes the page in browser. The first argument is the
page view specification and the second is the socket. This function must
return {:noreply, Socket.t}
tuple.
Link to this section Functions
@spec assign_page(Phoenix.LiveView.Socket.t(), keyword() | map()) :: Phoenix.LiveView.Socket.t()
@spec assign_page(t(), keyword() | map()) :: t()
@spec assign_page(Phoenix.LiveView.Socket.t(), atom(), any()) :: Phoenix.LiveView.Socket.t()
@spec assign_page(t(), atom(), any()) :: t()
@spec assign_page_new(Phoenix.LiveView.Socket.t(), atom(), assign_new_callback()) :: Phoenix.LiveView.Socket.t()
Returns a specification to start this module under a supervisor.
See Supervisor
.
@spec clear_page(Phoenix.LiveView.Socket.t(), atom() | [atom()]) :: Phoenix.LiveView.Socket.t()
@spec clear_page(t(), atom() | [atom()]) :: t()
@spec current_url(Phoenix.LiveView.Socket.t() | t()) :: binary() | nil
@spec handle_params(map(), binary(), Phoenix.LiveView.Socket.t()) :: {:cont | :halt, Phoenix.LiveView.Socket.t()}
@spec history(Phoenix.LiveView.Socket.t() | t()) :: LiveNavigator.History.t()
@spec on_mount(any(), map(), map(), Phoenix.LiveView.Socket.t()) :: {:cont, Phoenix.LiveView.Socket.t()}
@spec push_patch( Phoenix.LiveView.Socket.t(), keyword() ) :: Phoenix.LiveView.Socket.t()
@spec push_redirect( Phoenix.LiveView.Socket.t(), keyword() ) :: Phoenix.LiveView.Socket.t()
@spec redirect( Phoenix.LiveView.Socket.t(), keyword() ) :: Phoenix.LiveView.Socket.t()
@spec start_link(keyword()) :: Supervisor.on_start()