سرویس‌ها

سرویس‌ها قطعات کد طولانی‌عمری هستند که یک ویژگی را فراهم می‌کنند. آن‌ها ممکن است توسط کامپوننت‌ها (با useService) یا توسط سایر سرویس‌ها وارد شوند. همچنین می‌توانند مجموعه‌ای از وابستگی‌ها را اعلام کنند. از این لحاظ، سرویس‌ها اساساً یک سامانهٔ DI dependency injection هستند. برای مثال، سرویس notification راهی برای نمایش یک اعلان فراهم می‌کند، یا سرویس rpc راه درستی برای انجام یک درخواست به سرور Odoo است.

مثال زیر یک سرویس ساده را ثبت می‌کند که هر ۵ ثانیه یک اعلان نمایش می‌دهد:

import { registry } from "@web/core/registry";

const myService = {
    dependencies: ["notification"],
    start(env, { notification }) {
        let counter = 1;
        setInterval(() => {
            notification.add(`Tick Tock ${counter++}`);
        }, 5000);
    }
};

registry.category("services").add("myService", myService);

هنگام راه‌اندازی، کلاینت وب تمام سرویس‌های موجود در رجیستری services را شروع می‌کند. توجه کنید که نام مورد استفاده در رجیستری همان نام سرویس است.

توجه

بیشتر کدهایی که کامپوننت نیستند باید در یک سرویس بسته‌بندی شوند، به‌ویژه اگر اثر جانبی انجام می‌دهند. این برای اهداف تست بسیار مفید است: تست‌ها می‌توانند انتخاب کنند کدام سرویس‌ها فعال باشند، بنابراین شانس کمتری برای اثر جانبی ناخواسته در تداخل با کد در حال تست وجود دارد.

تعریف یک سرویس

یک سرویس باید رابط زیر را پیاده‌سازی کند:

dependencies

فهرست اختیاری از رشته‌ها. فهرست تمام وابستگی‌ها (سرویس‌های دیگر) که این سرویس به آن‌ها نیاز دارد

start(env, deps)
نشانوندها
  • env (Environment()) -- محیط برنامه

  • deps (Object()) -- تمام وابستگی‌های درخواست‌شده

بازگشت ها

مقدار سرویس یا Promise<مقدار سرویس>

این تعریف اصلی سرویس است. می‌تواند یک مقدار یا یک promise بازگرداند. در آن حالت، بارگذارندهٔ سرویس صرفاً منتظر می‌ماند تا promise به یک مقدار حل شود که سپس مقدار سرویس خواهد بود.

برخی سرویس‌ها هیچ مقداری صادر نمی‌کنند. آن‌ها ممکن است فقط کار خود را بدون نیاز به فراخوانی مستقیم توسط کد دیگر انجام دهند. در آن حالت، مقدار آن‌ها در env.services روی null تنظیم می‌شود.

async

مقدار اختیاری. در صورت ارائه، باید true یا فهرستی از رشته‌ها باشد.

برخی سرویس‌ها نیاز به ارائهٔ یک API ناهمگام دارند. برای مثال، سرویس rpc یک تابع ناهمگام است، یا سرویس orm مجموعه‌ای از توابع را برای فراخوانی سرور Odoo فراهم می‌کند.

در آن حالت، ممکن است کامپوننت‌هایی که از یک سرویس استفاده می‌کنند پیش از پایان فراخوانی تابع ناهمگام نابود شوند. بیشتر اوقات، فراخوانی تابع ناهمگام باید نادیده گرفته شود. در غیر این صورت کار به‌طور بالقوه بسیار پرخطر است، زیرا کامپوننت زیرین دیگر فعال نیست. پرچم async راهی برای انجام دقیقاً همین کار است: به سازندهٔ سرویس علامت می‌دهد که تمام فراخوانی‌های ناهمگام از کامپوننت‌ها در صورت نابودی کامپوننت باید معلق باقی بمانند.

استفاده از یک سرویس

سرویسی که به سرویس‌های دیگر وابسته است و dependencies خود را به‌درستی اعلام کرده، صرفاً یک ارجاع به سرویس‌های متناظر را در آرگومان دوم متد start دریافت می‌کند.

