Keretrendszer Áttekintés

Bevezetés

Az Odoo Javascript keretrendszer a web/ kiegészítő által biztosított funkciók/építőelemek halmaza, amelyek segítenek az odoo alkalmazások böngészőben történő futtatásában. Ugyanakkor az Odoo Javascript keretrendszer egy egyoldalas alkalmazás, amelyet általában web kliens néven ismernek (elérhető a /web URL-en).

A web kliens egy egyedi osztály- és widgetrendszerrel készült alkalmazásként indult, de most áttér a natív javascript osztályok használatára, és az Owl-t használja komponensrendszerként. Ez magyarázza, miért van mindkét rendszer jelenleg használatban a kódbázisban.

Magas szintű nézőpontból a web kliens egy egyoldalas alkalmazás: nem szükséges minden alkalommal teljes oldalt kérnie a szervertől, amikor a felhasználó végrehajt egy műveletet. Ehelyett csak azt kéri, amire szüksége van, majd ennek megfelelően cseréli/frissíti az aktuális képernyőt. Emellett kezeli az URL-t, hogy szinkronban tartsa az aktuális állapottal.

A javascript keretrendszert (teljesen vagy részben) más helyzetekben is használják, például az Odoo weboldalon vagy az értékesítési ponton. Ez a referencia főként a web kliensre összpontosít.

Megjegyzés

Az Odoo ökoszisztémában gyakori, hogy a frontend és backend szavakat az odoo weboldal (nyilvános) és a web kliens szinonimájaként használják. Ez a terminológia nem összetévesztendő a böngésző-kód (frontend) és a szerver (backend) általánosabb használatával.

Megjegyzés

Ebben a dokumentációban a komponens szó mindig az új Owl komponensekre utal, míg a widget az régi Odoo widgetekre.

Megjegyzés

Minden új fejlesztést lehetőség szerint Owl-ban kell végezni!

Kódstruktúra

A web/static/src mappa tartalmazza az összes web/ javascript (és css és sablon) kódbázist. Itt található a legfontosabb mappák listája:

  • core/ a legtöbb alacsony szintű funkció

  • fields/ az összes mező komponens

  • views/ az összes javascript nézet komponens (form, list, …)

  • search/ vezérlőpult, keresősáv, keresőpanel, …

  • webclient/ a web kliens specifikus kódja: navigációs sáv, felhasználói menü, akció szolgáltatás, …

A web/static/src a gyökér mappa. Minden benne lévő egyszerűen importálható a @web előtag használatával. Például, itt van, hogyan lehet importálni a memoize függvényt, amely a web/static/src/core/utils/functions helyen található:

import { memoize } from "@web/core/utils/functions";

WebClient Architektúra

Ahogy fentebb említettük, a web kliens egy owl alkalmazás. Itt van egy kissé egyszerűsített változata a sablonjának:

<t t-name="web.WebClient">
    <body class="o_web_client">
        <NavBar/>
        <ActionContainer/>
        <MainComponentsContainer/>
    </body>
</t>

Amint láthatjuk, alapvetően egy burkoló a navigációs sávhoz, az aktuális akcióhoz és néhány további komponenshez. Az ActionContainer egy magasabb rendű komponens, amely megjeleníti az aktuális akció vezérlőt (tehát egy kliens akciót, vagy egy specifikus nézetet az act_window típusú akciók esetében). Az akciók kezelése a munkájának nagy részét képezi: az akció szolgáltatás memóriában tartja az összes aktív akció halmazát (amely a kenyérmorzsákban van ábrázolva), és koordinálja az egyes változásokat.

Egy másik érdekes megjegyzés a MainComponentsContainer: ez egyszerűen egy komponens, amely megjeleníti az összes komponenst, amely a main_components regisztrációban van. Így tudják a rendszer más részei kiterjeszteni a web klienst.

Környezet

Mint egy Owl alkalmazás, az Odoo web kliens meghatározza a saját környezetét (a komponensek hozzáférhetnek ehhez a this.env használatával). Itt található egy leírás arról, hogy mit ad hozzá az Odoo a megosztott env objektumhoz:

Kulcs

Érték

qweb

szükséges az Owl által (tartalmazza az összes sablont)

busz

fő busz, amelyet néhány általános esemény koordinálására használnak

szolgáltatások

