odix67

odix67

as @msaraiva already mentioned in post 162 you have to use the master branch of surface in combination with LV 0.16.x and replace the <%= %> with curly brackets {}

1
Post #183
cmo

cmo

There is a LiveFileInput too.

1
Post #184
maz

maz

Awesome, thanks. I’ll check it out.

0
Post #185
daveboo

daveboo

Is Surface going to render alpine.js useless in Phoenix?

0
Post #186
mikl

mikl

Is Surface going to render alpine.js useless in Phoenix?

Not at all. Surface is still just for server-side rendering. If you want to do anything in your app that does not require a round-trip to the server to have your LiveView re-rendered, you need JavaScript, and most likely something like Alpine.js.

3
Post #187
joerichsen

joerichsen

It looks like @chrismccord has something nice coming soon that will make the need for something like Alpine go away in some/most cases.

Check out this PR Add JS commands by chrismccord · Pull Request #1663 · phoenixframework/phoenix_live_view · GitHub

It looks pretty awesome :slight_smile:

6
Post #188
daveboo

daveboo

Sorry, this reply was aimed at @joerichsen’s comments :upside_down_face:

Yes, shades of Alpine and maybe Svelte? in there.

Looks great!

0
Post #190
wdiechmann

wdiechmann

There are probably many in this thread who figured it out already - I’m hoping for one of you to give me that push across the finishing line here :sweat_smile:

Please consider this very simplified “UI”

example desktop
example desktop1440×900 1.8 KB

that will serve to illustrate my predicament - and my question(s)

  • the blue could be a “profile” avatar with a dropdown menu - one item being ‘list your sales today’
  • the red could be a “sales” button
  • the green could be a overview of sales - best sales_order in nbr of items, max profit, most items in stock, etc
  • the yellow could be a list of sales

With Rails (and js - or recently Hotwire) I would have the layout in basically any place, or nicely divided into partials whichever suits my use-case, and I can afford the user to tap the blue and in a controller method generate data (and rendering transitions) for the red, the green, the blue, and the yellow - and on my next tap of the blue, now possibly another controller method/action could do all the magic on the colors, with partial renderings coming from a number of sources, and now suddenly

  • the blue could be the avatar still - but the dropdown menu now with a different dropdown (for some reason)
  • the red - a list stocks button now translated into French
  • the green - an action bar available to the yellow
  • the yellow - a ‘dynamic document’ where 2-3 workers are trying so solve what number of items should be ordered for stock from a huge number of possible SKU’s (a lot like @josevalim wonderful livebook - only for trend-analysis on past stock_transactions

With the PETALS [ Phoenix | Elixir | Tailwind | Alpine | LiveView | Surface ] stack I am really fighting to get a grip on the “moving parts” - and perhaps more importantly, the proper place to organize sources.

With LiveView I understand that I can live_render something very much akin to rails partials - which is okay, but adding Surface “to the mix” :wink: 'cause I really like the componentization/modularizing idea I seem to have to build a huge .sface

<Main {... messages: @flash, title: @title, info: @info, buttons: @buttons }>
    <Login :if={@auth == :logged_out || @live_action == :set_password}
	   {... prompt: @password_prompt, submit: "pw_submit" }/>
    <Search :if={@auth == :logged_in && @live_action == :search}
	    {... default_query: @default_query, submit: "search",
	    pick_example: "pick_search_example" } />
    <Write :if={@live_action == :write}
	   {... change: "write_change", auto_recover: "write_recover",
	   subject: @subject, recipients: @recipients,
	   addr_options: @addr_options, text: @write_text } />
    <Config :if={@auth == :logged_in && @live_action == :config}
	    {... change: "config_change", my_addr: @my_addr, my_addrs: @my_addrs,
	    my_lists: @my_lists, days: @archive_days, maildir: @archive_maildir,
	    orbit_api_key: @orbit_api_key, orbit_workspace: @orbit_workspace} />
    <div :if={@auth == :logged_in} hidden={@live_action != :find}>
	<Find {... mails: @list_mails, tree: @list_tree, tz_offset: @tz_offset } />
    </div>
    <div :if={@auth == :logged_in} hidden={@live_action != :view}>
	<View {... meta: @mail_meta, content: @mail_content,
	      attachments: @mail_attachment_metas, tz_offset: @tz_offset } />
    </div>
</Main>

and an even bigger .ex

...
def mount(_params, _session, socket) do
    if connected?(socket), do: PubSub.subscribe(Liv.PubSub, "messages")
    {:ok, socket}
  end

  # for the initial mount before login
  def handle_params(_params, _url, %Socket{assigns: %{live_action: :login}} = socket) do
    user = System.get_env("USER")

    {
      :noreply,
      socket
      |> clear_flash()
      |> push_event("set_value", %{key: "token", value: ""})
      |> assign(
        auth: :logged_out,
        saved_path: Routes.mail_path(socket, :find, @default_query),
        page_title: "Login as #{user}",
        password_hash: Application.get_env(:liv, :password_hash),
        password_prompt: "Enter your password: ",
        info: "Login as #{user}",
        buttons: []
      )
    }
  end

  # write must be outside auth protection for form recovery
  def handle_params(
        %{"to" => to},
        _url,
        %Socket{assigns: %{live_action: :write, mail_opened: false}} = socket
      ) do
    {recipients, subject, text} = MailClient.parse_mailto(to)

    {
      :noreply,
      socket
      |> assign(
        page_title: "Write",
        info: "",
        recipients: recipients,
        subject: subject,
        write_text: MailClient.quoted_text(nil, text),
        write_attachments: [],
        incoming_attachments: [],
        current_attachment: nil,
        write_chunk_outstanding: false,
        buttons: [
          {:button, "\u{1F4EC}", "send", false},
          {:attach, "\u{1F4CE}", "write_attach", false},
          {:button, "\u{1F5D1}", "drop_attachments", false},
          {:button, "\u{2716}", "close_write", false}
        ]
      )
    }
  end

  def handle_params(_params, url, %Socket{assigns: %{auth: nil}} = socket) do
...

that somehow - it seems to me - totally defeat the component/module idea in the first place?

Sorry @derek-zhou for c/p-ing your source which BTW I find totally cool and a very good idea which I’d really like to extend with a few other ideas (once I get my head around this Surface stuff).

So - yeah - there you have it - all my “none-knowledge” :woozy_face: I’m totally aware that I most definitely have misunderstood this Surface thing; just can’t put my finger on it!

1
Post #191
derek-zhou

derek-zhou

As you can see, my code organization is pretty simple. I have only one Liveview, no stateful components, etc. I would not call 60 LOC in a template or 1200 LOC in an .ex source huge, not by a long shot. Even if I have a hundred data assigns, 50 handle_params clauses and 10,000 LOC in the liveview file still nothing will break. Heck, 30 years ago people already have source file longer than 10,000 lines on DOS.

0
Post #192