قلاب useService راه درست استفاده از یک سرویس در یک کامپوننت است. صرفاً یک ارجاع به مقدار سرویس بازمی‌گرداند که سپس می‌تواند توسط کامپوننت استفاده شود. برای مثال:

import { rpc } from "@web/core/network/rpc";

class MyComponent extends Component {
  setup() {
    onWillStart(async () => {
      const result = await rpc(...);
    })
  }
}

فهرست مرجع

نام فنی

توضیح کوتاه

cookie

خواندن یا تغییر cookieها

effect

display graphical effects

http

perform low level http calls

notification

display notifications

router

manage the browser url

rpc

send requests to the server

scroller

handle clicks on anchors elements

title

read or modify the window title

user

provides some information related to the current user

نمای کلی

  • Technical name: cookie

  • Dependencies: none

Provides a way to manipulate cookies. For example:

cookieService.setCookie("hello", "odoo");

API

current

Object representing each cookie and its value if any (or empty string)

setCookie(name[, value, ttl])
نشانوندها
  • name (string()) -- the name of the cookie that should be set

  • value (any()) -- optional. If given, the cookie will be set to that value

  • ttl (number()) -- optional. the time in seconds before the cookie will be deleted (default=1 year)

Sets the cookie name to the value value with a max age of ttl

deleteCookie(name)
نشانوندها
  • name (string()) -- name of the cookie

Deletes the cookie name.

Effect service

نمای کلی

  • Technical name: effect

  • Dependencies: None

Effects are graphical elements that can be temporarily displayed on top of the page, usually to provide feedback to the user that something interesting happened.

A good example would be the rainbow man:

The rainbow man effect

Here's how this can be displayed:

const effectService = useService("effect");
effectService.add({
  type: "rainbow_man", // can be omitted, default type is already "rainbow_man"
  message: "Boom! Team record for the past 30 days.",
});

هشدار

The hook useEffect is not related to the effect service.

API

effectService.add(options)
نشانوندها
  • options (object()) -- the options for the effect. They will get passed along to the underlying effect component.

Display an effect.

The options are defined by:

interface EffectOptions {
  // The name of the desired effect
  type?: string;
  [paramName: string]: any;
}

Available effects

Currently, the only effect is the rainbow man.

RainbowMan
effectService.add({ type: "rainbow_man" });

نام

نوع

توضیحات

params.Component

owl.Component?

Component class to instantiate inside the RainbowMan (will replace the message).

params.props

object?={}

If params.Component is given, its props can be passed with this argument.

params.message

string?="Well Done!"

Message is the notice the rainbowman holds.

If effects are disabled for the user, the rainbowman won't appear and a simple notification will get displayed as a fallback.

If effects are enabled and params.Component is given, params.message is not used.

The message is a simple string or a string representing html (prefer using params.Component if you want interactions in the DOM).

params.messageIsHtml

boolean?=false

Set to true if the message represents html, s.t. it will be correctly inserted into the DOM.

params.img_url

string?=/web/static/img/smile.svg

The url of the image to display inside the rainbow.

params.fadeout

("slow"|"medium"|"fast"|"no")?="medium"

Delay for rainbowman to disappear.

"fast" will make rainbowman dissapear quickly.

"medium" and "slow" will wait little longer before disappearing (can be used when params.message is longer).

"no" will keep rainbowman on screen until user clicks anywhere outside rainbowman.

How to add an effect

The effects are stored in a registry called effects. You can add new effects by providing a name and a function.

const effectRegistry = registry.category("effects");
effectRegistry.add("rainbow_man", rainbowManEffectFunction);

The function must follow this API:

<newEffectFunction>(env, params)
نشانوندها
  • env (Env()) -- the environment received by the service

  • params (object()) -- the params received from the add function on the service.

بازگشت ها

({Component, props} | void) A component and its props or nothing.

This function must create a component and return it. This component is mounted inside the effect component container.

مثال

Let's say we want to add an effect that add a sepia look at the page.

import { registry } from "@web/core/registry";
import { Component, xml } from "@odoo/owl";

