mcpField
F
frappe-impl-ui-components

frappe-impl-ui-components

github.com/impertio-studio開発198111 回インストール30 週間前に更新
リポジトリ

frappe-impl-ui-components について

FrappeのUIを開発する人向けに、クライアント側コンポーネントとリアルタイム更新の実装を案内するスキルです。ダイアログ、ページ、リストビュー、イベント購読の手順とAPIをまとめています。

主な機能

  1. 01ダイアログとフォーム入力のパターン
  2. 02メッセージ、通知、確認操作
  3. 03リストビューのカスタマイズ
  4. 04カスタムページとカレンダー
  5. 05カンバンボードの設定
  6. 06リアルタイムイベントとスキャナーAPI

活用シーン

  1. 01ダイアログでの入力収集
  2. 02DocTypeリストビューのカスタマイズ
  3. 03カスタムアプリページの構築
  4. 04リアルタイムイベントによる画面更新

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/impertio-studio/frappe_claude_skill_package --skill frappe-impl-ui-components

インストール後、Claude Code で/frappe-impl-ui-componentsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
.gitkeep
anti-patterns.md
controls-api.md
decision-tree.md
examples.md
tree-view.md
workflows.md

詳細

Frappe UI コンポーネントとリアルタイム — 実装ワークフロー

クライアント側の UI を構築するためのステップバイステップのワークフローです。フォームのスクリプトについては frappe-impl-clientscripts を参照してください。サーバー側 API については frappe-syntax-serverscripts を参照してください。

バージョン:v14/v15/v16 | 注:v15 以降は Bootstrap 5 を使用します。Dialog API はすべてのバージョンで安定しています。

クイック判断:どの UI コンポーネントを使うか?

WHAT do you need?
├── Prompt user for input         → frappe.prompt (simple) or frappe.ui.Dialog (complex)
├── Show a message/alert          → frappe.msgprint / frappe.show_alert / frappe.throw
├── Confirm an action             → frappe.confirm
├── Multi-field data entry popup  → frappe.ui.Dialog with fields
├── Select from a list of records → frappe.ui.form.MultiSelectDialog
├── Full custom page (not a form) → frappe.ui.Page
├── Customize list columns/colors → frappe.listview_settings
├── Visual board for workflow     → Kanban Board (Select field based)
├── Date-based record view        → Calendar View ({doctype}_calendar.js)
├── Hierarchical data display     → Tree View (is_tree DocType)
├── Live updates without refresh  → frappe.publish_realtime + frappe.realtime.on
├── Show background job progress  → frappe.publish_progress
├── Scan barcode/QR code          → frappe.ui.Scanner
└── Custom cell formatting        → formatters in listview_settings or form

完全な判断ツリーについては references/decision-tree.md を参照してください。

ワークフロー 1:ダイアログ(frappe.ui.Dialog)

シンプルなダイアログ
let d = new frappe.ui.Dialog({
    title: "Enter Details",
    fields: [
        { label: "Full Name", fieldname: "full_name", fieldtype: "Data", reqd: 1 },
        { label: "Email", fieldname: "email", fieldtype: "Data", options: "Email" },
        { label: "Role", fieldname: "role", fieldtype: "Select",
          options: "Developer\nManager\nDesigner" },
    ],
    size: "small",  // "small", "large", or "extra-large"
    primary_action_label: "Create",
    primary_action(values) {
        frappe.call({
            method: "myapp.api.create_user",
            args: values,
            callback(r) {
                if (!r.exc) {
                    frappe.show_alert({ message: "User created", indicator: "green" });
                    d.hide();
                }
            }
        });
    }
});
d.show();

ルール:非同期呼び出しが完了する前ではなく、必ずコールバック内で d.hide() を呼び出してください。

テーブルフィールド付きダイアログ
let d = new frappe.ui.Dialog({
    title: "Add Items",
    fields: [
        { label: "Customer", fieldname: "customer", fieldtype: "Link",
          options: "Customer", reqd: 1 },
        { fieldtype: "Section Break" },
        { label: "Items", fieldname: "items", fieldtype: "Table",
          in_place_edit: true, reqd: 1,
          fields: [
              { fieldname: "item", label: "Item", fieldtype: "Link",
                options: "Item", in_list_view: 1, reqd: 1 },
              { fieldname: "qty", label: "Qty", fieldtype: "Int",
                in_list_view: 1, default: 1 },
              { fieldname: "rate", label: "Rate", fieldtype: "Currency",
                in_list_view: 1 },
          ],
        },
    ],
    primary_action_label: "Submit",
    primary_action(values) {
        console.log(values);  // { customer: "...", items: [{item, qty, rate}] }
        d.hide();
    }
});
d.show();

