frontend-js

v2026.09.25

Odoo frontend JavaScript patterns for website themes. Covers publicWidget framework (complete pattern with editableMode handling), Owl v1/v2 component patterns, _t() translation best practices, Bootstrap 4-to-5 migration, version detection, and critical development rules. Supports Odoo 14-19. <example> Context: User wants to create a publicWidget user: "Create a publicWidget for my Odoo website" assistant: "I will create a publicWidget with editableMode handling and proper cleanup." <commentary>publicWidget creation.</commentary> </example> <example> Context: User asks about Owl components user: "How do I create an Owl component in Odoo 18?" assistant: "I will show the Owl v2 pattern with static template and props." <commentary>Owl component pattern.</commentary> </example> <example> Context: User needs help with translations user: "How do I translate JavaScript strings in Odoo?" assistant: "Use _t() at DEFINITION TIME for static labels, not runtime wrappers." <commentary>Translation best practices.</commentary> </example> <example> Context: User migrating Bootstrap classes user: "Convert Bootstrap 4 classes to Bootstrap 5 for Odoo 17" assistant: "Replace ml-* with ms-*, mr-* with me-*, text-left with text-start." <commentary>Bootstrap migration.</commentary> </example>

GitHub
安装命令
npx skhub add ahmed-lakosha/frontend-js
Markdown
SKILL.md

Odoo Frontend JavaScript Patterns

Critical Rules

  1. Website themes: Use publicWidget framework ONLY — NOT Owl or vanilla JS
  2. JS modules: Start every file with /** @odoo-module **/
  3. No inline JS/CSS: Always separate files in static/src/js/ and static/src/scss/
  4. Bootstrap: v5.1.3 for Odoo 16+ (never Tailwind)
  5. Translations: Use _t() at DEFINITION TIME for static JS labels

Version Detection

OdooBootstrapOwlJavaScript
144.x—ES6+
154.xv1ES6+
165.1.3v1ES2020+
175.1.3v2ES2020+
18-195.1.3v2ES2020+

Detect from path (odoo17/ → v17), manifest version field, or config file.


publicWidget Pattern (REQUIRED for Themes)

Use for: Website interactions, theme functionality, animations, forms

/** @odoo-module **/

import publicWidget from "@web/legacy/js/public/public_widget";

publicWidget.registry.MyWidget = publicWidget.Widget.extend({
    selector: '.my-selector',
    disabledInEditableMode: false,  // Allow in website builder

    events: {
        'click .button': '_onClick',
        'change input': '_onChange',
        'submit form': '_onSubmit',
    },

    /**
     * CRITICAL: Check editableMode for website builder compatibility
     */
    start: function () {
        if (!this.editableMode) {
            this._initializeAnimation();
            this._bindExternalEvents();
        }
        return this._super.apply(this, arguments);
    },

    _initializeAnimation: function () {
        this.$el.addClass('animated');
    },

    _bindExternalEvents: function () {
        $(window).on('scroll.myWidget', this._onScroll.bind(this));
        $(window).on('resize.myWidget', this._onResize.bind(this));
    },

    _onClick: function (ev) {
        ev.preventDefault();
        if (this.editableMode) return;
        // Handler logic
    },

    /**
     * CRITICAL: Clean up event listeners to prevent memory leaks
     */
    destroy: function () {
        $(window).off('.myWidget');  // Remove namespaced events
        this._super.apply(this, arguments);
    },
});

export default publicWidget.registry.MyWidget;

Key Points

  1. ALWAYS check this.editableMode before animations/interactions
  2. disabledInEditableMode: false makes widgets work in website builder
  3. ALWAYS clean up event listeners in destroy()
  4. NEVER use Owl or vanilla JS for website themes
  5. Use namespaced events (.myWidget) for easy cleanup

Include in Manifest

'assets': {
    'web.assets_frontend': [
        'module_name/static/src/js/my_widget.js',
    ],
}

Owl Component Pattern

Odoo 17 (Owl v1)

/** @odoo-module **/

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