class SepiaEffect extends Component {
  static template = xml`
    <div style="
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        pointer-events: none;
        background: rgba(124,87,0, 0.4);
    "></div>
  `;
}

export function sepiaEffectProvider(env, params = {}) {
    return {
        Component: SepiaEffect,
    };
}

const effectRegistry = registry.category("effects");
effectRegistry.add("sepia", sepiaEffectProvider);

And then, call it somewhere you want and you will see the result. Here, it is called in webclient.js to make it visible everywhere for the example.

const effectService = useService("effect");
effectService.add({ type: "sepia" });
Odoo in sepia

Http Service

نمای کلی

  • Technical name: http

  • Dependencies: None

While most interactions between the client and the server in odoo are RPCs (XMLHTTPRequest), lower level control on requests may sometimes be required.

This service provides a way to send get and post http requests.

API

async get(route[, readMethod = "json"])
نشانوندها
  • route (string()) -- the url to send the request to

  • readMethod (string()) -- the response content type. Can be "text", "json", "formData", "blob", "arrayBuffer".

بازگشت ها

the result of the request with the format defined by the readMethod argument.

Sends a get request.

async post(route[, params = {}, readMethod = "json"])
نشانوندها
  • route (string()) -- the url to send the request to

  • params (object()) -- key value data to be set in the form data part of the request

  • readMethod (string()) -- the response content type. Can be "text", "json", "formData", "blob", "arrayBuffer".

بازگشت ها

the result of the request with the format defined by the readMethod argument.

Sends a post request.

مثال

const httpService = useService("http");
const data = await httpService.get("https://something.com/posts/1");
// ...
await httpService.post("https://something.com/posts/1", { title: "new title", content: "new content" });

Notification service

نمای کلی

  • Technical name: notification

  • Dependencies: None

The notification service allows to display notifications on the screen.

const notificationService = useService("notification");
notificationService.add("I'm a very simple notification");

API

add(message[, options])
نشانوندها
  • message (string()) -- the notification message to display

  • options (object()) -- the options of the notification

بازگشت ها

a function to close the notification

Shows a notification.

The options are defined by:

نام

نوع

توضیحات

title

string

Add a title to the notification

type

warning | danger | success | info

Changes the background color according to the type

sticky

بولی

Whether or not the notification should stay until dismissed

className

string

additional css class that will be added to the notification

onClose

function

callback to be executed when the notification closes

buttons

button[] (see below)

list of button to display in the notification

autocloseDelay

عدد

duration in milliseconds before the notification is closed automatically

The buttons are defined by:

نام

نوع

توضیحات

name

string

The button text

onClick

function

callback to execute when the button is clicked

primary

بولی

whether the button should be styled as a primary button

مثال‌ها

A notification for when a sale deal is made with a button to go some kind of commission page.

// in setup
this.notificationService = useService("notification");
this.actionService = useService("action");

// later
this.notificationService.add("You closed a deal!", {
  title: "Congrats",
  type: "success",
  buttons: [
      {
          name: "See your Commission",
          onClick: () => {
              this.actionService.doAction("commission_action");
          },
      },
  ],
});
Example of notification

A notification that closes after a second:

const notificationService = useService("notification");
const close = notificationService.add("I will be quickly closed");
setTimeout(close, 1000);

Router Service

نمای کلی

  • Technical name: router

  • Dependencies: none

The router service provides three features:

  • information about the current route

  • a way for the application to update the url, depending on its state

  • listens to every hash change, and notifies the rest of the application

API

current

The current route can be accessed with the current key. It is an object with the following information:

  • pathname (string): the path for the current location (most likely /web )

  • search (object): a dictionary mapping each search keyword (the querystring) from the url to its value. An empty string is the value if no value was explicitely given

  • hash (object): same as above, but for values described in the hash.

برای مثال:

// url = /web?debug=assets#action=123&owl&menu_id=174
const { pathname, search, hash } = env.services.router.current;
console.log(pathname); //   /web
console.log(search); //   { debug="assets" }
console.log(hash); //   { action:123, owl: "", menu_id: 174 }

