CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-odoo--owl

Odoo Web Library (OWL) is a modern, lightweight TypeScript UI framework for building reactive web applications with components, templates, and state management.

Overview
Eval results
Files

hooks.mddocs/

Component Hooks

Hook functions for component state management, lifecycle integration, and accessing component context and environment.

Capabilities

useState

Creates reactive state that automatically triggers component re-renders when modified.

/**
 * Creates reactive state for a component
 * @template T - State type
 * @param state - Initial state value
 * @returns Reactive state proxy
 */
function useState<T>(state: T): T;

Usage Examples:

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

class Counter extends Component {
  static template = xml`
    <div>
      <p>Count: <t t-esc="state.count"/></p>
      <button t-on-click="increment">+</button>
    </div>
  `;

  setup() {
    // Simple primitive state
    this.state = useState({ count: 0 });
  }

  increment() {
    this.state.count++; // Automatically triggers re-render
  }
}

class TodoApp extends Component {
  static template = xml`
    <div>
      <input t-model="state.newTodo" />
      <button t-on-click="addTodo">Add</button>
      <ul>
        <li t-foreach="state.todos" t-as="todo" t-key="todo.id">
          <span t-esc="todo.text" />
          <button t-on-click="() => this.removeTodo(todo.id)">Remove</button>
        </li>
      </ul>
    </div>
  `;

  setup() {
    // Complex object state
    this.state = useState({
      todos: [],
      newTodo: "",
      filter: "all"
    });
  }

  addTodo() {
    if (this.state.newTodo.trim()) {
      this.state.todos.push({
        id: Date.now(),
        text: this.state.newTodo.trim(),
        completed: false
      });
      this.state.newTodo = "";
    }
  }

  removeTodo(id) {
    const index = this.state.todos.findIndex(t => t.id === id);
    if (index >= 0) {
      this.state.todos.splice(index, 1);
    }
  }
}

useComponent

Gets a reference to the current component instance.

/**
 * Returns the current component instance
 * @returns Current component instance
 */
function useComponent(): Component;

Usage Examples:

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

class MyComponent extends Component {
  static template = xml`<div>Component with self-reference</div>`;

  setup() {
    const component = useComponent();
    console.log("Component instance:", component);

    // Useful for accessing component from nested functions
    setTimeout(() => {
      component.render(); // Force re-render
    }, 1000);
  }
}

useRef

Creates a reference to access DOM elements after rendering.

/**
 * Creates a reference to access DOM elements
 * @template T - HTMLElement type
 * @param name - Reference name (must match t-ref attribute in template)
 * @returns Reference object with el property
 */
function useRef<T extends HTMLElement = HTMLElement>(name: string): { el: T | null };

Usage Examples:

import { Component, xml, useRef, onMounted } from "@odoo/owl";

class FocusInput extends Component {
  static template = xml`
    <div>
      <input t-ref="input" placeholder="Will be focused on mount" />
      <button t-on-click="focusInput">Focus Input</button>
      <canvas t-ref="canvas" width="200" height="100"></canvas>
    </div>
  `;

  setup() {
    this.inputRef = useRef("input");
    this.canvasRef = useRef("canvas");

    onMounted(() => {
      // Focus input after component mounts
      this.inputRef.el?.focus();

      // Draw on canvas
      const canvas = this.canvasRef.el;
      if (canvas) {
        const ctx = canvas.getContext("2d");
        ctx.fillStyle = "blue";
        ctx.fillRect(10, 10, 50, 30);
      }
    });
  }

  focusInput() {
    this.inputRef.el?.focus();
  }
}

useEnv

Accesses the component's environment object.

/**
 * Gets the current component's environment
 * @template E - Environment type
 * @returns Component environment
 */
function useEnv<E>(): E;

Usage Examples:

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

class ApiComponent extends Component {
  static template = xml`
    <div>
      <button t-on-click="fetchData">Fetch Data</button>
      <div t-if="state.data">
        <t t-esc="state.data" />
      </div>
    </div>
  `;

  setup() {
    this.env = useEnv();
    this.state = useState({ data: null });
  }

  async fetchData() {
    try {
      const response = await fetch(this.env.apiUrl + '/data');
      this.state.data = await response.json();
    } catch (error) {
      console.error("Failed to fetch:", error);
    }
  }
}

// Mount with environment
mount(ApiComponent, document.body, {
  env: {
    apiUrl: "https://api.example.com",
    user: { id: 1, name: "John" }
  }
});

useSubEnv

Creates a sub-environment that extends the current environment.

/**
 * Extends the current environment with additional properties
 * @param envExtension - Properties to add to environment
 */
function useSubEnv(envExtension: Env): void;

Usage Examples:

import { Component, xml, useSubEnv, useEnv } from "@odoo/owl";

class ThemeProvider extends Component {
  static template = xml`
    <div class="theme-provider">
      <t t-slot="default" />
    </div>
  `;

