チャプター2: ギャラリービューを作成¶
一から完全に新しいビューを作る方法を見てみましょう。 ある意味では、それは非常に難しいことではありませんが、それを行う方法について本当に良いリソースはありません。 ほとんどの状況は、既存のビューをカスタマイズするか、クライアントアクションで解決する必要があることに注意してください。
この課題では、`gallery`ビューを作成したいと仮定します。 画像フィールドを持つ一連の記録を表すことができます
問題は間違いなくかんばんビューで解決することができます。 だがこれは通常のカンバンとギャラリービューを 同じ動作では使えないことを意味する
ギャラリービューを作ってみましょう。ギャラリービューは、それぞれの image_field
属性によって定義されます。
<gallery image_field="some_field"/>
この章でタスクを完了するには、awesome_gallery addon をインストールする必要があります。 このアドオンには、新しいビューを追加するために必要なサーバーファイルが含まれています。
目標

章の各演習の解決策は、 official Odoo tutorials repository にホストされています。
1. ハローワールドビューを作る¶
最初のステップは、単純なコンポーネントで JavaScript の実装を作成することです。
gallery_view.js
、gallery_controller.js
、gallery_controller.xml
ファイルをstatic/src
に作成します。gallery_controller.js
に単純な hello world コンポーネントを実装します。gallery_view.js
でコントローラをインポートし、ビューオブジェクトを作成し、ビューレジストリにgallery
という名前で登録します。Example
次に、ビューオブジェクトを定義する例を示します。
import { registry } from "@web/core/registry"; import { MyController } from "./my_controller"; export const myView = { type: "my_view", display_name: "MyView", icon: "oi oi-view-list", multiRecord: true, Controller: MyController, }; registry.category("views").add("my_controller", myView);
contacts.action_contacts
アクションのビュータイプにgallery
を追加します。ギャラリービューに切り替える際に、Helloワールドコンポーネントが表示されていることを確認してください。