Updating the URL is done with the pushState method:

pushState(hash: object[, replace?: boolean])
نشانوندها
  • hash (Object()) -- object containing a mapping from some keys to some values

  • replace (boolean()) -- if true, the url will be replaced, otherwise only key/value pairs from the hash will be updated.

Updates the URL with each key/value pair from the hash object. If a value is set to an empty string, the key is added to the url without any corresponding value.

If true, the replace argument tells the router that the url hash should be completely replaced (so values not present in the hash object will be removed).

This method call does not reload the page. It also does not trigger a hashchange event, nor a ROUTE_CHANGE in the main bus. This is because this method is intended to only updates the url. The code calling this method has the responsibility to make sure that the screen is updated as well.

برای مثال:

// url = /web#action_id=123
routerService.pushState({ menu_id: 321 });
// url is now /web#action_id=123&menu_id=321
routerService.pushState({ yipyip: "" }, replace: true);
// url is now /web#yipyip

Finally, the redirect method will redirect the browser to a specified url:

redirect(url[, wait])
نشانوندها
  • url (string()) -- a valid url

  • wait (boolean()) -- اگر true باشد، تا آماده شدن سرور صبر کرده و سپس تغییر مسیر می‌دهد.

مرورگر را به url هدایت می‌کند. این متد صفحه را بارگذاری مجدد می‌کند. آرگومان wait به‌ندرت استفاده می‌شود: در مواردی مفید است که می‌دانیم سرور برای مدت کوتاهی در دسترس نخواهد بود، معمولاً درست پس از یک عملیات به‌روزرسانی یا نصب افزونه.

توجه

سرویس مسیریاب هر زمان که مسیر فعلی تغییر کند، یک رویداد ROUTE_CHANGE را روی گذرگاه اصلی منتشر می‌کند.

سرویس RPC

نمای کلی

  • نام فنی: rpc

  • Dependencies: none

سرویس rpc یک تابع ناهمگام منفرد برای ارسال درخواست‌ها به سرور فراهم می‌کند. فراخوانی یک کنترل‌کننده بسیار ساده است: مسیر باید آرگومان اول باشد و به‌صورت اختیاری، یک شیء params می‌تواند به‌عنوان آرگومان دوم داده شود.

import { rpc } from "@web/core/network/rpc";

 // somewhere else, in an async function:
 const result = await rpc("/my/route", { some: "value" });

توجه

توجه داشته باشید که سرویس rpc یک سرویس سطح پایین محسوب می‌شود. تنها باید برای تعامل با کنترل‌کننده‌های Odoo استفاده شود. برای کار با مدل‌ها (که به‌مراتب مهم‌ترین مورد کاربرد است)، باید به‌جای آن از سرویس orm استفاده شود.

API

rpc(route, params, settings)
نشانوندها
  • route (string()) -- مسیر هدف‌گرفته‌شده توسط درخواست

  • params (Object()) -- (اختیاری) پارامترهای ارسال‌شده به سرور

  • settings (Object()) -- (اختیاری) تنظیمات درخواست (به ادامه مراجعه کنید)

شیء settings می‌تواند شامل موارد زیر باشد:

  • xhr که باید یک شیء XMLHTTPRequest باشد. در این حالت، متد rpc به‌جای ایجاد یک شیء جدید، صرفاً از همان استفاده می‌کند. این مورد زمانی مفید است که به ویژگی‌های پیشرفته API XMLHTTPRequest دسترسی پیدا می‌کنید.

  • silent (boolean) در صورت تنظیم روی true، وب‌کلاینت بازخوردی مبنی بر وجود یک rpc در انتظار ارائه نخواهد داد.

سرویس rpc با استفاده از یک شیء XMLHTTPRequest با سرور ارتباط برقرار می‌کند که برای کار با نوع محتوای application/json پیکربندی شده است. بنابراین واضح است که محتوای درخواست باید قابل سریال‌سازی به JSON باشد. هر درخواستی که این سرویس انجام می‌دهد از متد HTTP POST استفاده می‌کند.

خطاهای سرور درواقع پاسخ را با کد HTTP 200 برمی‌گردانند. اما سرویس rpc آنها را به‌عنوان خطا تلقی می‌کند.