ルール:表示するテーブルの子フィールドには、必ず in_list_view: 1 を設定してください。設定されていないフィールドはグリッド内で非表示になります。

複数ステップのダイアログ
let d = new frappe.ui.Dialog({
    title: "Setup Wizard",
    fields: [
        // Page 1
        { fieldtype: "Section Break", label: "Step 1: Basic Info",
          collapsible: 0 },
        { label: "Name", fieldname: "name", fieldtype: "Data", reqd: 1 },
        // Page 2
        { fieldtype: "Section Break", label: "Step 2: Configuration",
          collapsible: 0 },
        { label: "Option", fieldname: "option", fieldtype: "Select",
          options: "A\nB\nC" },
    ],
    primary_action_label: "Finish",
    primary_action(values) {
        d.hide();
    }
});
d.show();
主なダイアログメソッド
メソッド用途
d.show()ダイアログを表示します
d.hide()ダイアログを閉じます
d.get_values()すべてのフィールド値をオブジェクトとして取得します
d.set_values({field: val})フィールド値を設定します
d.get_field("name")特定のフィールドコントロールを取得します
d.set_df_property("name", "hidden", 1)フィールドの表示/非表示を動的に切り替えます
d.disable_primary_action()送信ボタンをグレーアウトします
d.enable_primary_action()送信ボタンを再度有効にします

ワークフロー 2:メッセージとアラート

frappe.msgprint:モーダルメッセージ
// Simple message
frappe.msgprint("Record saved successfully");

// With options
frappe.msgprint({
    title: "Warning",
    message: "This action cannot be undone",
    indicator: "orange",     // green, blue, orange, red
    primary_action: {
        label: "Proceed",
        action() { do_something(); }
    }
});

// List of messages
frappe.msgprint({
    title: "Validation Errors",
    message: "Please fix the following:",
    as_list: true,
    indicator: "red",
});
frappe.throw:例外を伴うエラー
// Client-side: shows msgprint and stops execution
frappe.throw("Amount cannot be negative");
# Server-side: raises ValidationError, shown as red msgprint
frappe.throw("Amount cannot be negative")
frappe.throw("Not Permitted", frappe.PermissionError)  # specific exception

ルール:検証エラーには必ず frappe.throw を使用してください。エラーに frappe.msgprint は絶対に使用しないでください。実行が停止しないためです。

frappe.confirm:はい/いいえのダイアログ
frappe.confirm(
    "Are you sure you want to delete this record?",
    () => { /* Yes callback */ delete_record(); },
    () => { /* No callback (optional) */ }
);
frappe.prompt:単一フィールドへの簡易入力
frappe.prompt(
    { label: "Reason", fieldname: "reason", fieldtype: "Small Text", reqd: 1 },
    (values) => {
        console.log(values.reason);
    },
    "Enter Reason",    // dialog title
    "Submit"           // primary action label
);

// Multiple fields
frappe.prompt([
    { label: "Reason", fieldname: "reason", fieldtype: "Small Text", reqd: 1 },
    { label: "Priority", fieldname: "priority", fieldtype: "Select",
      options: "Low\nMedium\nHigh" },
], (values) => { console.log(values); }, "Details");
frappe.show_alert:トースト通知
// Simple
frappe.show_alert("Saved");

// With indicator and duration
frappe.show_alert({ message: "Email sent", indicator: "green" }, 5);
// Duration in seconds (default: 7)

ルール:ブロックしない成功メッセージには frappe.show_alert を使用してください。ユーザーに必ず確認してもらう必要がある場合は frappe.msgprint を使用してください。

ワークフロー 3:リストビューのカスタマイズ

DocType ディレクトリに {doctype_name}_list.js を作成します。

