grych

grych

Creator of Drab

Drab: remote controlled frontend framework for Phoenix

Hi folks,
Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixir (here). Finally, first beta has been released!

And of course it is available as a hex package.


In the meantime I’ve changed the API, so it is - I believe - easier to remember. If you think about webpage as a database which contains DOM objects, it is natural to query:

  • select(:text, from: "p:first") to get text of the first paragraph or
  • select(attr: :href, from: "a") for all links in the document,
    update some object(s) with
  • update(css: :border, set: "3px solid red", on: this(sender)),
    add new nodes or attributes
  • insert("<b>IMPORTANT</b>", before: "p:first"),
    or remove something with
  • delete(class: "btn", from: "#the_button"),
    and more, like synchronous modal which - launched on the server side - waits for user input.

Any suggestions, criticism, ideas welcome!


Grych

639 49522 488

Most Liked

grych

grych

Creator of Drab

Girls and boys,
I’ve just released v0.3.0 of Drab - the addition to Phoenix to live control of browser’s User Interface from Elixir, from the server-side. This version comes with few API changes, new features and many bug fixes.

Waiters

First at all, 0.3.0 introduces Drab Waiter: the functionality which allows you to wait for the user input. That could be useful, when - for example - you made a changes in the database, and want to ask the user what to do: rollback or commit? (forgive the pseudocode, it’s just an example).

Database.sql(conn, "UPDATE users SET password='';")
waiter(socket) do
  on "#commit_button", "click", fn (sender) ->
    Database.commit(conn)
  end
  on "#rollback_button", "click", fn (sender) ->
    Database.rollback(conn)
  end
  on_timeout 5000, fn -> 
    Database.rollback(conn)
  end
end

This function will stop processing until user press one of the buttons - or until timeout.

Drab.Query.select API changed

The second major change is the Drab.Query.select API. Before, select(:html, from: "p") returned a list of all htmls found on this selector. Now, there are two versions of each jQuery method: singular and plural. Singular behaves exactly like jQuery, returning the value of the first found DOM object. It is an exact equevalent of running $("p").html() in the browser.

Drab 0.3.0 introduces plural version of methods. They return all found DOM object values. In the same example, you may use select(:htmls, from: "p") to get all “p” elements from the DOM tree and return the value of jQuery .html() method, executed on every single object.

Plural versions of the methods return a Map of %{name|id|__undefined_[number] => value}. Keys of the map are created of DOM object’s attributes: first name or, if not found: id or, if both id and name not found, system will construct a special key with incremental number. Let’s check it on example:

### <span name="first_span" class="qs_2 small-border">First span with class qs_2</span>
### <span id="second_span" class="qs_2 small-border">Second span with class qs_2</span>
socket |> select(:html, from: ".qs_2")
# "First span with class qs_2"
socket |> select(:htmls, from: ".qs_2")
# %{"first_span" => "First span with class qs_2", "second_span" => "Second span with class qs_2"}

Callbacks

Drab is now equipped with callbacks to be used in the Commander: before_handler and after_handler. Usage is quite obvious, it is only worth to mention that before_handler must return truly value (everything except false and nil). Otherwise, the proceeding event handler will never be processed (handy for checking the authorization etc). By the other hand, after_handler is launched every time after handler function finish, and it receives the handler function return value as an argument.

More

Please check the release notes for more details.

Plans for the future

Unfortunately, the TODO list is rather growing than shrinking. In the following weeks I would like to concentrate on building the test environment. After this, I’d like to completely refactor Drab.Query and Drab.Template.

Feedback

Any feedback is strongly welcome! Most of the suggestion of users in this forum are included in this release of Drab.


One more thing: only Elixir/Phoenix can make it possible!

OvermindDL1

OvermindDL1

StartCom is utterly horrible horrible horrible, never use them!

For SSL there is no reason not to be using Let’s Encrypt. :slight_smile:

grych

grych

Creator of Drab

Girls and boys,
there is a new release of Drab: 0.3.2. Finally, I’ve made tests for all Drab features. Because of how Drab works, mosts of the tests must be end-to-end (integration). I’ve decided to use Hound and chromedriver.

In addition, there is a build-in Phoenix server. Now to play with Drab in IEx, you don’t need to create your own Phoenix application and add drab to it, anymore. You can just clone the github repo and run the server on it:

git clone git@github.com:grych/drab.git
cd drab
mix deps.get
npm install && node_modules/brunch/bin/brunch build
iex -S mix phoenix.server

Open the browser, navigate to http://localhost:4000 and follow the instruction in IEX:

import Drab.Core; import Drab.Query; import Drab.Modal; import Drab.Waiter
socket = GenServer.call(pid("0.xxxxx.0"), :get_socket)

Now you can remote control the browser from IEx:

iex(5)> socket |> alert("Title", "WOW!")                                          
{:ok, %{}}
iex(6)> socket |> select(:text, from: "h3")
"Drab Tests"
iex(7)> socket |> update(:text, set: "It is set from IEx, wow!", on: "h3")  
%Phoenix.Socket{assigns: %{__action: :index, .........

And, last but not least, Drab has the own logo :slight_smile:

grych

grych

Creator of Drab

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.

grych

grych

Creator of Drab

Drab v0.6.0 released

Please read release notes.

In this release Drab.Live is completely redesigned. It is not pushing <span> in every corner of your html, but it tries to find the parent tag and link to it.

Also, Shared Commanders are introduced. This allows you to create component-like modules.

Where Next?

Popular in Libraries Top

zoltanszogyenyi
Hey everyone :wave: Excited to join this forum - I am one of the founders and current project maintainers of a popular and open-source U...
New
nikokozak
Hello all, I’ve been working on Svonix - a library for quickly integrating Svelte components into Phoenix views. It’s a much-needed succ...
New
kelvinst
Hey everyone! Well, we made this lib a while ago and now we decided to finally go out and public with it! It’s a tool for creating and m...
New
archan937
It is a well-know topic within the Elixir community: “To mock or not to mock? :)” Every alchemist probably has his / her own opinion con...
New
mindok
What is ContEx? A pure Elixir server-side data plotting/charting library outputting SVG. It has nice barcharts in particular and works g...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New
ericlathrop
I built a silly site for Halloween that uses Phoenix Channels on the backend, and React on the frontend. I had many problems integrating ...
New
bryanjos
Hi, I just published version 0.23.0 of Elixirscript. Most of the changes are around JavaScript interop now that Elixirscript uses the ...
New
anshuman23
Hello all, I have been working on my proposed project called Tensorflex as part of Google Summer of Code 2018.. Tensorflex can be used f...
New
ostinelli
Let’s write a database! Well not really, but I think it’s a little sad that there doesn’t seem to be a simple in-memory distributed KV da...
New

Other popular topics Top

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 30048 115
New
shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New

Sub Categories:

We're in Beta

About us Mission Statement