Valian / live_vue

End-to-end reactivity for Phoenix LiveView and Vue
https://hex.pm/packages/live_vue
MIT License
246 stars 16 forks source link

Demo for third-party libraries #5

Open roger120981 opened 6 months ago

roger120981 commented 6 months ago

Is it possible to create a small example that integrates third-party libraries such as vue-flowbite or shadcn?

Valian commented 6 months ago

@roger120981 Certainly! I'm working on some examples right now.

Will show:

That said, it should be as straightforward as installing using npm install and using these libraries in Vue components. No magic here, it should just work ;)

Any more examples you'd like to see?

roger120981 commented 6 months ago

@Valian I have been working in the geospatial industry for years, and many times a dashboard is needed to visualize different indicators coming from the backend. It would be interesting to integrate some example with libraries like maplibrejs or deckgl into this stack, I know there is a wrapper called vue-maplibre-gl, even if it is simple it is fine for me, I just want to see the possibilities of live-vue for future projects. Thanks

cvkmohan commented 6 months ago

I am really looking forward to the forms examples. How do we combine the server validation with a client side input component. I hope it will not be regular JS way - but - nearer to the Ecto, Phoenix way.

Valian commented 6 months ago

I'm thinking what would be the best way to solve it.

Probably I could implement Jason.Encoder for changeset and add some utils to extract errors for a given (possibly nested) field 🤔

cvkmohan commented 6 months ago

Hmm... can we not use phx-data and phx-events - and - render the inputs in a regular phoenix form? Your solution looks definitely workable - but - I am worried - we are moving more and more away from the regular Phoenix Development approach. That is when the maximum benefit from the library can be derived. My two cents. Sorry, if it is not making sense.

Valian commented 6 months ago

How phoenix is doing it

  1. In your view, you crate a form out of changeset eg. data |> Ecto.Changeset.cast(...) |> to_form(as: "form")
  2. In you template, you render form and form inputs using <.input field={form[:field]}> component. form[:field] basically extracts id, name, errors and value from changeset. Your <.input> component makes use of it:
  def input(assigns) do
    ~H"""
    <div phx-feedback-for={@name}>
      <.label for={@id}><%= @label %></.label>
      <input
        type={@type}
        name={@name}
        id={@id}
        value={Phoenix.HTML.Form.normalize_value(@type, @value)}
        class={[
          "mt-2 block w-full rounded-lg text-zinc-900 focus:ring-0 sm:text-sm sm:leading-6",
          "phx-no-feedback:border-zinc-300 phx-no-feedback:focus:border-zinc-400",
          @errors == [] && "border-zinc-300 focus:border-zinc-400",
          @errors != [] && "border-rose-400 focus:border-rose-400"
        ]}
        {@rest}
      />
      <.error :for={msg <- @errors}><%= msg %></.error>
    </div>
    """
  end

In other words, it simply updates classes and render errors.

  1. Phoenix JS library overrides normal form behaviour if you define phx-change or phx-submit. It collects all the inputs and their values into an object and pushes it as an event to LiveView, so you can validate / handle submit.

So this is a small overview how it works with LiveView. What can we accomplish with LiveVue?

How it works in LiveVue

  1. & 2. Vue "takes over" rendering from Phoenix. Once you use <.vue>, phoenix no longer is responsible for rendering that component. We can't directly use components from Elixir (unless we'll go with slots, but it's not always an option).
  2. Phoenix JS library can still handle form submits when using phx-submit etc in Vue. So we can and should make it working.

The best way forward

To render form in Vue and make use of easy live validation from Phoenix, we'd need to:

  1. Pass form to vue component <.vue form={@form}>. It would require serializing form to JSON.
  2. Create some utilities in LiveVue js library to render these forms, mimicking LIveView behaviour (naming, ids, errors etc).
<script setup lang="ts">
import {useForm} from 'liveVue';

const props = defineProps<{form: any}>();

const form = useForm(props.form);
</script>

<template>
  <form phx-submit="submit">
     <input type="text" :id="form['field'].id" :name="form['field'].name" value="form['field'].value">
  </form>
</template>

This is just a quick sketch, but I believe it might be the best apprach. Maybe we could provide some built-in components for rendering inputs.

@cvkmohan how do you like it? do you have any other ideas?

cvkmohan commented 6 months ago

That is a detailed example @Valian. Thanks a lot. I am not sure about the difficulties - but - the best DX comes from using even the form component from phoenix only. <.input> is a phoenix functional component. If we can replace the call with a vue component - That will give the best experience for the developer. If we can put up such workflow, we will be leaving the extract id, name, errors and value to the regular workflow itself. Loud thinking - can we have an <.vue_input> component that interacts with Phoenix.HTML.FormField structure in both directions.

