Getting Started

Dialup を使って最初のリアルタイムページを動かすまでのガイドです。

インストール

mix dialup.new ジェネレータで新規プロジェクトを作成します(推奨)。

# `dialup_new`コマンドをインストール(初回のみ)
mix archive.install hex dialup_new

# 新規プロジェクト作成
mix dialup.new my_app
cd my_app
mix deps.get
mix run --no-halt

ブラウザで http://localhost:4000 にアクセスして動作を確認してください。

生成されるファイル構成

my_app/
├── priv/static/         # 静的ファイル置き場(画像・favicon など)
└── lib/
    ├── my_app.ex        # Application モジュール
    ├── root.html.heex   # HTML シェル(<head> / hooks / analytics)
    └── app/
        ├── layout.ex    # ルートレイアウト
        ├── layout.css   # 全ページ共通スタイル(コロケーション CSS)
        ├── page.ex      # ホームページ /
        ├── page.css     # / 固有スタイル(コロケーション CSS)
        ├── error.ex     # エラーページ(404 / 500)
        └── error.css    # エラーページスタイル

Application モジュール

lib/my_app.ex が Dialup の起点です。use Dialup で設定を行い、Supervision Tree に {Dialup, app: __MODULE__, port: 4000} を追加するだけです。

defmodule MyApp do
  use Application

  use Dialup,
    app_dir: __DIR__ <> "/app",
    title: "My App",
    lang: "en"

  @impl Application
  def start(_type, _args) do
    children = [
      {Dialup, app: __MODULE__, port: 4000}
    ]
    Supervisor.start_link(children, strategy: :one_for_one, name: MyApp.Supervisor)
  end
end
app_dir に指定したディレクトリのファイル配置が自動的にルートになります。 router.ex への手書き登録は不要です。

root.html.heex — HTML シェル

lib/root.html.heex は全ページ共通の HTML シェルです。 <head> へのタグ追加・分析スクリプト・JS フックの登録はここで行います。 id="dialup-root"Dialup.connect() は必須です。

<!DOCTYPE html>
<html lang="{@lang}">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>{@title}</title>
  <!-- ここに <meta>, <link>, analytics を追加 -->
</head>
<body>
  <div id="dialup-root">{raw(@inner_content)}</div>
  <script src="/idiomorph.js"></script>
  <script src="/dialup.js"></script>
  <script>
    Dialup.connect({
      /* hooks: { MyHook: { mounted(el) {}, destroyed(el) {} } } */
    });
  </script>
</body>
</html>

最初のページを作る

lib/app/page.ex がルートページ / になります。

defmodule Dialup.App.Page do
  use Dialup.Page

  def mount(_params, assigns) do
    {:ok, Map.put(assigns, :count, 0)}
  end

  def handle_event("increment", _, assigns) do
    {:update, Map.update!(assigns, :count, &(&1 + 1))}
  end

  def render(assigns) do
    ~H"""
    <h1>Hello, Dialup!</h1>
    <p>Count: {@count}</p>
    <button ws-event="increment">+1</button>
    """
  end
end

ページの追加とルーティング

ファイルを置くだけでルートが追加されます。動的ルートは [パラメータ名] ディレクトリで表現します。

lib/app/
├── page.ex              # /
├── about/
│   └── page.ex          # /about
├── blog/
│   ├── layout.ex        # /blog/* 共通レイアウト
│   └── [slug]/
│       └── page.ex      # /blog/:slug(動的ルート)
└── users/
    └── [id]/
        └── page.ex      # /users/:id

コロケーション CSS

page.ex と同名の page.css を同じディレクトリに置くと、 コンパイル時に自動スコーピングされて注入されます。ビルドツール不要。

/* lib/app/page.css — page.ex と同じディレクトリに置く */
.hero {
  text-align: center;
  padding: 4rem 2rem;
}
h1 { font-size: 3rem; }
静的ファイル配信: priv/static/ に配置したファイルは / パスから自動配信されます。 例: priv/static/images/logo.png<img src="/images/logo.png">

次のステップ