// myapp/doctype/task/task_list.js
frappe.listview_settings["Task"] = {
    // Extra fields to fetch (beyond standard)
    add_fields: ["priority", "status", "assigned_to"],

    // Hide the name column
    hide_name_column: true,

    // Row indicator (colored dot)
    get_indicator(doc) {
        // MUST return [label, color, comma-separated-filter]
        if (doc.status === "Completed") return ["Completed", "green", "status,=,Completed"];
        if (doc.status === "Overdue") return ["Overdue", "red", "status,=,Overdue"];
        return ["Open", "orange", "status,=,Open"];
    },

    // Custom column formatters
    formatters: {
        priority(val) {
            const colors = { High: "red", Medium: "orange", Low: "green" };
            return `<span class="indicator-pill ${colors[val] || ""}">${val}</span>`;
        }
    },

    // Row action button
    button: {
        show(doc) { return doc.status === "Open"; },
        get_label() { return __("Complete"); },
        get_description(doc) { return __("Mark {0} as complete", [doc.name]); },
        action(doc) {
            frappe.xcall("myapp.api.complete_task", { task: doc.name })
                .then(() => cur_list.refresh());
        }
    },

    // Lifecycle hooks
    onload(listview) {
        listview.page.add_inner_button("Export", () => export_tasks());
    },

    refresh(listview) {
        // Runs on every list refresh
    },

    // Default filters
    filters: [["status", "!=", "Cancelled"]],
};

ルール:get_indicator から必ず 3 要素の配列を返してください。3 つ目の要素は、クリックしてフィルターするためのフィルター文字列です。

ワークフロー 4:カスタムページ(frappe.ui.Page)

ステップ 1:hooks.py に登録する
# hooks.py
page_js = { "my-custom-page": "public/js/my_custom_page.js" }
ステップ 2:ページ定義を作成する
// myapp/myapp/my_custom_page/my_custom_page.js
frappe.pages["my-custom-page"].on_page_load = function(wrapper) {
    let page = frappe.ui.make_app_page({
        parent: wrapper,
        title: "My Custom Page",
        single_column: true,
    });

    // Primary action button
    page.set_primary_action("Create", () => create_new(), "octicon octicon-plus");

    // Secondary action
    page.set_secondary_action("Refresh", () => refresh_data());

    // Dropdown menu
    page.add_menu_item("Export CSV", () => export_csv());
    page.add_menu_item("Settings", () => frappe.set_route("Form", "My Settings"));

    // Inner toolbar buttons
    page.add_inner_button("Update All", () => update_all());
    page.add_inner_button("New Post", () => new_post(), "Make");  // grouped

    // Toolbar filter fields
    let status_field = page.add_field({
        label: "Status",
        fieldtype: "Select",
        fieldname: "status",
        options: ["", "Open", "Closed", "Cancelled"],
        change() { refresh_data(); }
    });

    // Status indicator
    page.set_indicator("Active", "green");

    // Content area
    $(page.body).html(`<div class="my-page-content"></div>`);

    // Load initial data
    refresh_data();
};
主なページメソッド
メソッド用途
page.set_title(title)ページの見出しを設定します
page.set_indicator(label, color)ステータスバッジ(緑/赤/オレンジ/青)
page.set_primary_action(label, fn, icon)メインのアクションボタンを設定します
page.set_secondary_action(label, fn)セカンダリボタンを設定します
page.add_menu_item(label, fn)ドロップダウンメニュー項目を追加します
page.add_inner_button(label, fn, group)ツールバーにボタンを追加します(グループは任意)
page.add_field({...})ツールバーにフィルター/入力フィールドを追加します
page.get_form_values()ツールバーのすべてのフィールド値を取得します
page.clear_fields()ツールバーのフィールドをすべて削除します
page.clear_primary_action()プライマリボタンを削除します

ワークフロー 5:カレンダービュー

DocType ディレクトリに {doctype}_calendar.js を作成します。

// myapp/doctype/event/event_calendar.js
frappe.views.calendar["Event"] = {
    field_map: {
        start: "starts_on",
        end: "ends_on",
        id: "name",
        title: "subject",
        allDay: "all_day",
        color: "color",
    },
    gantt: true,  // Enable Gantt view toggle
    get_events_method: "myapp.api.get_events",  // Optional custom event source
    filters: [
        { fieldtype: "Link", fieldname: "event_type", label: "Type",
          options: "Event Type" }
    ],
};

ルール:start と end は、DocType の実際の Date または Datetime フィールドに必ずマッピングしてください。マッピングがないと、カレンダーが空白になります。

ワークフロー 6:カンバンボード

カンバンボードは、Select フィールドを持つ任意の DocType で使用できます。コードは不要です。

  1. リストビューを開く → サイドバー → カンバン → 新しいカンバンボード
  2. Select フィールド(例:status)を選択します。オプションが列になります
  3. 保存すると、カードを列間でドラッグできます

