Drab: remote controlled frontend framework for Phoenix



Agreed, we can, using sigil_E is not a big deal. But using raw is not a big deal as well :slight_smile:

Drab targets not only experienced users. Imagine you just learned phoenix, made your application with <input>, and whatever user typed into it, is shown properly escaped. Great! You don’t have to deal with escaping, making your application safer.
Then you are trying Drab. Create an <input> and poke (or insert_html) its value. What do you expect? For me, more natural for beginners would be escaping it by default, just like Phoenix does.

If you need to manually escape every string you push to the browser, you may just simply forgot to do it. I made such mistake in the Drab’s demo page! - remember, you’ve found it, thanks a lot again :slight_smile:

Safe-by-default should be implemented only on the functions, which deal with node’s innerHTML, like Element.insert_html, Query.insert(:html) and poke (but only when updating the html, not attribute or property). Low level functions, like exec_js should stay unescaped, obviously.

Looks like this answer convinced myself to safe-by-default idea :slight_smile:


Maybe I’m misunderstanding you, but are you using a VDOM both on the server and the client? Why not sending state through the network and allow the client to resolve the state into the UI using a VDOM?

This is the approach I intend to take: run VueJS on the client and sent state. The Elixir code doesn’t deal with the DOM at all.


By the other hand, we mostly use Drab to send htmls, not safe texts.


I think insert_html should be unsafe by default. You’re sending HTML, not text.


My experiment was trying to remove as much JS from the client as possible, and just like Drab, try to send the entire view from the server to the client. So Bard is actually creating a stream of vdom AST that is sent to the server (the complete view at first and updates as they are produced over time), but since the View is just a function of the State, we could very easily send any portion of the state itself instead of the vdom produced by it.

Actually I have been thinking about something like what you mention for a few weeks but again havent had time to actually refactor Bard for it. This change would let you send as much as it makes sense for you from the server, and write as much JS on the client as you want. For example, you could send the complete vdom AST for react just to render (my original plan) or just send the props and use normal React components on the client, or send any bits of state for clients that can handle Observable streams, say polymer/angular/vue and let those libs mess with DOM updates.


(this might be veering a little offtopic, but I’ll answer here anyway, because it’s related to Drab, and might provide some ideas for someone who wants to write a Drab client that interfaces with different Javascript libraries)

Ok, my experiment is the philosophical opposite of yours: trying to remove as much DOM from the server as possible :smile: Javascript is already very good at manipulating the DOM. This means something like React handles the DOM on the client and the server sends props.

Actually I’m using VueJS instead of React because:

  1. I’m more familiar with VueJS
  2. By default, VueJS comes with a powerful template language which is natural to use and parse from Elixir (so that I can customize the templates a little with extra event listeners and other goods to make it work). Unlike React, writing VueJS templates doesn’t feel like writing Javascript. It feels a lot like writing EEx, which means that using it mixed with Elixir function calls doesn’t feel out of place.
  3. I really like the way VueJS uses events with special syntax sugar for databinding, and how that makes it useful to effortlessly create complex input components which sync with your model (like datepickers, map location pickers, etc.)

Until the Weex project is more well developed, VueJS is a big No for your use case, which require React Native, but if you’re developing for the web, it’s quite attractive.

This approach of sending pure values over the network might be useful too if you want to be renderer-agnostic. I could have a Qt-based client, for example (I have an unpublished React-like library for PyQt that would make a pretty good fit with this approach).

EDIT: I don’t think of these experiments as competitors in the capitalistic sense of trying to fight over user mindshare. I think that there is space for all these approaches, and they all can feed on each other for ideas or implementation details.