مدیریت خطا

یک rpc می‌تواند به دو دلیل اصلی ناموفق باشد:

  • یا سرور Odoo یک خطا برمی‌گرداند (ما به این مورد یک خطای server می‌گوییم). در این حالت، درخواست HTTP با کد HTTP 200 برمی‌گردد اما با یک شیء پاسخ شامل کلید error.

  • یا نوع دیگری از خطای شبکه رخ داده است

زمانی که یک rpc ناموفق باشد:

  • promise نمایانگر rpc رد می‌شود، بنابراین کد فراخواننده از کار می‌افتد، مگر اینکه وضعیت را مدیریت کند

  • یک رویداد RPC_ERROR روی گذرگاه اپلیکیشن اصلی فعال می‌شود. payload رویداد شامل توصیفی از علت خطا است:

    اگر یک خطای سرور باشد (کد سرور یک استثنا پرتاب کرده است). در این حالت payload رویداد یک شیء با کلیدهای زیر خواهد بود:

    • type = 'server'

    • message(string)

    • code(number)

    • name(string) (اختیاری، توسط سرویس خطا برای یافتن یک دیالوگ مناسب جهت استفاده هنگام مدیریت خطا به کار می‌رود)

    • subType(string) (اختیاری، اغلب برای تعیین عنوان دیالوگ استفاده می‌شود)

    • data(object) (شیء اختیاری که می‌تواند شامل کلیدهای مختلفی باشد که در میان آنها debug قرار دارد: اطلاعات اصلی اشکال‌زدایی، همراه با پشتهٔ فراخوانی)

    اگر یک خطای شبکه باشد، توصیف خطا صرفاً شیء {type: 'network'} است. هنگام بروز خطای شبکه، یک اعلان نمایش داده می‌شود و به‌طور منظم تا زمانی که سرور پاسخ دهد با آن تماس گرفته می‌شود. اعلان به محض پاسخ‌دهی سرور بسته می‌شود.

سرویس Scroller

نمای کلی

  • نام فنی: scroller

  • Dependencies: none

هر زمان که کاربر روی یک anchor در وب‌کلاینت کلیک کند، این سرویس به‌طور خودکار به سمت هدف اسکرول می‌کند (در صورت مناسب بودن).

این سرویس یک event listener اضافه می‌کند تا click`‌ها روی document را دریافت کند. سرویس بررسی می‌کند که آیا selector موجود در ویژگی href آن معتبر است یا خیر تا anchorها و اکشن‌های Odoo را تشخیص دهد (مثلاً `<a href="#target_element"></a>). اگر چنین نباشد، کاری انجام نمی‌دهد.

اگر به‌نظر برسد کلیک به یک عنصر هدف‌گیری شده، یک رویداد SCROLLER:ANCHOR_LINK_CLICKED روی گذرگاه اصلی اپلیکیشن فعال می‌شود. این رویداد شامل یک رویداد سفارشی است که element منطبق و id آن را به‌عنوان مرجع در بر دارد. این کار ممکن است به سایر بخش‌ها اجازه دهد رفتاری مرتبط با خود anchorها را مدیریت کنند. رویداد اصلی نیز ارائه می‌شود چرا که ممکن است نیاز به جلوگیری از آن باشد. اگر از رویداد جلوگیری نشود، رابط کاربری به عنصر هدف اسکرول می‌کند.

API

مقادیر زیر در رویداد سفارشی anchor-link-clicked که در بالا توضیح داده شد قرار دارند.

نام

نوع

توضیحات

element

HTMLElement | null

عنصر anchor که توسط href هدف قرار گرفته است

id

string

id موجود در href

originalEv

Event

رویداد کلیک اصلی

توجه

سرویس scroller یک رویداد SCROLLER:ANCHOR_LINK_CLICKED را روی گذرگاه اصلی منتشر می‌کند. برای جلوگیری از رفتار اسکرول پیش‌فرض سرویس scroller، باید از preventDefault() روی رویداد ارسال‌شده به listener استفاده کنید تا بتوانید رفتار خود را به‌درستی از داخل listener پیاده‌سازی نمایید.