class MyComponent extends Component {
    setup() {
        this.state = useState({ items: [], loading: false });
    }

    async willStart() {
        await this.loadData();
    }
}

MyComponent.template = "module_name.MyComponentTemplate";
registry.category("public_components").add("MyComponent", MyComponent);

Odoo 18-19 (Owl v2 — Breaking Changes)

/** @odoo-module **/

import { Component, useState } from "@odoo/owl";

class MyComponent extends Component {
    static template = "module_name.MyComponentTemplate";  // Static property
    static props = {
        title: { type: String, optional: true },
        items: { type: Array },
    };

    setup() {
        this.state = useState({ selectedId: null });
    }
}

XML Template

<template id="MyComponentTemplate" name="My Component">
    <div class="my-component">
        <h3 t-if="props.title"><t t-esc="props.title"/></h3>
        <ul>
            <li t-foreach="props.items" t-as="item" t-key="item.id">
                <t t-esc="item.name"/>
            </li>
        </ul>
    </div>
</template>

Translation (_t) Best Practices

CORRECT — Wrap at DEFINITION TIME

/** @odoo-module **/
import { _t } from "@web/core/l10n/translation";

// Static labels wrapped where defined
const MONTHS = [
    {value: 1, short: _t("Jan"), full: _t("January")},
    {value: 2, short: _t("Feb"), full: _t("February")},
    // ...
];

const STATUS_LABELS = {
    draft: _t("Draft"),
    pending: _t("Pending"),
    approved: _t("Approved"),
};

WRONG — Runtime wrappers DON'T WORK

// WRONG: Strings without _t() at definition
const MONTHS = [{value: 1, label: "Jan"}]; // NOT found by PO extractor!

// WRONG: Variable passed to _t() at runtime
translateLabel(key) {
    return _t(key);  // PO extractor can't find string literals
}

When to use _t()

Use _t()Don't use _t()
Static labels in JS arrays/objectsStatic text in XML templates (auto-translated)
Error messages in JS constantsDynamic variables passed at runtime
User-facing strings defined in JSHardcoded strings in .xml files

Bootstrap 4 → 5 Migration (Odoo 14/15 → 16+)

Class Replacements

Bootstrap 4Bootstrap 5
ml-*ms-* (margin-start)
mr-*me-* (margin-end)
pl-*ps-* (padding-start)
pr-*pe-* (padding-end)
text-lefttext-start
text-righttext-end
float-leftfloat-start
float-rightfloat-end
form-groupmb-3
custom-selectform-select
closebtn-close
badge-*bg-*
font-weight-boldfw-bold
sr-onlyvisually-hidden
no-guttersg-0

Data Attributes

Bootstrap 4Bootstrap 5
data-toggledata-bs-toggle
data-targetdata-bs-target
data-dismissdata-bs-dismiss

Removed Classes (find alternatives)

  • form-inline → Use grid/flex utilities
  • jumbotron → Recreate with utilities
  • media → Use d-flex with flex utilities

SCSS Bootstrap Overrides

File: static/src/scss/bootstrap_overridden.scss Bundle: web._assets_frontend_helpers

@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables";

$spacer: 1rem !default;
$border-radius: 0.25rem !default;
$border-radius-lg: 0.5rem !default;
$box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15) !default;

Use !default flag on all overrides.


Version-Specific Notes

Odoo 17

  • Owl v1: template as separate property
  • Snippet registration: simple XPath
  • Import: @web/legacy/js/public/public_widget

Odoo 18-19

  • Owl v2: static template, props validation
  • Snippet groups required
  • Website builder: plugin architecture (Odoo 19)
  • Breaking: type='json' → type='jsonrpc' in controllers
发现
标签

此技能尚未发布标签。

版本
最新版本元数据

版本

v2026.09.25

发布时间

2026年9月25日

分类

未分类

许可证

MIT

源路径

odoo-frontend-plugin/skills/frontend-js

默认分支

master

最新提交

5541d98

Tree SHA

dca9407