minden telepített szolgáltatás (általában a useService hookkal kell elérni)

hibakeresés

karakterlánc. Ha nem üres, a web kliens debug mód-ban van

_t

fordítási funkció

kicsi

boolean. Ha igaz, a web kliens jelenleg mobil módban van (képernyő szélesség <= 767px)

Tehát például egy szöveg fordításához egy komponensben (megjegyzés: a sablonok automatikusan lefordítódnak, így ebben az esetben nincs szükség külön intézkedésre), ezt tehetjük:

const someString = this.env._t('some text');

Megjegyzés

A környezetre való hivatkozás meglehetősen erőteljes, mert hozzáférést biztosít minden szolgáltatáshoz. Ez sok esetben hasznos: például a felhasználói menüelemek többnyire szövegként vannak meghatározva, és egy függvényként, amely az env-et veszi egyedi argumentumként. Ez elegendő az összes felhasználói menü igény kifejezésére.

Építőelemek

A web kliens nagy része néhány absztrakció típusból épül fel: regiszterek, szolgáltatások, komponensek és horgok.

Regiszterek

Regiszterek alapvetően egy egyszerű kulcs/érték párosítás, amely bizonyos típusú objektumokat tárol. Fontos részei a felhasználói felület kiterjeszthetőségének: ha egy objektum regisztrálva van, a web kliens többi része használhatja azt. Például a mező regiszter tartalmazza az összes mező komponenst (vagy widgetet), amelyeket nézetekben lehet használni.

import { Component } from "@odoo/owl";
import { registry } from "./core/registry";

class MyFieldChar extends Component {
    // some code
}

registry.category("fields").add("my_field_char", MyFieldChar);

Megjegyzendő, hogy importáljuk a fő regisztert a @web/core/registry-ből, majd megnyitjuk a fields alregisztert.

Szolgáltatások

Szolgáltatások hosszú élettartamú kódrészek, amelyek egy funkciót biztosítanak. Komponensek (useService-el) vagy más szolgáltatások importálhatják őket. Továbbá, megadhatnak egy függőségi halmazt. Ebben az értelemben a szolgáltatások alapvetően egy DI (függőség injektálás) rendszert alkotnak. Például a notification szolgáltatás lehetőséget biztosít egy értesítés megjelenítésére, vagy az rpc szolgáltatás a megfelelő módja egy kérés végrehajtásának az Odoo szerver felé.

A következő példa egy egyszerű szolgáltatást regisztrál, amely 5 másodpercenként megjelenít egy értesítést:

import { registry } from "./core/registry";

const serviceRegistry = registry.category("services");

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

serviceRegistry.add("myService", myService);

Komponensek és Horgok

Komponensek és horgok az Owl komponens rendszer ötleteiből származnak. Az Odoo komponensek egyszerűen owl komponensek, amelyek a web kliens részét képezik.

Horgok egy módja a kód faktorizálásának, még akkor is, ha az életciklustól függ. Ez egy összetett/funkcionális módja egy funkció beillesztésének egy komponensbe. Ezek egyfajta mixinként is tekinthetők.

function useCurrentTime() {
    const state = useState({ now: new Date() });
    const update = () => state.now = new Date();
    let timer;
    onWillStart(() => timer = setInterval(update, 1000));
    onWillUnmount(() => clearInterval(timer));
    return state;
}

Környezet

Az Odoo javascript fontos fogalma a környezet: lehetőséget biztosít a kód számára, hogy több kontextust adjon egy függvényhíváshoz vagy egy rpc-hez, így a rendszer más részei megfelelően reagálhatnak erre az információra. Valamilyen módon ez olyan, mint egy információs csomag, amely mindenhová eljut. Hasznos bizonyos helyzetekben, például amikor az Odoo szervernek tudnia kell, hogy egy modell rpc egy adott űrlap nézetből származik, vagy egyes funkciók aktiválása/letiltása egy komponensben.

Az Odoo web kliensben két különböző környezet létezik: a felhasználói környezet és az akció környezet (tehát óvatosnak kell lennünk, amikor a környezet szót használjuk: a helyzettől függően mást jelenthet).

Megjegyzés

A környezet objektum sok esetben hasznos lehet, de óvatosnak kell lenni, hogy ne használjuk túl! Sok probléma megoldható szabványos módon a környezet módosítása nélkül.

Felhasználói kontextus