سرویس Title

نمای کلی

  • نام فنی: title

  • Dependencies: none

سرویس title یک API ساده ارائه می‌دهد که اجازهٔ خواندن/تغییر عنوان document را می‌دهد. به‌عنوان مثال، اگر عنوان document فعلی "Odoo" باشد، می‌توانیم با استفاده از دستور زیر آن را به "Odoo 15 - Apple" تغییر دهیم:

// in some component setup method
const titleService = useService("title");

titleService.setParts({ odoo: "Odoo 15", fruit: "Apple" });

API

سرویس title رابط زیر را دستکاری می‌کند:

interface Parts {
    [key: string]: string | null;
}

هر کلید نشان‌دهندهٔ هویت بخشی از عنوان است و هر مقدار رشته‌ای است که نمایش داده می‌شود، یا null اگر حذف شده باشد.

API آن این‌گونه است:

current

این یک رشته است که نشان‌دهندهٔ عنوان فعلی است. به شیوهٔ زیر ساختاربندی شده است: value_1 - ... - value_n که هر value_i یک مقدار (غیر null) موجود در شیء Parts است (که توسط تابع getParts بازگردانده می‌شود)

getParts()
بازگشت ها

Parts شیء Parts فعلی که توسط سرویس title نگهداری می‌شود

setParts(parts)
نشانوندها
  • parts (Parts()) -- شیء نشان‌دهندهٔ تغییر موردنیاز

متد setParts اجازه می‌دهد چندین بخش از عنوان را افزود/جایگزین/حذف کرد. حذف یک بخش (یک مقدار) با تنظیم مقدار کلید مرتبط روی null انجام می‌شود.

توجه داشته باشید که می‌توان تنها یک بخش را بدون تأثیرگذاری بر سایر بخش‌ها تغییر داد. به‌عنوان مثال، اگر عنوان از بخش‌های زیر تشکیل شده باشد:

{ odoo: "Odoo", action: "Import" }

با مقدار current برابر با Odoo - Import، آنگاه

setParts({
  action: null,
});

عنوان را به Odoo تغییر خواهد داد.

سرویس User

نمای کلی

  • نام فنی: user

  • وابستگی‌ها: rpc

سرویس user مجموعه‌ای از داده‌ها و چند تابع کمکی مرتبط با کاربر متصل را فراهم می‌کند.

API

نام

نوع

توضیحات

context

Object

کانتکست کاربر

db

Object

اطلاعات دربارهٔ پایگاه داده

home_action_id

(number | false)

شناسهٔ اکشن استفاده‌شده به‌عنوان صفحهٔ اصلی برای کاربر

isAdmin

boolean

اینکه آیا کاربر یک مدیر است (گروه base.group_erp_manager یا کاربر فوقانی)

isSystem

boolean

اینکه آیا کاربر بخشی از گروه سیستمی (base.group_system) است

lang

string

زبان مورد استفاده

name

string

نام کاربر

partnerId

number

شناسهٔ نمونهٔ شریک مربوط به کاربر

tz

string

منطقهٔ زمانی کاربر

userId

number

شناسهٔ کاربر

userName

string

نام مستعار جایگزین کاربر

updateContext(update)
نشانوندها
  • update (object()) -- شیئی که کانتکست با آن به‌روزرسانی می‌شود

کانتکست کاربر را با شیء داده‌شده به‌روزرسانی می‌کند.

userService.updateContext({ isFriend: true })
removeFromContext(key)
نشانوندها
  • key (string()) -- کلید ویژگی هدف‌گرفته‌شده

مقدار را با کلید داده‌شده از کانتکست کاربر حذف می‌کند

userService.removeFromContext("isFriend")
hasGroup(group)
نشانوندها
  • group (string()) -- xml_id گروهی که باید جست‌وجو شود

بازگشت ها

Promise<boolean> آیا کاربر در گروه قرار دارد

بررسی می‌کند که آیا کاربر بخشی از یک گروه است یا خیر

const isInSalesGroup = await userService.hasGroup("sale.group_sales")