1. ハローワールドビューを作る

最初のステップは、単純なコンポーネントで JavaScript の実装を作成することです。

  1. gallery_view.jsgallery_controller.jsgallery_controller.xml ファイルを static/src に作成します。

  2. gallery_controller.js に単純な hello world コンポーネントを実装します。

  3. 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);
    
  4. contacts.action_contacts アクションのビュータイプに gallery を追加します。

  5. ギャラリービューに切り替える際に、Helloワールドコンポーネントが表示されていることを確認してください。

../../../_images/view_button.png ../../../_images/new_view.png

2. レイアウトコンポーネントを使用

これまでのところ、ギャラリービューは標準ビューのようには見えません。他のビューのような標準機能を持つために Layout コンポーネントを使用しましょう。

  1. Layout コンポーネントをインポートし、 GalleryControllercomponents に追加します。

  2. Layout を使用するためにテンプレートを更新します。display propが必要です。props.display` で見つけることができます。

../../../_images/layout.png

3. アーチを解析

今のところ, 私たちのギャラリービューはあまりありません. ビューのアーチに含まれている情報を読むことから始めましょう.

アーチを解析するプロセスは通常、各ビューに固有の ArchParser で行われます。一般的な XMLParser クラスを継承します。

Example

ArchParserがどのように見えるかを以下に示します。

export class MyCustomArchParser {
    parse(xmlDoc) {
       const myAttribute = xmlDoc.getAttribute("my_attribute")
       return {
           myAttribute,
       }
    }
}
  1. 独自のファイルに ArchParser クラスを作成します。

  2. `image_field`の情報を読むのに使います。

  3. 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,
    }
})
  1. GalleryControllerloadImages(domain) {...} メソッドを追加します。 これは、ドメインに対応するレコードを取得するために、ormサービスから`webSearchRead`コールを実行し、propsで受け取った`imageField`を使用する必要があります。

  2. 呼び出しのコンテキストに bin_size を含まない場合、base64 でエンコードされた画像フィールドを受け取ります。 画像フィールドのサイズを受け取るには、コンテキストに bin_size を入れてください。後で画像を表示します。

  3. `setup`コードを変更して、`onWillStart`と`onWillUpdateProps`フックでそのメソッドを呼び出します。

  4. テンプレートを変更して、Layout コンポーネントのデフォルトスロット内の各イメージの id とサイズを表示します。

注釈

ロードデータコードは、次の課題で適切なモデルに移動されます。

../../../_images/gallery_data.png

5. 並行問題を解決

今のところ、私たちのコードは並列化されていません。ドメインを2回変更すると、`loadImages(domain)`が2回トリガーされます。 このように、異なる要因によって異なる時期に到達できるリクエストが二つあります。 2番目のリクエストに対するレスポンスを受信した後、最初のリクエストに対するレスポンスを受信すると、一貫性のない状態になります。

Odoo の KeepLast プリミティブはこの問題を解決し、タスクのリストを管理し、最後のタスクだけをアクティブにします。

  1. :file:`@web/core/utils/concurrency`から`KeepLast`をインポートします。

  2. モデルに KeepLast オブジェクトをインスタンス化します。

  3. KeepLast に`webSearchRead` を追加して、最後の呼び出しのみが解決されます。

6. コードを再編成する

実際の見解はもう少し整理されています。 この例ではやり過ぎかもしれませんが、Odoo でコードを構造化する方法を学ぶことを目的としています。 また、これは変化する要件でより良いスケールになります。

  1. すべてのモデルコードを GalleryModel クラスに移動します。

  2. レンダリングコードを GalleryRenderer コンポーネント内に移動します。

  3. `GalleryController`に`GalleryModel`と`GalleryRenderer`をインポートして動作させます。

7. ビューを拡張可能にする

ビューを拡張するために、ギャラリービューオブジェクトをインポートして好みに変更することができます。 問題は、現時点では、コントローラ内でハードコードされているため、カスタムモデルやレンダラーを定義することはできないということです。

  1. ギャラリービューファイルに GalleryModelGalleryRenderer をインポートします。

  2. ギャラリービューオブジェクトに`Model`と`Renderer`キーを追加し、`GalleryModel`と`GalleryRenderer`に割り当てます。 `Model`と`Renderer`をpropsとしてコントローラに渡します。

  3. コントローラ内のハードコードされたインポートを削除し、props から取得します。

  4. 動的サブコンポーネントを持つには 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,
});
../../../_images/tshirt_images.png

9. クリックするとフォームビューに切り替える

レンダラーを更新して、画像上のクリックに反応し、フォームビューに切り替えます。 アクションサービスの switchView 関数を使用できます。

10. オプションのツールチップを追加

マウスホバーに関する追加情報を持つことが便利です。

  1. コードを更新して、アーチのオプションの追加属性を許可します。

    <gallery image_field="some_field" tooltip_field="some_other_field"/>
    
  2. マウスホバーでは、ツールチップフィールドの内容を表示します。 フィールドが文字フィールド、数値フィールド、または many2one フィールドである場合には動作します。 html要素にツールチップを置くには、要素の`data-tooltip`属性に文字列を入れます。

  3. 顧客をツールチップフィールドとして追加するには、顧客ギャラリービューアーチを更新します。

../../../_images/image_tooltip.png

関連項目

例: t-att-data-tooltip の使用

11. ページネーションを追加

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

../../../_images/pagination.png

12. ビューの検証

これまでのところ、私たちは素晴らしいと便利なビューを持っています。 しかし実際の生活では Galleryビューの「arch」をエンコードするユーザーに問題があるかもしれません。現在は構造化されていないXMLだけです。

検証を追加しましょう! Odoo では、XML ドキュメントを RN ファイル :dfn:`(Relax NG ファイル)`で記述し、検証します。

  1. 現在の文法を説明する RNG ファイルを追加します。

    • 必須属性 image_field

    • オプションの属性: tooltip_field

  2. すべてのビューがこの RNG ファイルに対して検証されていることを確認するコードを追加します。

  3. ここでは、`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. 画像をアップロードしています