A felhasználói kontextus egy kis objektum, amely különféle információkat tartalmaz az aktuális felhasználóval kapcsolatban. Elérhető a user szolgáltatáson keresztül:

class MyComponent extends Component {
    setup() {
        const user = useService("user");
        console.log(user.context);
    }
}

A következő információkat tartalmazza:

Név

Típus

Leírás

engedélyezett_vállalat_azonosítók

szám[]

a felhasználó aktív vállalati azonosítóinak listája

lang

string

the user language code (such as „en_us”)

tz

string

the user current timezone (for example „Europe/Brussels”)

Gyakorlatban az orm szolgáltatás automatikusan hozzáadja a felhasználói kontextust minden kéréséhez. Ezért általában nem szükséges közvetlenül importálni a legtöbb esetben.

Megjegyzés

Az allowed_company_ids első eleme a felhasználó fő cége.

Műveleti Kontextus

A ir.actions.act_window és ir.actions.client támogat egy opcionális context mezőt. Ez a mező egy char, amely egy objektumot képvisel. Amikor a megfelelő művelet betöltődik a web kliensben, ez a kontextus mező objektumként lesz kiértékelve, és átadva annak a komponensnek, amely a művelethez tartozik.

<field name="context">{'search_default_customer': 1}</field>

Sokféleképpen használható. Például a nézetek hozzáadják a műveleti kontextust minden szerverhez intézett kéréshez. Egy másik fontos felhasználás az, hogy alapértelmezés szerint aktiváljunk néhány keresési szűrőt (lásd a fenti példát).

Néha, amikor új műveleteket hajtunk végre manuálisan (tehát programozottan, javascriptben), hasznos lehet a műveleti kontextus kiterjesztése. Ezt az additional_context argumentummal lehet megtenni.

// in setup
let actionService = useService("action");

// in some event handler
actionService.doAction("addon_name.something", {
    additional_context:{
        default_period_id: defaultPeriodId
    }
});

Ebben a példában az addon_name.something xml_id-val rendelkező művelet lesz betöltve, és annak kontextusa ki lesz terjesztve a default_period_id értékkel. Ez egy nagyon fontos felhasználási eset, amely lehetővé teszi a fejlesztők számára, hogy műveleteket kombináljanak azáltal, hogy némi információt adnak a következő művelethez.

Python Értelmező

Az Odoo keretrendszer egy beépített kis python értelmezőt tartalmaz. Ennek célja, hogy kis python kifejezéseket értékeljen ki. Ez fontos, mert az Odoo nézeteiben a módosítók pythonban vannak írva, de a böngészőnek kell azokat kiértékelnie.

Példa:

import { evaluateExpr } from "@web/core/py_js/py";

evaluateExpr("1 + 2*{'a': 1}.get('b', 54) + v", { v: 33 }); // returns 142

A py javascript kód 5 függvényt exportál:

tokenize(expr)
Argumentum
  • expr (string()) – a tokenizálandó kifejezés

Visszatérési érték

Token[] egy token lista

parse(tokens)
Argumentum
  • tokens (Token[]()) – egy token lista

Visszatérési érték

AST egy absztrakt szintaxisfa struktúra, amely a kifejezést reprezentálja

parseExpr(expr)
Argumentum
  • expr (string()) – egy érvényes python kifejezést reprezentáló karakterlánc

Visszatérési érték

AST egy absztrakt szintaxisfa struktúra, amely a kifejezést reprezentálja

evaluate(ast[, context])
Argumentum
  • ast (AST()) – egy AST struktúra, amely egy kifejezést reprezentál

  • context (Object()) – egy objektum, amely további értékelési kontextust biztosít

Visszatérési érték

bármilyen a kifejezés eredményértéke, a kontextus figyelembevételével

evaluateExpr(expr[, context])
Argumentum
  • expr (string()) – egy érvényes python kifejezést reprezentáló karakterlánc

  • context (Object()) – egy objektum, amely további értékelési kontextust biztosít

Visszatérési érték

bármilyen a kifejezés eredményértéke, a kontextus figyelembevételével

Domainek

Általánosságban elmondható, hogy az Odoo-ban a domainek olyan rekordok halmazát képviselik, amelyek megfelelnek bizonyos megadott feltételeknek. Javascriptben általában vagy feltételek listájaként (vagy operátorok: |, & vagy ! prefix notációban), vagy karakterlánc kifejezésekként vannak ábrázolva. Nem szükséges, hogy normalizáltak legyenek (a & operátor szükség esetén implicit). Például:

