{"id":13552726,"url":"https://github.com/grych/drab","last_synced_at":"2025-10-21T18:53:19.602Z","repository":{"id":15210590,"uuid":"72545618","full_name":"grych/drab","owner":"grych","description":"Remote controlled frontend framework for Phoenix.","archived":false,"fork":false,"pushed_at":"2023-01-05T23:27:25.000Z","size":2632,"stargazers_count":869,"open_issues_count":32,"forks_count":43,"subscribers_count":37,"default_branch":"master","last_synced_at":"2024-04-14T07:59:58.262Z","etag":null,"topics":["dom","elixir","elixir-lang","elixir-phoenix","elixir-programming-language","live","liveview","phoenix","phoenix-channels","view","web-application","web-framework","websocket"],"latest_commit_sha":null,"homepage":"https://tg.pl/drab","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/grych.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-11-01T14:46:22.000Z","updated_at":"2024-04-07T09:20:52.000Z","dependencies_parsed_at":"2023-01-13T18:18:25.384Z","dependency_job_id":null,"html_url":"https://github.com/grych/drab","commit_stats":null,"previous_names":[],"tags_count":44,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grych%2Fdrab","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grych%2Fdrab/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grych%2Fdrab/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grych%2Fdrab/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/grych","download_url":"https://codeload.github.com/grych/drab/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246933421,"owners_count":20857049,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dom","elixir","elixir-lang","elixir-phoenix","elixir-programming-language","live","liveview","phoenix","phoenix-channels","view","web-application","web-framework","websocket"],"created_at":"2024-08-01T12:02:08.599Z","updated_at":"2025-10-21T18:53:14.572Z","avatar_url":"https://github.com/grych.png","language":"Elixir","readme":"[![drab logo](https://raw.githubusercontent.com/grych/drab-poc/master/web/static/assets/images/drab-banner.png)](https://tg.pl/drab)\n\n[![Build Status](https://travis-ci.org/grych/drab.svg?branch=master)](https://travis-ci.org/grych/drab)\n[![hex.pm version](https://img.shields.io/hexpm/v/drab.svg)](https://hex.pm/packages/drab)\n[![hex.pm downloads](https://img.shields.io/hexpm/dt/drab.svg?style=flat)](https://hex.pm/packages/drab)\n[![API Docs](https://img.shields.io/badge/api-docs-yellow.svg?style=flat)](https://hexdocs.pm/drab/)\n[![Inline docs](http://inch-ci.org/github/grych/drab.svg)](http://inch-ci.org/github/grych/drab)\n[![Ebert](https://ebertapp.io/github/grych/drab.svg)](https://ebertapp.io/github/grych/drab)\n\n### See [Demo Page](https://tg.pl/drab) for live demo and description.\n\nDrab - Access the browser User Interface from the Server Side. No Javascript programming needed anymore!\n\nDrab extends Phoenix Framework to \"remote control\" UI on the browser, live. The idea is to move all User Interface\nlogic to the server-side, to eliminate Javascript and Ajax calls.\n\n## Teaser\n\n* Client side:\n\n```html\n\u003cdiv class=\"progress\"\u003e\n  \u003cdiv class=\"progress-bar \u003c%= @progress_bar_class %\u003e\" role=\"progressbar\" @style.width=\u003c%= \"#{@bar_width}%\" %\u003e\u003e\n    \u003c%= \"#{@bar_width}%\" %\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cbutton class=\"btn btn-primary\" drab-click=\"perform_long_process\"\u003e\n  \u003c%= @long_process_button_text %\u003e\n\u003c/button\u003e\n```\n\n* Server side:\n\n```elixir\ndefhandler perform_long_process(socket, _sender) do\n  poke socket, progress_bar_class: \"progress-bar-danger\", long_process_button_text: \"Processing...\"\n\n  steps = :rand.uniform(100)\n  for i \u003c- 1..steps do\n    Process.sleep(:rand.uniform(500)) #simulate real work\n    poke socket, bar_width: Float.round(i * 100 / steps, 2)\n  end\n\n  poke socket, progress_bar_class: \"progress-bar-success\", long_process_button_text: \"Click me to restart\"\nend\n```\n\n## Prerequisites\n\n  1. Elixir ~\u003e 1.6.0 (see [Installation Guide](https://elixir-lang.org/install.html))\n\n  2. Phoenix ~\u003e 1.2 (see [Installation Guide](https://hexdocs.pm/phoenix/installation.html#phoenix))\n\n## Browser Requirements\n\nThis has the same requirements as Phoenix.Sockets.  Above that it depends on the javascript you call or other potential tools you use on top of Drab.\n\n## Installation\n\n  First of all, you need to have a Phoenix application, on top of which you will install Drab. If this is a standard app, generated with `mix phx.new`, you may use Drab Installer to make it running in one, simple step. Otherwise, see [Manual Installation](#manual-installation) section below.\n\n  1. Edit `mix.exs` in the main folder in your web application (if you have multiple application under an umbrella, this is the one ending with `_web`). Locate function `deps` (search for `def deps` string). Add an entry `{:drab, \"~\u003e 0.10.0\"}` to the list. Don't forget about comma!\n\n```elixir\ndef deps do\n  [\n    {...},\n    {:drab, \"~\u003e 0.10.0\"}\n  ]\nend\n```\n\n  2. Download and install packages:\n\n```bash\n$ mix deps.get\n```\n\n  3. Go to the application directory (**if your Phoenix Web application is under the umbrella, go there**) and run `mix drab.install`:\n\n```\nbash% mix drab.install\nChecking prerequisites for :my_app\n  lib/my_app_web/templates/layout/app.html.eex\n  lib/my_app_web/channels/user_socket.ex\n  config/config.exs\n  config/dev.exs\nThe installer is going to modify those files. OK to proceed? [Yn] Y\nDrab has been successfully installed in your Phoenix application.\n\nNow it is time to create your first commander, for example, for PageController:\n\n    mix drab.gen.commander Page\n```\n\nCongratulations! You have installed Drab and you can proceed with your own commander.\n\nPlease notice that Drab will run only on pages, which have the corresponding commander.\n\n## Usage\n\nAll the Drab functions (callbacks, event handlers) are placed in the module called `Commander`.\nThink about it as a controller for the live pages. Commanders should be placed in `web/commanders` directory.\n\nTo enable Drab on the pages generated with corresponding controller, you need to create a twin commander. For example, for `MyApp.PageController` the commander should be named `MyApp.PageCommander`.\n\nRemember the difference: `controller` renders the page, while `commander` works on the live page.\n\n  1. Generate the page Commander. The commander name should correspond to the controller, so `PageController` should have `PageCommander`:\n\n```bash\n$ mix drab.gen.commander Page\n* creating web/commanders/page_commander.ex\n```\n\n  2. Add the `@welcome_text` assign to `render/3` in index action in the controller, to be used in the future:\n\n```elixir\ndefmodule MyApp.PageController do\n  use Example.Web, :controller\n\n  def index(conn, _params) do\n    render conn, \"index.html\", welcome_text: \"Welcome to Phoenix!\"\n  end\nend\n```\n\n  3. Rename the template from `web/templates/page/index.html.eex` to `index.html.drab`\n\n  4. Edit the template `web/templates/page/index.html.drab` and change the fixed welcome text to the assign:\n\n```html\n\u003cdiv class=\"jumbotron\"\u003e\n  \u003ch2\u003e\u003c%= @welcome_text %\u003e\u003c/h2\u003e\n```\n\n  5. 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:\n\n```elixir\ndefmodule DrabExample.PageCommander do\n  use Drab.Commander\n\n  onload :page_loaded\n\n  # Drab Callbacks\n  def page_loaded(socket) do\n    poke socket, welcome_text: \"This page has been drabbed\"\n    set_prop socket, \"div.jumbotron p.lead\",\n      innerHTML: \"Please visit \u003ca href='https://tg.pl/drab'\u003eDrab\u003c/a\u003e page for more\"\n  end\nend\n```\n\nThe `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.\n\n  6. 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:\n\n```elixir\n[debug]\n    Started Drab for same_path:/, handling events in DrabExample.PageCommander\n    You may debug Drab functions in IEx by copy/paste the following:\nimport Drab.{Core, Element, Live}\nsocket = Drab.get_socket(pid(\"0.653.0\"))\n\n    Examples:\nsocket |\u003e exec_js(\"alert('hello from IEx!')\")\nsocket |\u003e poke(count: 42)\n\n```\n\nAs instructed, copy and paste those two lines, and check out yourself how you could remotely control the displayed page:\n\n```elixir\niex\u003e alert socket, \"Alert title\", \"Do you like modals?\", buttons: [ok: \"A juści\", cancel: \"Poniechaj\"]\n{:ok, %{}}\n\niex\u003e poke socket, welcome_text: \"WOW, this is nice\"\n%Phoenix.Socket{...}\n\niex\u003e query socket, \"div.jumbotron h2\", :innerText\n{:ok,\n %{\"[drab-id='425d4f73-9c14-4189-992b-41539377c9eb']\" =\u003e %{\"innerText\" =\u003e \"WOW, this is nice\"}}}\n\niex\u003e set_style socket, \"div.jumbotron\", backgroundColor: \"red\"\n{:ok, 1}\n```\n\n\n### The example above is available [here](https://github.com/grych/drab-example)\n\n## What now?\n\nVisit [Demo Page](https://tg.pl/drab) for a live demo and more description.\n\nVisit [Documentation](https://hexdocs.pm/drab) page.\n\n## Getting help\n\nThere is a Drab's thread on [elixirforum.com](https://elixirforum.com/t/drab-phoenix-library-for-server-side-dom-access/3277), please address questions there.\n\n## Tests and Sandbox\n\nSince 0.3.2, Drab is equipped with its own Phoenix Server for running integration tests automatically, for sandboxing and for  playing\nwith it.\n\n### Sandbox\n\n* clone Drab from github:\n\n```shell\ngit clone git@github.com:grych/drab.git\ncd drab\n```\n\n* get deps and node modules:\n\n```shell\nmix deps.get\nnpm install \u0026\u0026 node_modules/brunch/bin/brunch build\n```\n\n* start Phoenix with Drab:\n\n```shell\niex -S mix phoenix.server\n```\n\n* open the browser and navigate to http://localhost:4000\n\n* follow the instructions in IEx to play with Drab functions:\n\n```elixir\nimport Drab.{Core, Live, Element, Query, Waiter}\nsocket = Drab.get_socket(pid(\"0.784.0\"))\n\niex\u003e query socket, \"h3\", :innerText\n{:ok,\n %{\"#header\" =\u003e %{\"innerText\" =\u003e \"Drab Tests\"},\n   \"#page_loaded_indicator\" =\u003e %{\"innerText\" =\u003e \"Page Loaded\"}}}\n\niex\u003e set_prop socket, \"h3\", innerText: \"Updated from IEx\"\n{:ok, 2}\n\niex\u003e exec_js socket, \"alert('You do like alerts?')\"\n{:ok, nil}\n```\n\n### Tests\n\nMost of the Drab tests are integration (end-to-end) tests, thus they require automated browser. Drab uses [chromedriver](https://sites.google.com/a/chromium.org/chromedriver/), which must be running while you run tests.\n\n* clone Drab from github:\n\n```shell\ngit clone git@github.com:grych/drab.git\ncd drab\n```\n\n* get deps and node modules:\n\n```shell\nmix deps.get\nnpm install \u0026\u0026 node_modules/brunch/bin/brunch build\n```\n\n* run `chromedriver`\n\n* run tests:\n\n```shell\n% mix test\nCompiling 23 files (.ex)\n........................................\n\nFinished in 120.9 seconds\n123 tests, 0 failures\n\nRandomized with seed 934572\n```\n\n## Manual Installation\n\n  1. Add Drab to the dependencies in `mix.exs`.\n\n  2. Initialize Drab client library by adding to the layout page (`app.html.eex` - or any other layout you use).\n\n```html\n\u003c%= Drab.Client.run(@conn) %\u003e\n```\n\n  just after the following line:\n\n```html\n\u003cscript src=\"\u003c%= static_path(@conn, \"/js/app.js\") %\u003e\"\u003e\u003c/script\u003e\n```\n\n  3. Initialize Drab sockets by adding the following to `user_socket.ex`:\n\n```elixir\nuse Drab.Socket\n```\n\n  4. Add Drab template engine and application name with endpoint to `config.exs`:\n\n```elixir\nconfig :phoenix, :template_engines,\n  drab: Drab.Live.Engine\n\nconfig :drab, MyAppWeb.Endpoint,\n  otp_app: :my_app_web\n```\n\n  5. Add `:drab` to applications started by default in `mix.exs`:\n\n```elixir\ndef application do\n  [mod: {MyApp, []},\n   applications: [:phoenix, :phoenix_pubsub, :phoenix_html, :cowboy, :logger, :gettext, :drab]]\nend\n```\n\n  It is not needed if you are running Phoenix 1.3\n\n  6. To enable live reload on Drab pages, add `.drab` extension to live reload patterns in `dev.exs`:\n\n```elixir\nconfig :my_app, MyApp.Endpoint,\n  live_reload: [\n    patterns: [\n      ~r{priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$},\n      ~r{priv/gettext/.*(po)$},\n      ~r{web/views/.*(ex)$},\n      ~r{web/templates/.*(eex|drab)$}\n    ]\n  ]\n```\n\n  7. If you are not using webpack, you will get `require is not defined` error. You need to provide `Socket`:\n\n  In the `app.js` add a global variable, which will be passed to Drab later:\n\n```elixir\n  window.__socket = require(\"phoenix\").Socket;\n```\n\n  Then, tell Drab to use this instead of default `require(\"phoenix\").Socket`. Add to `config.exs`:\n\n```elixir\nconfig :drab, MyAppWeb.Endpoint,\n  js_socket_constructor: \"window.__socket\"\n```\n\n\n#### If you want to use Drab.Query (jQuery based module):\n\n  1. Add `jquery` and `boostrap` to `package.json`:\n\n```json\n\"dependencies\": {\n  \"jquery\": \"\u003e= 3.1.1\",\n  \"bootstrap\": \"~3.3.7\"\n}\n```\n\n  2. Add jQuery as a global at the end of `brunch-config.js`:\n\n```javascript\nnpm: {globals: {\n  $: 'jquery',\n  jQuery: 'jquery',\n  bootstrap: 'bootstrap'\n}}\n```\n\n  3. And install it:\n\n```bash\n$ npm install \u0026\u0026 node_modules/brunch/bin/brunch build\n```\n\n## Contact\n\n(c)2016-2018 Tomek \"Grych\" Gryszkiewicz,\n\u003cgrych@tg.pl\u003e\n\n\nIllustrations by https://www.vecteezy.com\n\n\n","funding_links":[],"categories":["Elixir"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgrych%2Fdrab","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgrych%2Fdrab","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgrych%2Fdrab/lists"}