ルール:Select フィールドのない DocType には、カンバンボードを絶対に作成しないでください。プログラムによる設定については references/examples.md を参照してください。

ワークフロー 7:リアルタイム更新(Socket.IO)

サーバー:イベントを公開する
# Broadcast to all users
frappe.publish_realtime("task_updated", {"task": task.name, "status": "Done"})

# Send to specific user
frappe.publish_realtime("notification", {"msg": "Your report is ready"},
    user="[email protected]")

# Send to users viewing a specific document
frappe.publish_realtime("doc_updated", {"field": "status"},
    doctype="Task", docname="TASK-001")

# ALWAYS use after_commit=True in document events
frappe.publish_realtime("order_created", message, after_commit=True)
クライアント:イベントを購読する
// Listen for events
frappe.realtime.on("task_updated", (data) => {
    frappe.show_alert({ message: `Task ${data.task}: ${data.status}`, indicator: "green" });
    cur_list && cur_list.refresh();
});

// Stop listening
frappe.realtime.off("task_updated");
進捗インジケーター
# Server: publish progress during long operations
def process_items(items):
    total = len(items)
    for i, item in enumerate(items):
        process(item)
        frappe.publish_progress(
            percent=(i + 1) / total * 100,
            title="Processing Items",
            description=f"Processing {item.name}",
        )

ルール:ドキュメントイベントから公開する場合は、必ず after_commit=True を使用してください。設定しないと、トランザクションがロールバックされてもイベントが発行されます。

リアルタイムルーム
ルーム対象ユーザー用途
(default)すべてのシステムユーザーグローバル通知
user:{email}単一ユーザー個人向けアラート
doctype:{dt}リストを表示しているユーザーリスト更新のトリガー
doc:{dt}/{name}ドキュメントを表示しているユーザードキュメント変更アラート
websiteゲストを含むすべてのユーザー公開アナウンス

ワークフロー 8:スキャナー API(バーコード/QR)

// Single scan — closes after first scan
new frappe.ui.Scanner({
    dialog: true, multiple: false,
    on_scan(data) {
        frappe.set_route("Form", "Item", data.decodedText);
    }
});

// Continuous scanning — stays open for multiple scans
let scanner = new frappe.ui.Scanner({
    dialog: true, multiple: true,
    on_scan(data) { add_item_to_list(data.decodedText); }
});
// Stop: scanner.stop_scan() or close the dialog

ルール:単一アイテムを検索する場合は、必ず multiple: false を設定してください。バーコードを使った Stock-Entry の完全な例については、references/examples.md を参照してください。

アンチパターンのまとめ

アンチパターン正しい方法
エラーに frappe.msgprint を使用するfrappe.throw を使用します。実行が停止します
非同期処理が完了する前にダイアログを非表示にするコールバック内で非表示にします:callback() { d.hide(); }
ダイアログ内で同期 API 呼び出しを行う必ず frappe.call / frappe.xcall(非同期)を使用します
テーブルフィールドで in_list_view が未設定表示する列に in_list_view: 1 を設定します
after_commit なしで publish_realtime を使用するドキュメントイベントでは必ず after_commit=True を使用します
Select フィールドのない DocType で Kanban を使用するKanban では列に Select フィールドが必要です
カレンダーの field_map で start/end が未設定必ず start と end の両方のフィールドをマッピングします
get_indicator から 2 要素の配列を返す必ず 3 要素を返します:[ラベル、色、フィルター]

参考ファイル

  • references/controls-api.md — frappe.ui.form.make_control() によるスタンドアロンコントロール、コントロールタイプの完全なリファレンス、コントロールのメソッドとイベント
  • references/tree-view.md — Tree DocType の設定、frappe.views.TreeView API、frappe.ui.Tree の低レベル API、ツリーノードの操作
  • references/workflows.md — 拡張ワークフローの手順
  • references/examples.md — 完全なコード例
  • references/decision-tree.md — UI コンポーネントの完全な判断ツリー
  • references/anti-patterns.md — コード例を含む、詳しいアンチパターン

関連項目

  • frappe-impl-clientscripts — フォームレベルのクライアントスクリプト
  • frappe-syntax-clientscripts — クライアントサイド API の構文リファレンス
  • frappe-impl-hooks — ページとルートのフック登録

コメント