// list of conditions
[]
[["a", "=", 3]]
[["a", "=", 1], ["b", "=", 2], ["c", "=", 3]]
["&", "&", ["a", "=", 1], ["b", "=", 2], ["c", "=", 3]]
["&", "!", ["a", "=", 1], "|", ["a", "=", 2], ["a", "=", 3]]

// string expressions
"[('some_file', '>', a)]"
"[('date','>=', (context_today() - datetime.timedelta(days=30)).strftime('%Y-%m-%d'))]"
"[('date', '!=', False)]"

A karakterlánc kifejezések erősebbek, mint a lista kifejezések: tartalmazhatnak python kifejezéseket és kiértékeletlen értékeket, amelyek egy értékelési kontextustól függenek. Azonban a karakterlánc kifejezések kezelése nehezebb.

Mivel a domainek meglehetősen fontosak a web kliensben, az Odoo biztosít egy Domain osztályt:

new Domain([["a", "=", 3]]).contains({ a: 3 }) // true

const domain = new Domain(["&", "&", ["a", "=", 1], ["b", "=", 2], ["c", "=", 3]]);
domain.contains({ a: 1, b: 2, c: 3 }); // true
domain.contains({ a: -1, b: 2, c: 3 }); // false

// next expression returns ["|", ("a", "=", 1), ("b", "<=", 3)]
Domain.or([[["a", "=", 1]], "[('b', '<=', 3)]"]).toString();

Itt található a Domain osztály leírása:

class Domain([descr])
Argumentum
  • descr (string | any[] | Domain()) – egy domain leírás

Domain.contains(record)
Argumentum
  • record (Object()) – egy rekord objektum

Visszatérési érték

logikai

Igaz értéket ad vissza, ha a rekord megfelel a domain által meghatározott összes feltételnek

Domain.toString()
Visszatérési érték

karakterlánc

Visszaad egy karakterlánc leírást a domainhez

Domain.toList([context])
Argumentum
  • context (Object()) – értékelési kontextus

Visszatérési érték

bármilyen[]

Visszaad egy listaleírást a tartományhoz. Vegye figyelembe, hogy ez a metódus egy opcionális context objektumot is elfogad, amelyet az összes szabad változó helyettesítésére használnak.

new Domain(`[('a', '>', b)]`).toList({ b:3 }); // [['a', '>', 3]]

A Domain osztály 4 hasznos statikus metódust is biztosít a tartományok kombinálására:

// ["&", ("a", "=", 1), ("uid", "<=", uid)]
Domain.and([[["a", "=", 1]], "[('uid', '<=', uid)]"]).toString();

// ["|", ("a", "=", 1), ("uid", "<=", uid)]
Domain.or([[["a", "=", 1]], "[('uid', '<=', uid)]"]).toString();

// ["!", ("a", "=", 1)]
Domain.not([["a", "=", 1]]).toString();

// ["&", ("a", "=", 1), ("uid", "<=", uid)]
Domain.combine([[["a", "=", 1]], "[('uid', '<=', uid)]"], "AND").toString();
static Domain.and(domains)
Paraméterek

domains (string[] | any[][] | Domain[]) – tartományábrázolások listája

Visszatérési érték

Tartomány

Visszaad egy tartományt, amely az összes tartomány metszetét képviseli.

static Domain.or(domains)
Paraméterek

domains (string[] | any[][] | Domain[]) – tartományábrázolások listája

Visszatérési érték

Tartomány

Visszaad egy tartományt, amely az összes tartomány unióját képviseli.

static Domain.not(domain)
Paraméterek

domain (string | any[] | Domain) – egy tartományábrázolás

Visszatérési érték

Tartomány

Visszaad egy tartományt, amely a tartomány argumentum negációját képviseli

static Domain.combine(domains, operator)
Paraméterek
  • domains (string[] | any[][] | Domain[]) – tartományábrázolások listája

  • operator ('AND' or 'OR') – egy operátor

Visszatérési érték

Tartomány

Visszaad egy domaint, amely vagy az összes domain metszetét, vagy unióját képviseli, az operátor argumentum értékétől függően.

