Odoo 14から導入されたOWL(Odoo Web Library)は、フロントエンド開発を大幅に強化しましたが、初期段階のフレームワークであるため、公式ドキュメントだけでは実装方法が不明確な場合があります。特にカスタムウィジェットを作成する際、現在のレコードID(res_id)の取得や、サーバーサイドのモデルメソッドの実行(RPC呼び出し)は頻繁に必要となる操作です。
レコード情報へのアクセス
OWLのコンポーネント、特にフィールドを拡張する AbstractFieldOwl を継承したクラス内では、this.record プロパティを介してレコードのデータにアクセスできます。現在のレコードIDを取得するには以下のプロパティを参照します。
// 現在のレコードのIDを取得
const currentId = this.record.res_id;
モデルメソッドの実行(RPC呼び出し)
Python側のモデルで定義したメソッドを呼び出すには、this.rpc を使用します。これにより、バックエンドのロジックを実行し、その結果を非同期で取得してUIに反映させることが可能です。
以下に、AbstractFieldOwl を使用してカスタムフィールドを定義し、サブコンポーネントと通信しながらデータを取得する実装例を示します。
JavaScript 実装例
odoo.define('custom_module.team_viewer', function (require) {
"use strict";
const { Component } = owl;
const AbstractFieldOwl = require('web.AbstractFieldOwl');
const fieldRegistry = require('web.field_registry_owl');
// 子コンポーネント:個別メンバーのカード表示
class TeamMemberCard extends Component {
static template = 'TeamMemberCardTemplate';
onCardClick() {
// 親コンポーネントにイベントを通知
this.trigger('member-selected', {
memberId: this.props.memberId
});
}
}
// メインコンポーネント:フィールドウィジェット
class TeamDisplayWidget extends AbstractFieldOwl {
static supportedFieldTypes = ['many2many'];
static template = 'TeamDisplayMain';
static components = { TeamMemberCard };
constructor(...args) {
super(...args);
this.membersData = [];
}
async willStart() {
const contextId = this.record.res_id;
if (contextId) {
// モデルメソッドの呼び出し
this.membersData = await this.rpc({
model: 'res.partner',
method: 'search_read',
domain: [['id', 'in', this.value.res_ids]],
fields: ['id', 'name', 'email'],
});
}
}
async onMemberDetailRequest(ev) {
const partnerId = ev.detail.memberId;
// バックエンドのアクション定義を取得する例
const action = await this.rpc({
model: 'res.users',
method: 'get_external_user_action',
args: [partnerId],
});
// 注意: OWLコンポーネント内では従来の this.do_action は直接利用できないため、
// AbstractFieldOwl のコンテキストに応じたアクション制御が必要です。
console.log("Action data retrieved:", action);
}
}
fieldRegistry.add('team_member_viewer', TeamDisplayWidget);
return TeamDisplayWidget;
});
XML テンプレート実装例
OWLのテンプレートでは owl="1" 属性を付与し、t-on- ディレクティブを使用してイベントハンドリングを行います。
<templates>
<!-- 子コンポーネントのテンプレート -->
<t t-name="TeamMemberCardTemplate" owl="1">
<div class="card p-2 m-1 shadow-sm" t-on-click="onCardClick" style="cursor: pointer;">
<div class="card-body">
<h6 class="card-title">
<t t-esc="props.name"/>
</h6>
<p class="small text-muted">ID: <t t-esc="props.memberId"/></p>
</div>
</div>
</t>
<!-- メインウィジェットのテンプレート -->
<div t-name="TeamDisplayMain" owl="1" t-on-member-selected="onMemberDetailRequest">
<div class="d-flex flex-wrap">
<t t-foreach="membersData" t-as="m" t-key="m.id">
<TeamMemberCard name="m.name" memberId="m.id"/>
</t>
</div>
</div>
</templates>
実装のポイント
Odoo 14のOWL環境において、ビューの遷移(do_action)を伴う高度な操作を行いたい場合は、web.AbstractFieldOwl を利用してレガシーなWebクライアント層との互換性を保つのが一般的です。純粋なOWLコンポーネント内では、env を通じてサービスにアクセスし、アクションマネージャーを呼び出す設計が求められます。res_id を起点にRPCでデータを動的に取得することで、標準機能では難しい柔軟なUI表現が可能になります。