I’ve got noticed that author is making an effort to solve problems including this. (from post#216)
I just choose to modifying my css, and I will be waiting for it! :slight_smile:

Changing extension from .html.eex to .html.drab breaks some stylings
by automatically adding wrapper to each elements

Is there anyway to avoid this problem?

<ul class="tab tab-block">
    <span drab-ampere="gi3dqnrrg4yti">
        <li drab-ampere="he4tmmztha4ds" id="hero_slide_tab_0" class="tab-item active active-tab">
            <a drab-ampere="giztiobrgq4q" onclick="onSelectHeroSlide(0)" style="cursor: pointer;">슬라이드-0</a>


I think it should be safe regardless, you may want to put user input into the html you are sending, and remembering to escape their input will be forgotten. At least with it being safe by default you will immediately see it escaped and thus can then just put ~E in front of it or so. ^.^

The wrapper is gone in the version 6 and above. :slight_smile:


Anybody knows how to deal with special charactors?

Using special characters in form, and input causes error.

for example,
(1) % causes invalid www-form error (encoded form of ascii like %25 is okay)
(2) & makes my input params to be divided.

and because of (2), when handling wyswig editor’s innerHTML to be escaped also causes problems.

my form is like below

  <div class="form-group">
    <label for="accordion_body" class="form-label">body</label>
    <textarea id="accordion_body" name="accordion_body"><%= @accordion.body %></textarea>
  <input id="widget_name_of_accordions" name="widget_name" value="accordion" class="hidden"/>
  <button class="btn btn-primary hidden" drab-click="save_accordion">Save</button>

if i type input like this.
accordion_body: Default Text will be like&lt ;THIS&gt ;

and i inspect sender params,

%{"accordion_body" => "Default Text will be like",
  "lt;THIS" => nil, "gt;" => nil,
  "widget_name" => "accordion"}


Thanks for sharing the problem, it is obviously a bug while parsing params and need to be fixed.
Could you please create an issue on github so it won’t be forgotten.

Thanks again!


Using multiple drab partials in one template is not binding assigns properly.

How can I handle this?

[error] Process #PID<0.3488.0> raised an exception
** (ArgumentError) Assign @services not found in Drab EEx template

Please make sure all proper assigns have been set. If this
is a child template, ensure assigns are given explicitly by
the parent template as they are not automatically forwarded.

Available assigns:
[:hero_slides, :page]

I ensured to put :hero_slides and :sections to assigns.
When I change the order of each partials, then I got

Available assigns:
[:services, :page]

some of my codes are like below.

# edit.html.eex

<%= for widget <- @page.widgets do %>
    <%= if "hero_slides" == widget do %>
        <%= render "hero-slides-edit.html", assigns %>
    <% end %>
    <%= if "sections" == widget do %>
        <%= render "sections-edit.html", assigns %>
    <% end %>
<% end %>


# hero_slides.html.drab

<form id="hero_slides_edit">
    <%= for {hero_slide, idx} <- Enum.with_index(@hero_slides) do %>
    <% end %>
    <button class="btn btn-primary" drab-click="save_all_in_widget">Save</button>


# sections.html.drab

<form id="sections_edit">
    <%= for {section, idx} <- Enum.with_index(@sections) do %>
    <% end %>
    <button class="btn btn-primary" drab-click="save_all_in_widget">Save</button>


Are you updating the assigns with the partial given?

poke socket, "hero_slides.html", hero_slides: ...



Thanks, I’ve totally forgot to include partial template when I call poke.


I am thinking about changing this behavior, so when you not specify the template name, it changes it in all.


Did you manage to do that? Having to return to eex templates would be a real show-stopper for me.


No, sorry, I didn’t try. I’ve started aorking on my own thing instead…


Drab looks really interesting and I am hoping it will be a fit for an experimental project I m working on. I am trying to get drab working but running into an error “Erlang error: :notsup” when executing page render DrabExample.PageView.“index.html”/1

After digging a bit it seems to be coming from lib/plug/crypto/message_encryptor.ex. The call that fails is :crypto.block_encrypt(:aes_gcm, key, iv, {aad, plain_text})

This is likely an issue with my environment (OS X Sierra 10.12.6), OpenSSL 1.0.2l 25 May 2017

iex> :crypto.supports()
[hashs: [:sha, :sha224, :sha256, :sha384, :sha512, :md4, :md5, :ripemd160],
ciphers: [:des3_cbc, :des_ede3, :des3_cbf, :des3_cfb, :aes_cbc, :aes_cbc128,
:aes_cfb8, :aes_cfb128, :aes_cbc256, :aes_ctr, :aes_ecb, :aes_ige256,
:des_cbc, :des_cfb, :des_ecb, :blowfish_cbc, :blowfish_cfb64, :blowfish_ofb64,
:blowfish_ecb, :rc2_cbc, :rc4],
public_keys: [:rsa, :dss, :dh, :ec_gf2m, :ecdsa, :ecdh, :srp]]

iex> :ssl.versions
[ssl_app: ‘8.1.1’, supported: [:“tlsv1.2”],
available: [:“tlsv1.2”, :“tlsv1.1”, :tlsv1, :sslv3]]

My past Phoenix projects have not hit any issue, but may not have exercised this particular crypto path. Asking here in case anyone has had this issue or has an idea how to resolve it.

Any advice is greatly appreciated.


That does not seem like a drab error, rather that sounds like some application is not started that should have been started. What is your entire mix.exs file and your projects application file?


Hey @piex,
Yes, Drab is using Plug’s MessageVerifier to sign the tokens.
And yes, it looks like your environment issue. You are missing :aes_gcm.

On my system it’s there:

ex(3)> :crypto.supports()[:ciphers]
[:des3_cbc, :des_ede3, :des3_cbf, :des3_cfb, :aes_cbc, :aes_cbc128, :aes_cfb8,
 :aes_cfb128, :aes_cbc256, :aes_ctr, :aes_ecb, :aes_gcm, :aes_ige256, :des_cbc,
 :des_cfb, :des_ecb, :blowfish_cbc, :blowfish_cfb64, :blowfish_ofb64,
 :blowfish_ecb, :rc2_cbc, :rc4]

Not sure it is openssl version or erlang. I am using built-in openssl in macOS Sierra (OpenSSL 0.9.8zh 14 Jan 2016) and OpenSSL 1.0.1t 3 May 2016 on Debian.


Thanks grych, that confirmation was all I needed. I had been using the Erlang Solutions installer to manage switching between several Erlang versions but all of their builds seem to be missing :aes_gcm cipher. I switched to point to the brew installed version of Erlang and it includes :aes_gcm and now works. Thanks much.