Busz

A web kliens environment objektum tartalmaz egy esemény buszt, amelynek neve bus. Célja, hogy a rendszer különböző részei megfelelően koordinálják magukat, anélkül, hogy összekapcsolódnának. Az env.bus egy owl EventBus, amelyet a globális érdeklődésre számot tartó eseményekhez kell használni.

// for example, in some service code:
env.bus.on("WEB_CLIENT_READY", null, doSomething);

Itt található az események listája, amelyek ezen a buszon kiválthatók:

Üzenet

Adatcsomag

Kiváltó

ACTION_MANAGER:UI-UPDATED

egy mód, amely jelzi, hogy a felhasználói felület mely része frissült («current», «new» vagy «fullscreen»)

az akció menedzsernek kért akció megjelenítése megtörtént

ACTION_MANAGER:UPDATE

következő renderelési információ

az akciókezelő befejezte a következő felület kiszámítását

MENUS:APP-CHANGED

nincs

a menüszolgáltatás aktuális alkalmazása megváltozott

ROUTE_CHANGE

nincs

az url hash megváltozott

RPC:REQUEST

rpc azonosító

egy rpc kérés éppen elindult

RPC:RESPONSE

rpc azonosító

egy rpc kérés befejeződött

WEB_CLIENT_READY

nincs

a web kliens fel lett szerelve

FOCUS-VIEW

nincs

a fő nézetnek fókuszálnia kell magát

CLEAR-CACHES

nincs

minden belső gyorsítótárat törölni kell

CLEAR-UNCOMMITTED-CHANGES

függvények listája

minden nézet, amely nem elkötelezett változtatásokat tartalmaz, törölje azokat, és helyezzen egy visszahívást a listába

Böngésző Objektum

A javascript keretrendszer egy speciális browser objektumot is biztosít, amely hozzáférést nyújt számos böngésző API-hoz, mint például a location, localStorage vagy setTimeout. Például, így lehet használni a browser.setTimeout függvényt:

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

// somewhere in code
browser.setTimeout(someFunction, 1000);

Ez elsősorban tesztelési célokra érdekes: minden kód, amely a böngésző objektumot használja, könnyen tesztelhető azáltal, hogy a teszt időtartamára a releváns függvényeket szimuláljuk.

A következő tartalmat tartalmazza:

addEventListener

cancelAnimationFrame

clearInterval

clearTimeout

konzol

Dátum

lekérés

előzmények

helyiTároló

hely

navigátor

megnyit

véletlen

eseményEltávolító

requestAnimationFrame

sessionStorage

setInterval

setTimeout

XMLHttpRequest

Hibakeresési mód

Az Odoo néha egy speciális módban működhet, amelyet debug módnak neveznek. Két fő célra használják:

  • további információk/mezők megjelenítése bizonyos képernyőkön,

  • néhány további eszköz biztosítása a fejlesztők számára az Odoo felület hibakeresésének segítésére.

A debug módot egy karakterlánc írja le. Egy üres karakterlánc azt jelenti, hogy a debug mód nincs aktív állapotban. Ellenkező esetben aktív. Ha a karakterlánc tartalmazza az assets vagy tests szavakat, akkor a megfelelő specifikus al-módok aktiválódnak (lásd alább). Mindkét mód egyszerre is aktív lehet, például az assets,tests karakterlánccal.

A debug mód aktuális értéke olvasható a környezet: env.debug.

Javaslat

Ahhoz, hogy a menük, mezők vagy nézeti elemek csak hibakeresési módban jelenjenek meg, a base.group_no_one csoportot kell megcélozni:

<field name="fname" groups="base.group_no_one"/>

Eszközök mód

A debug=assets al-mód hasznos a javascript kód hibakereséséhez: aktiválás után a eszközök csomagok már nincsenek tömörítve, és forrás-térképek is generálódnak. Ez hasznos mindenféle javascript kód hibakereséséhez.

Teszt mód

Van egy másik al-mód, amelynek neve tests: ha engedélyezve van, a szerver a web.assets_tests csomagot injektálja az oldalba. Ez a csomag főként teszt túrákat tartalmaz (olyan túrákat, amelyek célja egy funkció tesztelése, nem pedig valami érdekes bemutatása a felhasználóknak). A tests mód így hasznos ezen túrák futtatásához.

Lásd még