Morzaram
How to structure a large live view app?
Hey everyone,
I have a live view app that is slowly getting bigger and I’m really struggling to find a way to manage all of the logic for each page. Components, queries, page, etc.
I was hoping to get some insight on how I can best have file structure and other tips on making sure I can manage all the code for these things. Right now I feel that the contexts are getting in the way and I should be moving the queries in a file next to the page view itself.
Any insight and resources would be much appreciated!
Marked As Solved
kokolegorille
I am suggesting a technique I learned from the book Absinthe/GraphQL..
For example…
def list_requests_query(criteria \\ []) do
query = from(p in Request)
Enum.reduce(criteria, query, fn
{:limit, limit}, query ->
from p in query, limit: ^limit
{:offset, offset}, query ->
from p in query, offset: ^offset
{:filter, filters}, query ->
filter_with(filters, query)
{:order, order}, query ->
from p in query, order_by: [{^order, ^@order_field}]
{:preload, preloads}, query ->
from p in query, preload: ^preloads
arg, query ->
Logger.info("args is not matched in query #{inspect(arg)}")
query
end)
end
defp filter_with(filters, query) do
Enum.reduce(filters, query, fn
{:user_id, user_id}, query ->
from q in query, where: q.user_id == ^user_id
{:is_fetched, is_fetched}, query ->
from q in query, where: q.is_fetched == ^is_fetched
{:is_post_processed, is_post_processed}, query ->
from q in query, where: q.is_post_processed == ^is_post_processed
{:with_medium_path, true}, query ->
from q in query, where: not is_nil(q.medium_path)
{:with_medium_path, false}, query ->
from q in query, where: is_nil(q.medium_path)
arg, query ->
Logger.info("args is not matched in query #{inspect(arg)}")
query
end)
end
def list_requests(criteria \\ []) do
criteria
|> list_requests_query()
|> Repo.all()
end
Using this, I could probably do something like…
filter by location
filter by category
move to the next month
move to the previous month
show/hide the events that already have occured this month
filter by the person who created the event
… and this would translate to
[filter: [by_location: location, by_category: category, by_month: month ...]]
|> App.list_requests()
Also Liked
kokolegorille
Instead of passing queries from web… can You pass criteria to the queries? I use the liveview to manage parameters I send to the query
I have one list_events function that do this only by passing parameters to the query. Do You have multiple queries instead?
mindok
Maybe I’m a heretic, but I’ve stopped using “Live” in any module (or folder) naming - most things are live now so it seems superfluous. Any non-live UI modules are controllers and/or views and named explicitly.
In the example above, I’d go:
lib/project_web/orders
index.ex - ProjectWeb.Orders.Index
view.ex - ProjectWeb.Orders.View
edit.ex - ProjectWeb.Orders.Edit
line_item_view_component.ex - ProjectWeb.Orders.LineItemView
line_item_edit_form.ex - ProjectWeb.Orders.LineItemEdit
Any common logic related to the orders front end would get put in a module in that folder too.
Grouping everything around end user functionality makes it easier (IMO) to navigate between use wants, issues and bugs and the codebase.
Shared/generic components (buttons, progress bars, form fields etc) live in a components area at the top of the tree. Generally I put live components in a module to themselves, and function components grouped by general area of functionality.
lib/project_web/components
forms.ex
nav.ex
datagrid.ex
...
As with most things, the biggest project I’m working on has archeology rather than architecture - there’s a few iterations of thinking about where to put things and not everything is in the right spot as yet.
lccezinha
I usually try to organize my code like this
- lib
-- project
---- orders
------ order.ex # schema
---- orders.ex # context
-- project_web
---- components # for shared/general components
------ header.ex
---- live
------ orders_live
-------- orders_live.ex # LV module handle in/out for info where heavy biz logic lives inside the context
-------- orders_live.html.heex
-------- component_related_to_orders_feature.ex
If that helps you.
If my context starts to grow I can extract into a smaller new module to handle queries, but I don’t do this that often.
Last Post!
kokolegorille
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex










