Git Product home page Git Product logo

drab's Introduction

drab logo

hex.pm version hex.pm downloads API Docs Inline docs

See Demo Page for live demo and description.

Drab - Access the browser User Interface from the Server Side. No Javascript programming needed anymore!

Drab extends Phoenix Framework to "remote control" UI on the browser, live. The idea is to move all User Interface logic to the server-side, to eliminate Javascript and Ajax calls.

Teaser

  • Client side:
<div class="progress">
  <div class="progress-bar <%= @progress_bar_class %>" role="progressbar" @style.width=<%= "#{@bar_width}%" %>>
    <%= "#{@bar_width}%" %>
  </div>
</div>
<button class="btn btn-primary" drab-click="perform_long_process">
  <%= @long_process_button_text %>
</button>
  • Server side:
def perform_long_process(socket, _sender) do
  poke socket, progress_bar_class: "progress-bar-danger", long_process_button_text: "Processing..."

  steps = :rand.uniform(100)
  for i <- 1..steps do
    Process.sleep(:rand.uniform(500)) #simulate real work
    poke socket, bar_width: Float.round(i * 100 / steps, 2)
  end

  poke socket, progress_bar_class: "progress-bar-success", long_process_button_text: "Click me to restart"
end

Prerequisites

  1. Erlang ~> 19

  2. Elixir ~> 1.4

  3. Phoenix ~> 1.2

  4. Brunch

Installation

So far the process of the installation is rather manual. In the future it will be automated.

  1. Add drab to your list of dependencies in mix.exs in your Phoenix application and install it:
def deps do
  [{:drab, "~> 0.5"}]
end
$ mix deps.get
$ mix compile
  1. Initialize Drab client library by adding to the layout page (web/templates/layout/app.html.eex)
<%= Drab.Client.js(@conn) %>

just after the following line:

<script src="<%= static_path(@conn, "/js/app.js") %>"></script>
  1. Initialize Drab sockets by adding the following to web/channels/user_socket.ex:
use Drab.Socket
  1. Add Drab template engine to config.exs:
config :phoenix, :template_engines,
  drab: Drab.Live.Engine
  1. Add :drab to applications started by default in mix.exs:
def application do
  [mod: {MyApp, []},
   applications: [:phoenix, :phoenix_pubsub, :phoenix_html, :cowboy, :logger, :gettext, :drab]]
end
  1. To enable live reload on Drab pages, add .drab extension to live reload patterns in dev.exs:
config :my_app, MyApp.Endpoint,
  live_reload: [
    patterns: [
      ~r{priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$},
      ~r{priv/gettext/.*(po)$},
      ~r{web/views/.*(ex)$},
      ~r{web/templates/.*(eex|drab)$}
    ]
  ]

If you want to use Drab.Query (jQuery based module):

  1. Add jquery and boostrap to package.json:
"dependencies": {
  "jquery": ">= 3.1.1",
  "bootstrap": "~3.3.7"
}
  1. Add jQuery as a global at the end of brunch-config.js:
npm: {globals: {
  $: 'jquery',
  jQuery: 'jquery',
  bootstrap: 'bootstrap'
}}
  1. And install it:
$ npm install && node_modules/brunch/bin/brunch build 

Congratulations! You have installed Drab and you can proceed with your own commanders.

Usage

All the Drab functions (callbacks, event handlers) are placed in the module called Commander. Think about it as a controller for the live pages. Commanders should be placed in web/commanders directory.

To enable Drab on specific pages, you need to add the directive use Drab.Controller to your controller.

Remember the difference: controller renders the page, while commander works on the live page.

  1. Generate the page Commander. The commander name should correspond to the controller, so PageController should have PageCommander:
$ mix drab.gen.commander Page
* creating web/commanders/page_commander.ex

Add the following line to your Example.PageController:
    use Drab.Controller 
  1. As described in the previous task, add Drab.Controller to your page Controller (eg. web/controllers/page_controller.ex in the default app):
