Послуги¶
Сервіси – це довговічні фрагменти коду, які надають певну функцію. Вони можуть бути імпортовані компонентами (за допомогою useService) або іншими сервісами. Також вони можуть оголошувати набір залежностей. У цьому сенсі сервіси – це, по суті, система DI впровадження залежностей. Наприклад, сервіс notification надає спосіб відображення сповіщення, або сервіс rpc – це правильний спосіб виконання запиту до сервера Odoo.
У наступному прикладі реєструється простий сервіс, який відображає сповіщення кожні 5 секунд:
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<value of service>
Це основне визначення сервісу. Він може повертати або значення, або обіцянку. У такому випадку завантажувач сервісу просто чекає, поки обіцянка перетвориться на значення, яке потім є значенням сервісу.
Деякі сервіси не експортують жодних значень. Вони можуть просто виконувати свою роботу без потреби безпосереднього виклику іншим кодом. У такому випадку їхнє значення буде встановлено на
nullуenv.services.
- async¶
Необов’язкове значення. Якщо вказано, воно має бути
trueабо списком рядків.Деяким сервісам потрібно надавати асинхронний API. Наприклад, сервіс
rpcє асинхронною функцією, або сервісormнадає набір функцій для виклику сервера Odoo.У такому випадку компоненти, що використовують сервіс, можуть бути знищені до завершення асинхронного виклику функції. У більшості випадків асинхронний виклик функції потрібно ігнорувати. В іншому випадку це потенційно дуже ризиковано, оскільки базовий компонент більше не активний. Прапорець
async- це спосіб зробити саме це: він сигналізує творцю сервісу, що всі асинхронні виклики, що надходять від компонентів, слід залишити в очікуванні, якщо компонент знищено.
Використання сервісу¶
Сервіс, який залежить від інших сервісів і правильно оголосив свої dependencies, просто отримує посилання на відповідні сервіси у другому аргументі методу start.
Hook useService - це правильний спосіб використання сервісу в компоненті. Він просто повертає посилання на значення сервісу, яке потім може бути використане компонентом. Наприклад:
import { useService } from "@web/core/utils/hooks";
class MyComponent extends Component {
setup() {
const rpc = useService("rpc");
onWillStart(async () => {
this.someValue = await rpc(...);
});
}
}
Список посилань¶
Технічна назва |
Короткий опис |
|---|---|
читати або змінювати файли cookie |
|
відображення графічних ефектів |
|
виконувати низькорівневі http-виклики |
|
відображення сповіщень |
|
керувати URL-адресою браузера |
|
надсилати запити на сервер |
|
обробляти кліки на елементах прив’язки |
|
читати або змінювати заголовок вікна |
|
надає деяку інформацію, пов’язану з поточним користувачем |
Огляд¶
Технічна назва:
cookieЗалежності: немає
Надає спосіб маніпулювання файлами cookie. Наприклад:
cookieService.setCookie("hello", "odoo");
API¶
- current¶
Об’єкт, що представляє кожен файл cookie та його значення, якщо таке є (або порожній рядок).
- setCookie(name[, value, ttl])¶
- Аргументи
name (
string()) – назва файлу cookie, який слід встановитиvalue (
any()) – необов’язково. Якщо вказано, значення cookie буде встановлено на це значенняttl (
number()) – необов’язково. час у секундах до видалення файлу cookie (за замовчуванням = 1 рік)
Встановлює значення cookie
nameнаvalueз максимальним вікомttl.
- deleteCookie(name)¶
- Аргументи
name (
string()) – назва файлу cookie
Видаляє файл cookie
name.
Послуги з ефектами¶
Огляд¶
Технічна назва:
effectЗалежності: Немає
Ефекти – це графічні елементи, які можна тимчасово відображати у верхній частині сторінки, зазвичай для того, щоб повідомити користувачеві про те, що сталося щось цікаве.
Гарним прикладом може бути райдужною людиною:
Ось як це можна відобразити:
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.",
});
Попередження
Hook useEffect не пов’язаний зі сервісом ефектів.
API¶
- effectService.add(options)¶
- Аргументи
options (
object()) – параметри ефекту. Вони будуть передані до базового компонента ефекту.
Відобразити ефект.
Опції визначаються:
interface EffectOptions {
// The name of the desired effect
type?: string;
[paramName: string]: any;
}
Доступні ефекти¶
Наразі єдиним ефектом є райдужна людина.
RainbowMan¶
effectService.add({ type: "rainbow_man" });
Name |
Тип |
Опис |
|---|---|---|
|
|
Клас компонента для створення екземпляра всередині RainbowMan (замінить повідомлення). |
|
|
Якщо задано params.Component, його властивості можна передати з цим аргументом. |
|
|
Повідомлення – це повідомлення, яке тримає в руках райдужна людина. Якщо ефекти для користувача вимкнено, райдужна людина не з’явиться, а як резервний варіант відобразиться просте сповіщення. Якщо ефекти ввімкнено та задано params.Component, params.message не використовується. Повідомлення - це простий рядок або рядок, що представляє html (бажано використовувати params.Component, якщо вам потрібна взаємодія в DOM). |
|
|
Встановіть значення true, якщо повідомлення являє собою html, тобто воно буде коректно вставлено в DOM. |
|
|
URL-адреса зображення, яке потрібно відобразити всередині веселки. |
|
|
Затримка зникнення райдужної людини.
|
Як додати ефект¶
Ефекти зберігаються в реєстрі під назвою effects. Ви можете додавати нові ефекти, вказавши назву та функцію.
const effectRegistry = registry.category("effects");
effectRegistry.add("rainbow_man", rainbowManEffectFunction);
Функція повинна відповідати цьому API:
- <newEffectFunction>(env, params)¶
- Аргументи
env (
Env()) – середовище, отримане сервісомparams (
object()) – параметри, отримані від функції додавання на сервісі.
- Повертає
({Component, props} | void)Компонент та його властивості або нічого.
Ця функція повинна створити компонент і повернути його. Цей компонент монтується всередині контейнера компонента ефекту.
Приклад¶
Припустимо, ми хочемо додати ефект, який додасть сторінці сепії.
/** @odoo-module **/
import { registry } from "@web/core/registry";
const { Component, tags } = owl;
class SepiaEffect extends Component {}
SepiaEffect.template = tags.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);
А потім викличте його де завгодно, і ви побачите результат. Тут, для прикладу, він викликається у webclient.js, щоб зробити його видимим скрізь.
const effectService = useService("effect");
effectService.add({ type: "sepia" });
Http-сервіс¶
Огляд¶
Технічна назва:
httpЗалежності: Немає
Хоча більшість взаємодій між клієнтом і сервером в odoo є RPC (XMLHTTPRequest), іноді може знадобитися контроль нижчого рівня над запитами.
Цей сервіс надає спосіб надсилання http-запитів get та post <https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods>`_.
API¶
- async get(route[, readMethod = "json"])¶
- Аргументи
route (
string()) – URL-адреса для надсилання запитуreadMethod (
string()) – тип вмісту відповіді. Може бути «text», «json», «formData», «blob», «arrayBuffer».
- Повертає
результат запиту у форматі, визначеному аргументом readMethod.
Надсилає запит на отримання.
- async post(route[, params = {}, readMethod = "json"])¶
- Аргументи
route (
string()) – URL-адреса для надсилання запитуparams (
object()) – дані ключа-значення, які потрібно встановити в частині даних форми запитуreadMethod (
string()) – тип вмісту відповіді. Може бути «text», «json», «formData», «blob», «arrayBuffer».
- Повертає
результат запиту у форматі, визначеному аргументом readMethod.
Надсилає запит на публікацію.
Приклад¶
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 дозволяє відображати сповіщення на екрані.
const notificationService = useService("notification");
notificationService.add("I'm a very simple notification");
API¶
- add(message[, options])¶
- Аргументи
message (
string()) – повідомлення сповіщення, яке має відображатисяoptions (
object()) – опції сповіщення
- Повертає
функція для закриття сповіщення
Показує сповіщення.
Опції визначаються:
Name
Тип
Опис
titlestring
Додатии заголовок до сповіщення
typewarning|danger|success|infoЗмінює колір фону відповідно до типу
stickyboolean
Чи має сповіщення залишатися активним до його відхилення
classNamestring
додатковий клас CSS, який буде додано до сповіщення
onClosefunction
зворотний виклик, який буде виконано після закриття сповіщення
buttonsbutton[] (див. нижче)
список кнопок для відображення в сповіщенні
Кнопки визначаються:
Name
Тип
Опис
namestring
Текст кнопки
onClickfunction
зворотний виклик для виконання при натисканні кнопки
primaryboolean
чи слід стилізувати кнопку як основну кнопку
Приклади¶
Сповіщення про укладення угоди купівлі-продажу з кнопкою переходу на певну сторінку комісійних.
// 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");
},
},
],
});
Сповіщення, яке закривається через секунду:
const notificationService = useService("notification");
const close = notificationService.add("I will be quickly closed");
setTimeout(close, 1000);
Router Service¶
Огляд¶
Технічна назва:
routerЗалежності: немає
Служба router надає три функції:
інформація про поточний маршрут
спосіб оновлення URL-адреси застосунком залежно від її стану
прослуховує кожну зміну хешу та повідомляє решту додатку
API¶
- current
До поточного маршруту можна отримати доступ за допомогою ключа
current. Це об’єкт з такою інформацією:pathname (string): шлях до поточного розташування (найімовірніше/web)search (object): словник, що відображає кожне ключове слово пошуку (рядок запиту) з URL-адреси на його значення. Пустий рядок є значенням, якщо значення не було вказано явно.хеш (об'єкт): те саме, що й вище, але для значень, описаних у хеші.
Наприклад:
// 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 }
Оновлення URL-адреси виконується за допомогою методу pushState:
- pushState(hash: object[, replace?: boolean])¶
- Аргументи
hash (
Object()) – об’єкт, що містить відображення деяких ключів у деякі значенняreplace (
boolean()) – якщо значення true, URL-адреса буде замінена, інакше оновлюватимуться лише пари ключ/значення зhash.
Оновлює URL-адресу кожною парою ключ/значення з об’єкта
hash. Якщо значення встановлено як порожній рядок, ключ додається до URL-адреси без відповідного значення.Якщо значення true, аргумент
replaceповідомляє маршрутизатору, що хеш URL-адреси слід повністю замінити (тому значення, яких немає в об’єктіhash, будуть видалені).Цей виклик методу не перезавантажує сторінку. Він також не ініціює подію
hashchange, а такожROUTE_CHANGEв main bus. Це тому, що цей метод призначений лише для оновлення URL-адреси. Код, який викликає цей метод, відповідає за оновлення екрана.
Наприклад:
// 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
Нарешті, метод redirect перенаправить браузер на вказану URL-адресу:
- redirect(url[, wait])¶
- Аргументи
url (
string()) – дійсна URL-адресаwait (
boolean()) – якщо значення true, чекати готовності сервера та перенаправляти після цього
Перенаправити браузер на
url. Цей метод перезавантажує сторінку. Аргументwaitвикористовується рідко: він корисний у деяких випадках, коли ми знаємо, що сервер буде недоступний протягом короткого періоду, зазвичай одразу після оновлення або встановлення доповнення.
Примітка
Служба router генерує подію ROUTE_CHANGE на main bus щоразу, коли поточний маршрут змінюється.
Служба RPC¶
Огляд¶
Технічна назва:
rpcЗалежності: немає
Служба rpc надає одну асинхронну функцію для надсилання запитів на сервер. Виклик контролера дуже простий: маршрут має бути першим аргументом, а за бажанням об’єкт params можна вказати як другий аргумент.
// in setup
this.rpc = useService("rpc");
// somewhere else, in an async function:
const result = await this.rpc("/my/route", { some: "value" });
Примітка
Зверніть увагу, що сервіс rpc вважається низькорівневим сервісом. Його слід використовувати лише для взаємодії з контролерами Odoo. Для роботи з моделями (що є найважливішим випадком використання) слід використовувати сервіс orm.
API¶
- rpc(route, params, settings)¶
- Аргументи
route (
string()) – маршрут, на який спрямований запитparams (
Object()) – (необов’язково) параметри, що надсилаються на серверsettings (
Object()) – (необов’язково) налаштування запиту (див. нижче)
Об’єкт
settingsможе містити:xhr, який має бути об’єктомXMLHTTPRequest. У такому випадку методrpcпросто використовуватиме його замість створення нового. Це корисно, коли хтось отримує доступ до розширених функцій APIXMLHTTPRequest.silent (boolean)Якщо встановлено значенняtrue, веб-клієнт не надаватиме зворотний зв’язок про наявність очікуваного rpc.
Служба rpc взаємодіє з сервером за допомогою об’єкта XMLHTTPRequest, налаштованого для роботи з типом вмісту application/json. Отже, очевидно, що вміст запиту має бути серіалізованим у форматі JSON. Кожен запит, виконаний цією службою, використовує метод http POST.
Помилки сервера насправді повертають відповідь з http-кодом 200. Але служба rpc трактуватиме їх як помилку.
Обробка помилок¶
RPC може вийти з ладу з двох основних причин:
або сервер odoo повертає помилку (тому ми називаємо це помилкою
сервера). У такому випадку http-запит поверне код http 200, АЛЕ з об’єктом відповіді, що містить ключerror.або є якась інша помилка мережі
Коли RPC не працює, тоді:
promise, що представляє rpc, відхилено, тому викликаючий код аварійно завершить роботу, якщо він не обробить ситуацію.
на головній шині програми спрацьовує подія
RPC_ERROR. Корисне навантаження події містить опис причини помилки:Якщо це помилка сервера (код сервера видав виняток), у такому випадку корисним навантаженням події буде об’єкт з такими ключами:
type = 'server'message(string)code(number)name(string)(необов’язково, використовується службою помилок для пошуку відповідного діалогового вікна для використання під час обробки помилки)subType(string)(необов’язково, часто використовується для визначення заголовка діалогового вікна)data(object)(необов’язковий об’єкт, який може містити різні ключі, серед якихdebug: основна налагоджувальна інформація зі стеком викликів)
Якщо це мережева помилка, то опис помилки – це просто об’єкт
{type: 'network'}. Коли виникає мережева помилка, відображається notification, і сервер регулярно зв’язується з ним, доки він не відповість. Сповіщення закривається, щойно сервер відповідає.
Scroller service¶
Огляд¶
Технічна назва:
scrollerЗалежності: немає
Щоразу, коли користувач натискає на якір у веб-клієнті, цей сервіс автоматично прокручує до цілі (якщо це доречно).
Сервіс додає слухач подій для отримання click на документі. Сервіс перевіряє, чи селектор, що міститься в його атрибуті href, є дійсним для розрізнення якорів та дій Odoo (наприклад, <a href="#target_element"></a>). Він нічого не робить, якщо це не так.
Подія SCROLLER:ANCHOR_LINK_CLICKED спрацьовує на головній шині застосунку, якщо клік, здається, спрямований на елемент. Подія містить користувацьку подію, що містить збіг element та його id як посилання. Це може дозволити іншим частинам обробляти поведінку відносно самих якорів. Початкова подія також вказана, оскільки її, можливо, потрібно запобігти. Якщо подію не запобігти, тоді інтерфейс користувача прокрутиться до цільового елемента.
API¶
Наступні значення містяться у користувацькій події anchor-link-clicked, поясненій вище.
Name |
Тип |
Опис |
|---|---|---|
|
|
Якірний елемент, на який орієнтований href |
|
|
id, що міститься в href |
|
|
Початкова подія кліку |
Примітка
Служба scroller генерує подію SCROLLER:ANCHOR_LINK_CLICKED на main bus. Щоб уникнути поведінки скролера за замовчуванням, необхідно використовувати preventDefault() для події, що передається слухачу, щоб мати змогу правильно реалізувати власну поведінку з цього слухача.
Сервіс заголовку¶
Огляд¶
Технічна назва:
titleЗалежності: немає
Сервіс title пропонує простий API, який дозволяє читати/змінювати назву документа. Наприклад, якщо поточна назва документа - «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, що підтримується службою заголовків
- setParts(parts)¶
- Аргументи
parts (
Parts()) – об’єкт, що представляє необхідну зміну
Метод
setPartsдозволяє додавати/замінювати/видаляти кілька частин заголовка. Видалення частини (значення) здійснюється шляхом встановлення відповідного значення ключа вnull.Зверніть увагу, що можна змінити лише одну частину, не впливаючи на інші частини. Наприклад, якщо заголовок складається з таких частин:
{ odoo: "Odoo", action: "Import" }
де значення
currentдорівнюєOdoo - Import, тодіsetParts({ action: null, });
змінить назву на
Odoo.
Сервіс користувача¶
Огляд¶
Технічна назва:
userЗалежності:
rpc
Сервіс user надає купу даних та кілька допоміжних функцій щодо підключеного користувача.
API¶
Name |
Тип |
Опис |
|---|---|---|
|
|
|
|
|
Інформація про базу даних |
|
|
Id дії, що використовується як домашня сторінка для користувача |
|
|
Чи є користувач адміністратором (група |
|
|
Чи належить користувач до системної групи ( |
|
|
мова, що використовується |
|
|
Ім’я користувача |
|
|
Id партнерського екземпляра користувача |
|
|
Часовий пояс користувача |
|
|
Id користувача |
|
|
Альтернативний нікнейм користувача |
- updateContext(update)¶
- Аргументи
update (
object()) – об’єкт, за допомогою якого потрібно оновити контекст
оновити user context заданим об’єктом.
userService.updateContext({ isFriend: true })
- removeFromContext(key)¶
- Аргументи
key (
string()) – ключ цільового атрибута
видалити значення з заданим ключем з user context
userService.removeFromContext("isFriend")
- hasGroup(group)¶
- Аргументи
group (
string()) – xml_id групи, яку потрібно шукати
- Повертає
Promise<boolean>– це користувач у групі
перевірити, чи є користувач частиною групи
const isInSalesGroup = await userService.hasGroup("sale.group_sales")