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

templates.mddocs/

Template System

XML-based template system with compilation, runtime evaluation, and template management for component rendering.

Capabilities

xml Template Function

Creates template strings using tagged template literals for component templates.

/**
 * Tagged template literal for creating XML templates
 * @param args - Template string parts and interpolated values
 * @returns Template string with unique identifier
 */
function xml(...args: Parameters<typeof String.raw>): string;

Usage Examples:

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

// Basic template
class SimpleComponent extends Component {
  static template = xml`
    <div class="simple">
      <h1>Hello World!</h1>
    </div>
  `;
}

// Template with dynamic content
class DynamicComponent extends Component {
  static template = xml`
    <div>
      <h1><t t-esc="props.title" /></h1>
      <p t-if="state.showDescription">
        <t t-esc="props.description" />
      </p>
      <button t-on-click="toggleDescription">
        <t t-esc="state.showDescription ? 'Hide' : 'Show'" />
      </button>
    </div>
  `;

  setup() {
    this.state = useState({ showDescription: false });
  }

  toggleDescription() {
    this.state.showDescription = !this.state.showDescription;
  }
}

// Template with loops and conditions
class ListComponent extends Component {
  static template = xml`
    <div class="list-container">
      <h2>Items (<t t-esc="props.items.length" />)</h2>
      <ul t-if="props.items.length > 0">
        <li t-foreach="props.items" t-as="item" t-key="item.id"
            t-att-class="{ 'completed': item.completed }">
          <span t-esc="item.title" />
          <button t-on-click="() => this.toggleItem(item.id)">
            <t t-esc="item.completed ? 'Undo' : 'Complete'" />
          </button>
        </li>
      </ul>
      <p t-else="">No items found.</p>
    </div>
  `;

  toggleItem(id) {
    const item = this.props.items.find(i => i.id === id);
    if (item) {
      item.completed = !item.completed;
    }
  }
}

// Template with sub-components
class ParentComponent extends Component {
  static template = xml`
    <div class="parent">
      <Header title="props.title" />
      <main>
        <TodoList items="state.todos" />
      </main>
      <Footer />
    </div>
  `;

  static components = { Header, TodoList, Footer };

  setup() {
    this.state = useState({
      todos: [
        { id: 1, title: "Learn OWL", completed: false }
      ]
    });
  }
}

Template Configuration

Configuration options for template sets and compilation.

/**
 * Template set configuration options
 */
interface TemplateSetConfig {
  /** Enable development mode with additional checks */
  dev?: boolean;
  /** List of attributes that should be translated */
  translatableAttributes?: string[];
  /** Function to translate strings */
  translateFn?: (s: string, translationCtx: string) => string;
  /** Initial templates as string, Document, or record */
  templates?: string | Document | Record<string, string>;
  /** Custom template getter function */
  getTemplate?: (s: string) => Element | Function | string | void;
  /** Custom directive definitions */
  customDirectives?: customDirectives;
  /** Global values object for template access */
  globalValues?: object;
}

Usage Examples:

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

// Internationalization setup
const i18nTemplateSet = new TemplateSet({
  dev: process.env.NODE_ENV === "development",
  translateFn: (text, context) => {
    return i18n.t(text, { context });
  },
  translatableAttributes: [
    "title", "placeholder", "aria-label", "aria-description",
    "alt", "label", "data-tooltip"
  ],
  globalValues: {
    formatDate: (date) => new Intl.DateTimeFormat().format(date),
    formatCurrency: (amount, currency) => new Intl.NumberFormat('en', {
      style: 'currency',
      currency
    }).format(amount)
  }
});

// Template set with custom directives
const customTemplateSet = new TemplateSet({
  customDirectives: {
    "tooltip": {
      compile(node, directive, context) {
        const expr = directive.value;
        return {
          pre: `node.setAttribute('data-tooltip', ${expr});`,
          post: `initTooltip(node);`
        };
      }
    },
    "lazy-load": {
      compile(node, directive, context) {
        return {
          pre: `
            if ('IntersectionObserver' in window) {
              const observer = new IntersectionObserver((entries) => {
                entries.forEach(entry => {
                  if (entry.isIntersecting) {
                    entry.target.src = entry.target.dataset.src;
                    observer.unobserve(entry.target);
                  }
                });
              });
              observer.observe(node);
            }
          `
        };
      }
    }
  }
});

// App with custom template configuration
const app = new App(RootComponent, {
  dev: true,
  templates: `
    <templates>
      <t t-name="shared-button">
        <button t-att-class="className" t-on-click="onClick">
          <t t-esc="label" />
        </button>
      </t>
    </templates>
  `,
  translateFn: (text) => translations[text] || text,
  globalValues: {
    utils: {
      capitalize: (str) => str.charAt(0).toUpperCase() + str.slice(1),
      truncate: (str, length) => str.length > length ? str.slice(0, length) + "..." : str
    }
  }
});

Template Directives

Core Directives

// Content directives
t-esc="expression"          // Escaped text content
t-raw="expression"          // Raw HTML content
t-out="expression"          // Output expression

// Conditional rendering
t-if="condition"            // Conditional rendering
t-elif="condition"          // Else-if condition
t-else=""                   // Else condition

// Loops
t-foreach="items" t-as="item" t-key="item.id"  // Loop over items

// Attributes
t-att="object"              // Set multiple attributes from object
t-att-class="expression"    // Dynamic class attribute
t-att-style="expression"    // Dynamic style attribute
t-att-[attr]="expression"  // Dynamic single attribute

// Event handling
t-on-[event]="handler"      // Event listener
t-on-[event].prevent="handler"    // With preventDefault
t-on-[event].stop="handler"       // With stopPropagation

// Components
t-component="ComponentClass"      // Render component
t-props="propsObject"            // Pass props to component

// References and slots
t-ref="refName"             // Element reference
t-slot="slotName"           // Named slot
t-set="variable" t-value="expression"  // Set variable

// Sub-templates
t-call="templateName"       // Call named template

Template Best Practices

  • Use t-key for list items to optimize rendering
  • Prefer t-esc over t-raw for security
  • Use t-att-class with objects for conditional classes
  • Cache complex expressions in computed properties
  • Use sub-templates for reusable template parts

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