  setup() {
    // Extend environment with theme-related utilities
    useSubEnv({
      theme: {
        primary: "#007bff",
        secondary: "#6c757d",
        isDark: this.props.darkMode
      },
      formatDate: (date) => date.toLocaleDateString(),
      t: (key) => this.env.translations[key] || key
    });
  }
}

class ThemedButton extends Component {
  static template = xml`
    <button t-att-style="buttonStyle" t-on-click="handleClick">
      <t t-esc="env.t(props.labelKey)" />
    </button>
  `;

  setup() {
    this.env = useEnv();
  }

  get buttonStyle() {
    return `background-color: ${this.env.theme.primary}; color: white;`;
  }

  handleClick() {
    console.log("Button clicked in theme:", this.env.theme.isDark ? "dark" : "light");
  }
}

useChildSubEnv

Creates a sub-environment specifically for child components.

/**
 * Creates a sub-environment for child components
 * @param envExtension - Properties to add to child environment
 */
function useChildSubEnv(envExtension: Env): void;

useEffect

Manages side effects with optional dependency tracking.

/**
 * Manages side effects with dependency tracking
 * @param effect - Effect callback that receives dependencies and optionally returns cleanup function
 * @param computeDependencies - Function that computes dependencies array for change detection
 */
function useEffect<T extends unknown[]>(
  effect: (...dependencies: T) => void | (() => void),
  computeDependencies?: () => [...T]
): void;

Usage Examples:

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

class TimerComponent extends Component {
  static template = xml`
    <div>
      <p>Timer: <t t-esc="state.seconds"/>s</p>
      <button t-on-click="toggleTimer">
        <t t-esc="state.isRunning ? 'Stop' : 'Start'" />
      </button>
    </div>
  `;

  setup() {
    this.state = useState({
      seconds: 0,
      isRunning: false
    });

    // Effect with cleanup
    useEffect(() => {
      let interval;
      if (this.state.isRunning) {
        interval = setInterval(() => {
          this.state.seconds++;
        }, 1000);
      }

      // Cleanup function
      return () => {
        if (interval) {
          clearInterval(interval);
        }
      };
    }, [this.state.isRunning]); // Re-run when isRunning changes

    // Effect that runs once on mount
    useEffect(() => {
      console.log("Timer component mounted");

      return () => {
        console.log("Timer component unmounting");
      };
    }, []); // Empty dependencies = run once
  }

  toggleTimer() {
    this.state.isRunning = !this.state.isRunning;
  }
}

useExternalListener

Adds event listeners to external DOM elements with automatic cleanup.

/**
 * Adds event listener to external elements with automatic cleanup
 * @param target - Event target (DOM element, window, document, etc.)
 * @param eventName - Event name to listen for
 * @param handler - Event handler function
 */
function useExternalListener(
  target: EventTarget,
  eventName: string,
  handler: EventListener,
  eventParams?: AddEventListenerOptions
): void;

Usage Examples:

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

class WindowSizeTracker extends Component {
  static template = xml`
    <div>
      <p>Window size: <t t-esc="state.width"/>x<t t-esc="state.height"/></p>
      <p>Mouse position: (<t t-esc="state.mouseX"/>, <t t-esc="state.mouseY"/>)</p>
    </div>
  `;

  setup() {
    this.state = useState({
      width: window.innerWidth,
      height: window.innerHeight,
      mouseX: 0,
      mouseY: 0
    });

    // Listen to window resize
    useExternalListener(window, "resize", () => {
      this.state.width = window.innerWidth;
      this.state.height = window.innerHeight;
    });

    // Listen to mouse movement on document
    useExternalListener(document, "mousemove", (event) => {
      this.state.mouseX = event.clientX;
      this.state.mouseY = event.clientY;
    });

    // Listen to keyboard events
    useExternalListener(document, "keydown", (event) => {
      if (event.key === "Escape") {
        console.log("Escape pressed!");
      }
    });
  }
}

class ClickOutside extends Component {
  static template = xml`
    <div t-ref="container" class="dropdown">
      <button t-on-click="toggle">Toggle Dropdown</button>
      <ul t-if="state.isOpen" class="dropdown-menu">
        <li>Option 1</li>
        <li>Option 2</li>
        <li>Option 3</li>
      </ul>
    </div>
  `;

  setup() {
    this.containerRef = useRef("container");
    this.state = useState({ isOpen: false });

    // Close dropdown when clicking outside
    useExternalListener(document, "click", (event) => {
      if (this.state.isOpen &&
          this.containerRef.el &&
          !this.containerRef.el.contains(event.target)) {
        this.state.isOpen = false;
      }
    });
  }

  toggle() {
    this.state.isOpen = !this.state.isOpen;
  }
}

Install with Tessl CLI

npx tessl i tessl/npm-odoo--owl

docs

app-components.md

blockdom.md

hooks.md

index.md

lifecycle.md

reactivity.md

templates.md

utils-validation.md

tile.json