2. レイアウトコンポーネントを使用¶
これまでのところ、ギャラリービューは標準ビューのようには見えません。他のビューのような標準機能を持つために Layout
コンポーネントを使用しましょう。
Layout
コンポーネントをインポートし、GalleryController
のcomponents
に追加します。Layout
を使用するためにテンプレートを更新します。display
propが必要です。props.display` で見つけることができます。

3. アーチを解析¶
今のところ, 私たちのギャラリービューはあまりありません. ビューのアーチに含まれている情報を読むことから始めましょう.
アーチを解析するプロセスは通常、各ビューに固有の ArchParser
で行われます。一般的な XMLParser
クラスを継承します。
Example
ArchParserがどのように見えるかを以下に示します。
export class MyCustomArchParser {
parse(xmlDoc) {
const myAttribute = xmlDoc.getAttribute("my_attribute")
return {
myAttribute,
}
}
}
独自のファイルに
ArchParser
クラスを作成します。`image_field`の情報を読むのに使います。
gallery
ビューコードを更新して、コントローラが受け取った props に追加します。
注釈
基本的にはアーチから属性を1つだけ読み取る必要があるので、そうするのは少しやり過ぎかもしれません。 それは他のどのオドウの見解でも使われるデザインです コントローラから前面処理を抽出することができます
関連項目
4. いくつかのデータを読み込む¶
サーバーから実際のデータを取得しましょう。そのためには、Orm サービスから webSearchRead
を使用する必要があります。
Example
モデルからレコードを取得するための webSearchRead
の例を以下に示します。
const { length, records } = this.orm.webSearchRead(this.resModel, domain, {
specification: {
[this.fieldToFetch]: {},
[this.secondFieldToFetch]: {},
},
context: {
bin_size: true,
}
})
GalleryController
にloadImages(domain) {...}
メソッドを追加します。 これは、ドメインに対応するレコードを取得するために、ormサービスから`webSearchRead`コールを実行し、propsで受け取った`imageField`を使用する必要があります。呼び出しのコンテキストに
bin_size
を含まない場合、base64 でエンコードされた画像フィールドを受け取ります。 画像フィールドのサイズを受け取るには、コンテキストにbin_size
を入れてください。後で画像を表示します。`setup`コードを変更して、`onWillStart`と`onWillUpdateProps`フックでそのメソッドを呼び出します。
テンプレートを変更して、
Layout
コンポーネントのデフォルトスロット内の各イメージの id とサイズを表示します。
注釈
ロードデータコードは、次の課題で適切なモデルに移動されます。

5. 並行問題を解決¶
今のところ、私たちのコードは並列化されていません。ドメインを2回変更すると、`loadImages(domain)`が2回トリガーされます。 このように、異なる要因によって異なる時期に到達できるリクエストが二つあります。 2番目のリクエストに対するレスポンスを受信した後、最初のリクエストに対するレスポンスを受信すると、一貫性のない状態になります。
Odoo の KeepLast
プリミティブはこの問題を解決し、タスクのリストを管理し、最後のタスクだけをアクティブにします。
:file:`@web/core/utils/concurrency`から`KeepLast`をインポートします。
モデルに
KeepLast
オブジェクトをインスタンス化します。KeepLast
に`webSearchRead` を追加して、最後の呼び出しのみが解決されます。
6. コードを再編成する¶
実際の見解はもう少し整理されています。 この例ではやり過ぎかもしれませんが、Odoo でコードを構造化する方法を学ぶことを目的としています。 また、これは変化する要件でより良いスケールになります。
すべてのモデルコードを
GalleryModel
クラスに移動します。レンダリングコードを
GalleryRenderer
コンポーネント内に移動します。`GalleryController`に`GalleryModel`と`GalleryRenderer`をインポートして動作させます。
7. ビューを拡張可能にする¶
ビューを拡張するために、ギャラリービューオブジェクトをインポートして好みに変更することができます。 問題は、現時点では、コントローラ内でハードコードされているため、カスタムモデルやレンダラーを定義することはできないということです。
ギャラリービューファイルに
GalleryModel
とGalleryRenderer
をインポートします。ギャラリービューオブジェクトに`Model`と`Renderer`キーを追加し、`GalleryModel`と`GalleryRenderer`に割り当てます。 `Model`と`Renderer`をpropsとしてコントローラに渡します。
コントローラ内のハードコードされたインポートを削除し、props から取得します。
動的サブコンポーネントを持つには t-component を使用します。
注釈
これは、レンダラーを変更することによってギャラリービューを拡張できる方法です。
import { registry } from '@web/core/registry';
import { galleryView } from '@awesome_gallery/gallery_view';
import { GalleryRenderer } from '@awesome_gallery/gallery_renderer';
export class MyExtendedGalleryRenderer extends GalleryRenderer {
static template = "my_module.MyExtendedGalleryRenderer";
setup() {
super.setup();
console.log("my gallery renderer extension");
}
}
registry.category("views").add("my_gallery", {
...galleryView,
Renderer: MyExtendedGalleryRenderer,
});
8. 画像を表示¶
フィールドが設定されている場合、レンダラーを更新して画像を素敵な方法で表示します。image_field
が空の場合は、代わりに空のボックスを表示します。
ちなみに
レコードから画像を取得できるコントローラがあります。このスニペットを使用してリンクを構築できます:
import { url } from "@web/core/utils/urls";
const url = url("/web/image", {
model: resModel,
id: image_id,
field: imageField,
});

9. クリックするとフォームビューに切り替える¶
レンダラーを更新して、画像上のクリックに反応し、フォームビューに切り替えます。 アクションサービスの switchView
関数を使用できます。
関連項目
10. オプションのツールチップを追加¶
マウスホバーに関する追加情報を持つことが便利です。
コードを更新して、アーチのオプションの追加属性を許可します。
<gallery image_field="some_field" tooltip_field="some_other_field"/>
マウスホバーでは、ツールチップフィールドの内容を表示します。 フィールドが文字フィールド、数値フィールド、または many2one フィールドである場合には動作します。 html要素にツールチップを置くには、要素の`data-tooltip`属性に文字列を入れます。
顧客をツールチップフィールドとして追加するには、顧客ギャラリービューアーチを更新します。

関連項目
11. ページネーションを追加¶
コントロールパネルにページャーを追加し、通常の Odoo ビューのようにすべてのページネーションを管理しましょう。

12. ビューの検証¶
これまでのところ、私たちは素晴らしいと便利なビューを持っています。 しかし実際の生活では Galleryビューの「arch」をエンコードするユーザーに問題があるかもしれません。現在は構造化されていないXMLだけです。
検証を追加しましょう! Odoo では、XML ドキュメントを RN ファイル :dfn:`(Relax NG ファイル)`で記述し、検証します。
現在の文法を説明する RNG ファイルを追加します。
必須属性
image_field
。オプションの属性:
tooltip_field
。
すべてのビューがこの RNG ファイルに対して検証されていることを確認するコードを追加します。
ここでは、`image_field`と`tooltip_field`が現在のモデルのフィールドであることを確認しましょう。
RNGファイルを検証することは簡単ではないので、ここで助けるためのスニペットがあります:
# -*- coding: utf-8 -*-
import logging
import os
from lxml import etree
from odoo.loglevels import ustr
from odoo.tools import misc, view_validation
_logger = logging.getLogger(__name__)
_viewname_validator = None
@view_validation.validate('viewname')
def schema_viewname(arch, **kwargs):
""" Check the gallery view against its schema
:type arch: etree._Element
"""
global _viewname_validator
if _viewname_validator is None:
with misc.file_open(os.path.join('modulename', 'rng', 'viewname.rng')) as f:
_viewname_validator = etree.RelaxNG(etree.parse(f))
if _viewname_validator.validate(arch):
return True
for error in _viewname_validator.error_log:
_logger.error(ustr(error))
return False
13. 画像をアップロードしています¶
ギャラリービューでは、ユーザーが画像をアップロードすることはできません。実装しましょう。
`FileUploader`コンポーネントを使用して、各画像にボタンを追加します。
FileUploader
コンポーネントはonUploaded
プロパティを受け取ります。これはユーザーが画像をアップロードしたときに呼び出されます。 新しい画像をアップロードするには、必ずwebSave
を orm サービスから呼び出してください。画像がアップロードされていることに気づいたかもしれませんが、ブラウザで再レンダリングされていません。 これは、画像のリンクが変更されなかったので、ブラウザーはそれらを再フェッチしません。 画像の url に
write_date
を含めます。アップロードボタンをクリックすると、スイッチビューがトリガーされないことを確認してください。

14. 高度なツールチップテンプレート¶
今のところ、ツールチップフィールドのみを指定できます。しかし、特定のテンプレートを書くことを許可したい場合はどうでしょうか?
Example
これは、この演習の後に動作するはずのギャラリーアーチのビューの例です。
<record id="contacts_gallery_view" model="ir.ui.view">
<field name="name">awesome_gallery.orders.gallery</field>
<field name="model">res.partner</field>
<field name="arch" type="xml">
<gallery image_field="image_1920" tooltip_field="name">
<field name="email"/> <!-- Specify to the model that email should be fetched -->
<field name="name"/> <!-- Specify to the model that name should be fetched -->
<tooltip-template> <!-- Specify the owl template for the tooltip -->
<p class="m-0">name: <field name="name"/></p> <!-- field is compiled into a t-esc-->
<p class="m-0">e-mail: <field name="email"/></p>
</tooltip-template>
</gallery>
</field>
</record>
awesome_gallery/views/views.xml
のres.partner
ギャラリーアーチビューを上記の例のアーチに置き換えます。rng 検証に合格しない場合は心配しないでください。新しいアーチ構造を受け入れるようにギャラリー rng バリデータを変更します。
ちなみに
この rng スニペットを使用して、tooltip-template タグを検証できます。
<rng:define name="tooltip-template"> <rng:element name="tooltip-template"> <rng:zeroOrMore> <rng:text/> <rng:ref name="any"/> </rng:zeroOrMore> </rng:element> </rng:define> <rng:define name="any"> <rng:element> <rng:anyName/> <rng:zeroOrMore> <rng:choice> <rng:attribute> <rng:anyName/> </rng:attribute> <rng:text/> <rng:ref name="any"/> </rng:choice> </rng:zeroOrMore> </rng:element> </rng:define>
アーチパーサはフィールドとツールチップテンプレートを解析する必要があります。
@web/core/utils/xml
からvisitXML
をインポートし、フィールド名とツールチップテンプレートを解析します。解析されたフィールド名を仕様に含めることで、モデルが
webSearchRead
を呼び出していることを確認してください。レンダラー(または作成したサブコンポーネント)は、解析されたツールチップテンプレートを受け取る必要があります。 このテンプレートを操作して、`<field>`要素を`<t t-esc="x">`要素に置き換えます。
ちなみに
テンプレートは
Element
オブジェクトで、HTML 要素のように操作できます。@odoo/owl
のxml
関数のおかげで Owl にテンプレートを登録します。ツールチップを表示するには、 :file:`@web/core/tooltip/tooltip_hook`の`useTooltip`フックを使用します。 このフックは引数として Owl テンプレートとテンプレートに必要な変数を取ります。

関連項目
例: kaban で使用される useTooltip
Owl: xmlヘルパー関数 を持つインラインテンプレート