Drab: remote controlled frontend framework for Phoenix

Are there any plans for standalone components, like VueJS components?

VueJS components are very useful for complex input widgets (like a datepicker or a location from a map), because the components can encapsulate their (potentially complex) HTML and Javascript and only communicate with the outside with a limited interface (usually an event and an event handler).

This allows you to automatically bind the value of the widget (the date picked by the user, for example) to the Drab store and vice versa.

I know the whole idea of Drab is to avoid javascript, but sometimes itā€™s unavoidable, and if you can write it (or reuse what someone else has written) once and reuse it later without caring about whether itā€™s javascript or not itā€™s great.

Thereā€™s been talk of a commander-per-element kind of model too.

Drab is designed to be modular, so making support for additional js libraries is relatively easy. There is a Drab.Module behaviour, as well as each module has corresponding JS templates.

But, as I explained in this thread before, I am not planning to do any more JS libraries integration, myself, in the near future.

  def validate_subscription_email(socket, sender) do
    email = sender.params["email_subscribe"]
    case Regex.run(~r/(\w+)@([\w.]+)/, email) do
      nil -> set_attr socket, "#subscribe_button", disabled: true
      [email, _username, _host] ->
        set_attr socket, "#subscribe_button", disabled: false
        set_prop socket, "#good", innerHTML: email
    end
  end

is there any way to unset_attr for self described attribute like ā€˜disabledā€™?
In this case, setting a value to false will not work.

I think Iā€™ve got my solution using set_prop instead
set_prop socket, "#subscribe_button", disabled: false

1 Like

Nope, there is no equivalent to JS removeAttribute() method. If you think it should be done, please create a change proposal (or PR on Drab.Element!) in githubā€™s issues.

1 Like

Iā€™ve explained myself badly :slight_smile:

I wasnā€™t talking about integrating with VueJS. That was just an example. I was aking about components, implemented in pure elixir plus some javascript that would act as VueJS components (encapsulate state and provide a limited interface for state manipulation)

Sorry I wasnā€™t clear

I see.
So the closest idea is, how @OvermindDL1 already mentioned, commander-per-element. There is a plan to do it in the next big release.

Please check his post here.

Should drab work with phoenix 1.3? IĀ“ve set up a new basic app and followed the steps in Usage. But i get Drab is unable to find a partial main when using
> poke socket, welcome_text: "This page has been drabbed

The following works:

set_prop socket, "div.jumbotron p.lead", 
  innerHTML: "Please visit <a href='https://tg.pl/drab'>Drab</a> page for more"

Yes, Phoenix 1.3 is supported.

Please make an issue on github. Add versions, code samples, steps to reproduce the issue, environment (umbrella, heroku, linux, etc)

Please also check if the example project works on your environment.

My mistake. Forgot to rename the template name to drab at the end.

Question: Is there a way without renaming the templates? IĀ“m not sure i like it.

Hi! First of all, would like to say that Drab is really wonderful lib. Iā€™v found an issue with using drab with tableā€™s. Drab-ampere span tag inserted not inside, but before table tag, so when action to update(poke) the table contents is fired - it puts the result just as a text strings before the table where it should be.

For example: iā€™m using such construction in drab template:

table
%= for user <- @users do %
   tr
     td<%= user %>/td
   /tr
% end %
/table

So, after action to change list fired it results that old data (inside table tag is still alive), and new data is printed as plain text (without tags) before table tag in drab-ampere spanā€¦

Help, plz.

Hi @fastel,
this is a known issue, Drab.Live does not work correctly with tables.

https://github.com/grych/drab/issues/50

It will be fixed in 0.6.0

For note, the reason it does not work is Drab tags elements differently, and <table>'s in the HTML5 spec are still broken as hell and only accept a very restricted set of elements within them, else it bumps them to before the table.

1 Like

On on hand, HTML5 doesnā€™t need any more chaos. On the other hand, an element you can put anywhere (like the <span> tag) is very useful in situations just like theseā€¦

Welcome, my fellow alchemists! A couple of news from the Drabā€™s world.

The next future version, v0.6.0, is focused on Drab.Live (living assigns). Because of some bugs and limits, I decided to completely refactor it and for now, it looks really promiscuous (for now ;)).


