Maybe it would be good to report it to plug team? They may want to check if you have a cipher before trying to use it, and change it, or at least give a better error message.
Good idea. I submitted it. Thanks again.
Hello,
I adopted Drab recently, and iām facing a problem (surely some kind of noob problem though):
How can I test commanders?
I used to test all my controllers, and have some controller-related code untested made me feel uncomfortable.
I suppose it is close to channel test (and I only work with channel when I go through Phoenix bookā¦) but I donāt have any clue what to send to socket.
Or maybe, this should be tested via headless browser like any front end?
Tanks your your help!
There is no support for tests in Drab yet. It must be done in the way to the stable production version, but I still have no idea how could it look like
In internal drab tests, I am doing a full integration tests with hound and chromedriver. Unfortunately, the headless browser (phantomjs) does not want to work with Drab.
Hello Grych
I get a protocol Enumerable not implemented for nil
while using the Drab.run_handler method on Phx 1.3. I try to reproduce your example but get the error bellow. Any suggestion ?
<a class="waves-effect waves-light btn grey" onclick="Drab.run_handler('click', 'clicked', {click: 'clickety-click'});">
def clicked(socket, payload) do
socket |> Drab.Browser.console("You've sent me this: #{payload |> inspect}")
end
Transport: Phoenix.Transports.WebSocket
Parameters: %{"event" => "click", "event_handler_function" => "clicked", "payload" => %{"click" => "clickety-click"}, "reply_to" => "8a0affa7-1dd2-4e9d-95d8-9441897eb747"}
[error] Drab Handler failed with the following exception:
** (Protocol.UndefinedError) protocol Enumerable not implemented for nil. This protocol is implemented for: ...
I also receive this after closing the alert box :
[debug] INCOMING "execjs" on "__drab:same_path:/packs/1" to Drab.Channel
Transport: Phoenix.Transports.WebSocket
Parameters: %{"ok" =>["SFMyNTY.g3QAAAACZAAEZGF0YWgCZ2QADW5vbm9kZUBub2hvc3QAAA6mAAAAAAByAANkAA1ub25vZGVAbm9ob3N0AAAAj__RLAABgxyz62QABnNpZ25lZG4GAAPPMjNfAQ.2KVP7lyR4fOT_q4tZNBGdCA3dzZFZX5fXWMV_gc-Xjg", nil]}
Itās a bug. Thanks a lot for reporting!
Could you please create an issue on github, so we will have all bugfixes in the one place.
the issue is done !
thanks
v0.6.0-pre.1 finally arrived
Girls and boys,
after a long time, I am happy with the brand new Drab EEx engine, completely rewritten, shorter, faster and way more clever. It is finally not pushing <span>
in every corner of your html template, but it is using the existing tags.
Please help me with testing this one.
- add
{:drab, "~> 0.6.0-pre.1"}
to your deps inmix.exs
mix deps.get
-
rm priv/hashes_expressions.drab.cache.*
(itās been renamed todrab.live.cache
) -
mix clean
to recompile templates
Changes
The main change in the new template engine is that now it is not injecting <span>
everywhere. Now, it parses the html and tries to find the sourrounding tag and mark it with the attribute called drab-ampere
. The attribute value is a hash of the previous buffer and the expression, so it is considered unique.
Consider the template, with initial value of 1
(given in render function in the Controller, as usual):
<p>Chapter <%= @chapter_no %>.</p>
which renders to:
<p drab-ampere="someid">Chapter 1.</p>
This drab-ampere
attribute is injected automatically by Drab.Live.EExEngine
. Updating the @chapter_no
assign in the Drab Commander, by using poke/2
:
chapter = peek(socket, :chapter_no) # get the current value of `@chapter_no`
poke(socket, chapter_no: chapter + 1) # push the new value to the browser
will change the innerHTML
of the <p drab-ampere="someid">
to āChapter 2.ā by executing the following JS on the browser:
document.querySelector('[drab-ampere=someid]').innerHTML = "Chapter 2."
This is possible because during the compile phase, Drab stores the drab-ampere
and the corresponding pattern in the cache DETS file (located in priv/drab.live.cache
).
Sometimes it must add a <span>
In case, when Drab canāt find the parent tag, it injects <span>
in the generated html. For example, template like:
Chapter <%= @chapter_no %>.
renders to:
Chapter <span drab-ampere="someid">1</span>.
Avoiding using Drab (nodrab
option)
If there is no need to use Drab with some expression, you may mark it with nodrab/1
function. Such expressions will be treated as a ānormalā Phoenix expressions and will not be updatable by poke/2
.
<p>Chapter <%= nodrab(@chapter_no) %>.</p>
In the future (Elixir 1.6 I suppose), the nodrab
keyword will be replaced by a special EEx mark /
(expression will look like <%/ @chapter_no %>
).
The @conn
case
The @conn
assign is often used in Phoenix templates. Drab considers it read-only, you can not update it with poke/2
. And, because it is often quite hudge, may significantly increase the number of data sent to the browser. This is why Drab treats all expressions with only one assign, which happen to be @conn
, as a nodrab
assign.
I am working on a small experimental app that incorporates geolocation into an interactive chat experience. I want the LiveCommander to not only have lat/lon available in @lat/@lon variables, but also be able to respond to GeoLocation changes and update the page with fresh info that is location specific. I could do some JS code that somehow combined Geolocation.watchPosition()) with sending info to a phoenix channel to update the phoenix session, but am wondering if there is better way to incorporate geo position changes with LiveCommander, some sort of drab-event that could be created, or some other way to do it that fits the drab model better.
Drab does not have any geolocation helpers (maybe it is a good idea to add it in the Drab.Browser?).
To archive your goal, you may call javascript function (from Geowatchin.watchPosition()) Drab.run_handler()
to execute function in the commander module directly from the browser.
Iām surprised pheonix doesnāt have something like this built-in. It appears to be the perfect fit for the strengths of Elixir and Pheonix while not being the same JavaScript SPA hell that is currently out there. Itās exhausting.
With polish, Drab could be a viable way to build rich applications on the server side that appear to be client side in the browser.
I think this approach should be a default option in Pheonix!
Great work btw, @grych
Itās not thaaaat easy to implement āsomething like drabā. Once you start going that way, you start hitting some problems, some of them serious.
With English too xD A great thing about Drab is that internationalization is dead-simple. Compare to doing something like that with VueJSā¦
It depends on what kind of application youāre talking about. The latency of going client-to-server is really high with Drab. This is obvious to most people who try Drabās demo webpage from outside Europe. Such applications wonāt appear client-side to anyone who pays attention to latency. Drab is good, but itās not a replacement for client-side JS.
That said, even though it might not be the best experience for the end-user, the ability to use Elixir for everything is great.
OK, perhaps my comment was too simple. Phoenix has a unique set of features that make stuff like this possible and potentially performant for broad use cases.
Having a client -> server -> client communication path can be slow but it isnāt any slower than traditional server rendered web apps. Itās even faster using web sockets. Is Drab doing something different? Perhaps I misunderstood how it works. Anything with a sub 500ms response time is certainly borderline reasonable for UI with button click actions.
Mostly, I long for the day we donāt have to play the JavaScript framework game to push a round peg into the square whole that is the HTML web.
Oh, the English language! It lacks a bit of polish, but maybe all languages do, even Polish.
Phoenix is a great foundation for the projects like Drab.
But building Drab into Phoenix could make it overcomplicated. It is better to keep it as an external library, maybe with the better, automatized installer.
Thanks for the pointer.
from inspecting the code it looks like drab run_handler() looks straightforward, with three required parameters, with the handler_function having arity 2. But I am getting an error (see simple example below). Probably something simple so thought I would ask.
from mix.exs
{:drab, ā~> 0.5.6ā},
commander handler example:
def test_handler(_first, _second) do
IO.puts(āhello form test_handlerā)
end
from browser console:
Drab.run_handler(ātest_event_nameā, ātest_handlerā, {āanswerā: 42})
The error from server:
ERROR:
[error] Drab Handler failed with the following exception:
** (Protocol.UndefinedError) protocol Enumerable not implemented for nil. This protocol is implemented for: DBConnection.PrepareStream, DBConnection.Stream, Date.Range, Ecto.Adapters.SQL.Stream, File.Stream, Function, GenEvent.Stream, HashDict, HashSet, IO.Stream, List, Map, MapSet, Postgrex.Stream, Range, Stream, Timex.Interval
(elixir) lib/enum.ex:1: Enumerable.impl_for!/1
(elixir) lib/enum.ex:116: Enumerable.reduce/3
(elixir) lib/enum.ex:1832: Enum.reduce/3
(drab) lib/drab/core.ex:173: Drab.Core.normalize_params/1
(drab) lib/drab/core.ex:168: Drab.Core.transform_payload/2
(elixir) lib/enum.ex:1811: Enum."-reduce/3-lists^foldl/2-0-"/3
(drab) lib/drab.ex:243: anonymous fn/6 in Drab.handle_event/6
Any advice appreciated. thanks.
Hey @piex,
it is a known bug (https://github.com/grych/drab/issues/62).
It is solved now, you may try master from github, or wait for v0.6.0 (I hope this week!).
BTW in 0.6.0 run_handler becomes exec_elixir
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.
These are awesome. ^.^
Sorry, this might be off topic. I remember reading somewhere that someone used Drab to build a āload moreā UI or an infinite scroll, but I canāt remember where the thread was. Does anyone know of something similar or have a link to such a thread/blog post?
Not off hand but Iād think it would be pretty trivial to write, itās just an onscroll handler when a condition is met.