cvkmohan commented 6 months ago

I think the main point of contention in handling forms in LiveVue application would be - what is the lowest atomic unit - Is it form or form_component(Each input unit) If we make form to be the atomic unit - advantage would be - entire Vue Input Component System would be at disposal for developer. DateRange, Tree, Tags - you name it - it will be accessible to the application. However, the disadvantage would be that the Form-Ecto communication that is built as a protocol will be broken. So, we need to bridge that gap. As you said, Json encoding - and - translating the to-and-fro messages in realtime. If we make the form_component as the atomic unit - advantage would be - we can completely rely on Phoenix built-in form handling and use a mixture of Phoenix Function Components and Vue Components to build the form. It is a very seamless way for the application developer in the development flow. The disadvantage is - every Vue Input Component needs to be put in a wrapper at the least to correspond to the protocol. So, both approaches have their positives and negatives. One wild thought that occurred to me is - if we are using a Vue UI library like shadcn etc. - style the basic input components using styles that match shadcn using CSS - and - then build forms using Phoenix LiveView itself for Input Components - and - use Vue Components for everything else.

I was just thinking aloud for anyone following thread. If it is not relevant please feel free to delete.

vheathen commented 5 months ago

Btw, I'm playing with radix-vue/shadcn-vue and it seems to work with default setup (tailwind based), at least, Button and Accordion work. Now I'm going to make it work with UnoCSS and get rid of tailwind.

OmarGoubail commented 5 months ago

@Valian First thanks for an a amazing library!

Second, I was wondering if we can make phoenix and liveVue work in an ionic mobile app, I've been researching for a couple of days, but so far have not found anything similar.

Phoenix isn't the best idea for a mobile app, but it's a great fit for a web app I am building, that needs a mobile port.

Valian commented 5 months ago

@OmarGoubail It's an interesting challenge.

In the past I used Capacitor to convert Vue.js app into mobile one. It worked really well, but it has an assumption all the assets are available in a single directory (index.html, scripts, images etc) and routing is handled by the frontend.

LiveVue is designed to be used within LiveView, so it's a normal server send HTML than later gets interactive thanks to Vue and a persistent websocket connection.

I don't have a solution to your problem, but maybe instead of using LiveVue you might want to look into eg. https://hexdocs.pm/live_state/readme.html and try to incorporate it into ionic mobile app? It could let you use a familiar server-state approach and keep your existing framework? Another option is to use LiveView Native, but I'm not sure how production-ready it is.

OmarGoubail commented 5 months ago

@Valian I appreciate the reply, I have not found a single resource or managed to get it working till now, I think I will just have to use a separate front end framework.

Thank you for pointing me to live state, it's an interesting project, will definitely explore it, and I will definitely keep an eye on LiveNative they don't support android just yet, but the project is very exciting.

Valian commented 5 months ago

For anyone interested, I've added a WIP example_project to the repository. You should be able to run it as any other phoenix project.

Once it will be a bit more polished I'll deploy it somewhere 😉 but for now can be used as an easy way to play with the library and test changes.

Screenshot 2024-06-13 at 01 04 07 image
lalabuy948 commented 3 months ago

Would be really great to see how to setup https://vuetifyjs.com for example.

Valian commented 3 months ago

Well it should work out of the box :)

Install it, import and use in Vue components. Nevertheless, might be good to include it in the example project. Would be amazing if you could try to contribute a PR @lalabuy948 !

lalabuy948 commented 3 months ago

Hi @Valian I created repo since it's yet not working I didn't make a proper PR to yours. It's fresh app with live_vue.

At the moment I got stuck with vuetify config, there is an issue with loading css properly on vite side. Since I am backend guy those configs for me way over complicated 😅, if someone could take a look would much more appreciated. Once it will be finalised I will submit proper PR.

Essentially I tried to use vite-plugin-vuetify as vuetify recommends, but no luck. Same as creating custom one, it's under assets/plugins/vuetify.js.

// vite.config.js
import vuetify from "vite-plugin-vuetify";
// import vuetify from "./plugins/vuetify";
Valian commented 3 months ago

@lalabuy948 I created a PR to your repo where I got it mostly working (SSR doesn't work yet, but hopefully will be soon). See more here https://github.com/lalabuy948/LiveVueVuetifyjs/pull/1

lalabuy948 commented 3 months ago

Added PR