defmodule MyApp.PageController do
  use Example.Web, :controller
  use Drab.Controller 

  def index(conn, _params) do
    render conn, "index.html", welcome_text: "Welcome to Phoenix!"
  end
end    

Also add the @welcome_text assign to render/3 in index action, to be used in the future.

  1. Rename the template from web/templates/page/index.html.eex to index.html.drab

  2. Edit the template web/templates/page/index.html.drab and change the fixed welcome text to the assign:

<div class="jumbotron">
  <h2><%= @welcome_text %></h2>
  1. Edit the commander file web/commanders/page_commander.ex and add some real action - the onload callback, which fires when the browser connects to the Drab server:
defmodule DrabExample.PageCommander do
  use Drab.Commander

  onload :page_loaded

  # Drab Callbacks
  def page_loaded(socket) do
    poke socket, welcome_text: "This page has been drabbed"
    set_prop socket, "div.jumbotron p.lead", 
      innerHTML: "Please visit <a href='https://tg.pl/drab'>Drab</a> page for more"
  end
end

The poke/2 function updates the assign. The set_prop/3 updates any property of the DOM object. All is done live, without reloading the page.

  1. Run iex -S mix phoenix.server. Go to http://localhost:4000 to see the changed web page. Now you may play with this page live, directly from iex! Observe the instruction given when your browser connects to the page:
[debug] 
    Started Drab for same_path:/, handling events in DrabExample.PageCommander
    You may debug Drab functions in IEx by copy/paste the following:
import Drab.{Core, Element, Live}
socket = Drab.get_socket(pid("0.653.0"))

    Examples:
socket |> exec_js("alert('hello from IEx!')")
socket |> poke(count: 42)

As instructed, copy and paste those to lines, and check out yourself how could you remote control the displayed page:

iex> poke socket, welcome_text: "WOW, this is nice"
%Phoenix.Socket{...}

iex> query socket, "div.jumbotron h2", :innerText
{:ok,
 %{"[drab-id='425d4f73-9c14-4189-992b-41539377c9eb']" => %{"innerText" => "WOW, this is nice"}}}

iex> set_style socket, "div.jumbotron", backgroundColor: "red"
{:ok, 1}

The example above is available here

What now?

Visit Demo Page for a live demo and more description.

Visit Documentation page.

Getting help

There is a Drab's thread on elixirforum.com, please address questions there.

Tests and Sandbox

Since 0.3.2, Drab is equipped with its own Phoenix Server for automatic integration tests and for sandboxing and play with it.

Sandbox

  • clone Drab from github:
git clone [email protected]:grych/drab.git
cd drab
  • get deps and node modules:
mix deps.get
npm install && node_modules/brunch/bin/brunch build
  • start Phoenix with Drab:
iex -S mix phoenix.server
  • open the browser and navigate to http://localhost:4000

  • follow the instructions in IEx to play with Drab functions:

import Drab.{Core, Live, Element, Query, Waiter}
socket = Drab.get_socket(pid("0.784.0"))

iex> query socket, "h3", :innerText
{:ok,
 %{"#header" => %{"innerText" => "Drab Tests"},
   "#page_loaded_indicator" => %{"innerText" => "Page Loaded"}}}

iex> set_prop socket, "h3", innerText: "Updated from IEx"
{:ok, 2}

iex> exec_js socket, "alert('You do like alerts!')"
{:ok, nil}

Tests

Most of the Drab tests are integration (end-to-end) tests, thus they require automated browser. Drab uses chromedriver, which must be running while you run tests.

  • clone Drab from github:
git clone [email protected]:grych/drab.git
cd drab
  • get deps and node modules:
mix deps.get
npm install && node_modules/brunch/bin/brunch build
  • run chromedriver

  • run tests:

% mix test                 
Compiling 23 files (.ex)
........................................

Finished in 120.9 seconds
123 tests, 0 failures

Randomized with seed 934572

Contact

(c)2016-2017 Tomek "Grych" Gryszkiewicz, [email protected]

Illustrations by https://www.vecteezy.com

drab's People

Contributors

grych avatar jpinnix avatar overminddl1 avatar vic avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.