ギャラリービューでは、ユーザーが画像をアップロードすることはできません。実装しましょう。

  1. `FileUploader`コンポーネントを使用して、各画像にボタンを追加します。

  2. FileUploader コンポーネントは onUploaded プロパティを受け取ります。これはユーザーが画像をアップロードしたときに呼び出されます。 新しい画像をアップロードするには、必ず webSave を orm サービスから呼び出してください。

  3. 画像がアップロードされていることに気づいたかもしれませんが、ブラウザで再レンダリングされていません。 これは、画像のリンクが変更されなかったので、ブラウザーはそれらを再フェッチしません。 画像の url に write_date を含めます。

  4. アップロードボタンをクリックすると、スイッチビューがトリガーされないことを確認してください。

../../../_images/upload_image.png

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>
  1. awesome_gallery/views/views.xmlres.partner ギャラリーアーチビューを上記の例のアーチに置き換えます。rng 検証に合格しない場合は心配しないでください。

  2. 新しいアーチ構造を受け入れるようにギャラリー 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>
    
  3. アーチパーサはフィールドとツールチップテンプレートを解析する必要があります。 @web/core/utils/xml から visitXML をインポートし、フィールド名とツールチップテンプレートを解析します。

  4. 解析されたフィールド名を仕様に含めることで、モデルが webSearchRead を呼び出していることを確認してください。

  5. レンダラー(または作成したサブコンポーネント)は、解析されたツールチップテンプレートを受け取る必要があります。 このテンプレートを操作して、`<field>`要素を`<t t-esc="x">`要素に置き換えます。

    ちなみに

    テンプレートは Element オブジェクトで、HTML 要素のように操作できます。

  6. @odoo/owlxml 関数のおかげで Owl にテンプレートを登録します。

  7. ツールチップを表示するには、 :file:`@web/core/tooltip/tooltip_hook`の`useTooltip`フックを使用します。 このフックは引数として Owl テンプレートとテンプレートに必要な変数を取ります。

../../../_images/advanced_tooltip.png

関連項目