First at all, Drab will not sourround <span> over expressions anymore, as it caused troubles in, for example, tables. Instead of this, it searches for the parent tag, injects some unique ID there and saves the innerHTML of this tag, in the cache, as a pattern.

Thus, if you have a template

<p>My name is Bond, <%= @first_name%>, Bond</p>

During the compile time, Drabā€™s engine injects some ID to <p>

<p drab-ampere=ovishfvoaidhfv>My name is Bond, <%= @first_name%>, Bond</p>

and saves the pattern to the internal DETS cache (it is a kind of the shadow html of the partial)

{"ovishfvoaidhfv", "My name is Bond, {{{{@drab-expr:fivdosifvoidfj}}}}, Bond"}

Thanks to this, when updating the assign @first_name, Drab re-renders the innerHTML of that <p> tag, injecting the new value of rendered expression to the pattern.


Second at all, Drab is going to be smarter while updating assigns. Consider the following:

<%= function(@assign1) do %>
    Some text, and other assign: <%= assign2 %>
<% end %>

In 0.5, updating @assign2 re-renders the whole expression. This is because @assign2 is one of the arguments of function/1, which is called as function(@assign, do: {:safe, ...@assign2...}).

That issue was found when having the living assign inside the form_for function. Updating the assign inside the form caused of re-render the whole stuff, including userā€™s input.


Third at all, the new code is smaller and faster (more floki, less regexp!). Iā€™ve learned a lot while doing this.


API change proposal: HTML safe updates

Now all the updates in drab are raw. Doing

poke socket, text: "<b>bolded</b>"

or

insert_html(socket, "div", :beforebegin, "<b>bolded</b>")

updates the DOM without escaping the html. Of course there is an easy way to avoid this:

import Phoenix.HTML
insert_html(socket, "div", :beforebegin, ~E/"<b>MORE</b>"/)

but in my opinion it is not consistent with Phoenix, which uses safe htmls by default, enforcing developer to mark them as raw in case.

Shall I change Drab to render SAFE by default?

This must be changed in all innerHTTML updating functions. This will require to update all the existing applications.

But since Drab is the only beta 0.5 version, such change could be done without a big pain (sorry!).

Drabā€™s philosophy is to be as much compatible with Phoenix as it is possible. This is why I am quite convinced to make an effort to do it.

Please comment it out.

7 Likes

Iā€™m definitely for assuming that input is not safe by default. Especially with functions like poke/2 it will be common to interpolate in user or database strings. We can always wrap our own strings with ~E/.../ or whatever to send html instead of text.

1 Like

I understand this isnā€™t a VDOM yet. I used to think that VDOMs were mysterious beasts, with lots of quircks and hard-to-do optimizations. However, while doing some research to write a possible Drab competitor (not even out of the drawing board lol) I came upon this JS library: https://github.com/snabbdom/snabbdom. It claims to implement a VDOM in javascript in 200 lines of code, plus some utilities. I havenā€™t looked at the source yet, but you might be interested, if youā€™re ever going to have a VDOM on the server.

I was experimenting with a tiny VDOM dsl on my recent bard experiment. It is not intended to be a Darb competitor per se, but just another approach at commanding a front-end application from Elixir. In my case my primary interest was to create react-native applications with it (which canā€™t rely on Darb live magic).

I havent had much free time to continue my experiment, but so far this is what Iā€™ve come up regarding bardā€™s design:

  1. Bard is a tiny library which letā€™s you define Elixir components (render functions that take a props map and return a vdom ast) and pushes them as needed via a websocket to the client.
  2. The Bard.js client is very tiny, it basically just creates react components from what the server sends. It currently relies on xstream as Bardā€™s pokable bits are just streams of values and recompose for turning such streams into renderable components.
  3. A Bard application can define ā€œnamespacedā€ components so for example, you can define a universal ā€œButtonā€ implemented in Elixir, but that is actually rendered with eg native-baseā€™s Button or react-semanticā€™s Button. So in theory you would be able to reuse your elixir components on both platforms.

Iā€™ll try to update or create an split thread when a bard POC is available for you to play. Also I guess Iā€™ll do a bit of refactor on the code I currently have.

2 Likes

@vic, this is a nice one.
I have it on my mind that I need to make components like this in Drab. Somehow, some day :slight_smile:

In Drab, for now, there is no need to create any vdom.