Skip to content

Commit 5518b4e

Browse files
committed
[IMP] awesome_owl: add basic Owl components as part of JS framework tutorial
Introduce initial Owl components as part of the JavaScript framework learning exercise. Includes examples of reactive state, component composition, prop passing, template rendering, and event handling using Owl.
1 parent 6792087 commit 5518b4e

20 files changed

+255
-15
lines changed

awesome_owl/__manifest__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
'web/static/lib/bootstrap/scss/_variables.scss',
3434
'web/static/lib/bootstrap/scss/_maps.scss',
3535
('include', 'web._assets_bootstrap'),
36+
("include", "web._assets_bootstrap_backend"),
3637
('include', 'web._assets_core'),
3738
'web/static/src/libs/fontawesome/css/font-awesome.css',
3839
'awesome_owl/static/src/**/*',
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/** @odoo-module **/
2+
3+
import { Component } from "@odoo/owl";
4+
5+
export class Card extends Component{
6+
static template = "awesome_owl.card";
7+
8+
static props = {
9+
title: String,
10+
slots: { type: Object, optional: true }
11+
};
12+
13+
state = {
14+
isOpen: true,
15+
};
16+
17+
toggleContent() {
18+
console.log("toggleContent called");
19+
this.state.isOpen = !this.state.isOpen;
20+
console.log("state.isOpen:", this.state.isOpen);
21+
}
22+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<templates xml:space="preserve">
3+
<t t-name="awesome_owl.card">
4+
<div class="card d-inline-block m-2" style="width: 18rem;">
5+
<div class="card-body">
6+
<div class="d-flex justify-content-between align-items-center mb-2">
7+
<h5 class="card-title mb-0">
8+
<t t-esc="props.title"/>
9+
</h5>
10+
<button type="button" class="btn btn-sm btn-outline-primary" t-on-click="toggleContent">
11+
<t t-esc="state.isOpen ? '−' : '+'"/>
12+
</button>
13+
</div>
14+
15+
<div class="card-text" t-if="state.isOpen">
16+
<t t-slot="default"/>
17+
</div>
18+
</div>
19+
</div>
20+
</t>
21+
</templates>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/** @odoo-module **/
2+
3+
import { Component, useState } from "@odoo/owl";
4+
5+
export class Counter extends Component {
6+
static template = "awesome_owl.counter";
7+
static props = {
8+
onChange: { type: Function, optional: true }
9+
};
10+
11+
setup() {
12+
this.state = useState({ counterValue: 0 });
13+
}
14+
15+
increment() {
16+
this.state.counterValue++;
17+
this.notifyParent(1);
18+
}
19+
20+
decrement() {
21+
this.state.counterValue--;
22+
this.notifyParent(-1);
23+
}
24+
25+
notifyParent(delta) {
26+
if (this.props.onChange) {
27+
this.props.onChange(delta);
28+
}
29+
}
30+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<templates xml:space="preserve">
3+
4+
<t t-name="awesome_owl.counter">
5+
<div style="display: flex; align-items: center; gap: 10px; border: 1px solid #ccc; padding: 10px; border-radius: 8px;">
6+
<span>Hello World</span>
7+
<span>Counter: <t t-esc="state.counterValue" /></span>
8+
<button class="btn btn-primary" t-on-click="increment">
9+
Increment
10+
</button>
11+
<button class="btn btn-primary" t-on-click="decrement">
12+
Decrement
13+
</button>
14+
</div>
15+
</t>
16+
</templates>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/** @odoo-module **/
2+
3+
import { Component } from "@odoo/owl";
4+
5+
export class TodoItem extends Component {
6+
static template = "awesome_owl.todo_item";
7+
8+
static props = {
9+
todo: Object,
10+
toggleState: Function,
11+
removeTodo: Function,
12+
13+
};
14+
onRemove() {
15+
this.props.removeTodo(this.props.todo.id);
16+
}
17+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<t t-name="awesome_owl.todo_item" t-bind="props">
2+
<p class="card-text d-flex justify-content-between align-items-center"
3+
t-att-class="props.todo.isCompleted ? 'text-decoration-line-through text-muted' : ''">
4+
<span>
5+
<input type="checkbox"
6+
t-att-checked="props.todo.isCompleted"
7+
t-on-change="() => props.toggleState(props.todo.id)"/>
8+
<strong class="ms-2"><t t-esc="props.todo.id"/></strong>:
9+
<t t-esc="props.todo.description"/>
10+
</span>
11+
<span class="fa fa-remove text-danger ms-3"
12+
role="button"
13+
t-on-click="() => props.removeTodo(props.todo.id)"></span>
14+
</p>
15+
</t>
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/** @odoo-module **/
2+
3+
import { Component, useState} from "@odoo/owl";
4+
import { TodoItem } from "../todo_item/todo_item";
5+
import { useAutofocus } from "@awesome_owl/utils/utils";
6+
7+
8+
export class TodoList extends Component {
9+
static template = "awesome_owl.todo_list";
10+
static components = {TodoItem};
11+
static props = {};
12+
13+
14+
setup() {
15+
this.todos = useState([]);
16+
this.ID = 1;
17+
this.todoInput = useAutofocus("todoInput");
18+
19+
}
20+
21+
addTodo(event){
22+
if(event.keyCode == 13){
23+
const inputVal = event.target.value.trim();
24+
25+
if(inputVal.length === 0){
26+
return;
27+
}
28+
29+
this.todos.push({
30+
id: this.ID++,
31+
description: inputVal,
32+
isCompleted: false,
33+
});
34+
35+
event.target.value = "";
36+
}
37+
}
38+
39+
toggleState = (todoId) => {
40+
const todo = this.todos.find(t => t.id === todoId);
41+
if (todo) {
42+
todo.isCompleted = !todo.isCompleted;
43+
} else {
44+
console.error("Todo not found for id:", todoId);
45+
}
46+
};
47+
48+
removeTodo = (todoId) => {
49+
const index = this.todos.findIndex(t => t.id === todoId);
50+
if (index >= 0) {
51+
this.todos.splice(index, 1);
52+
} else {
53+
console.error("Todo not found for id:", todoId);
54+
}
55+
56+
};
57+
58+
59+
60+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<t t-name="awesome_owl.todo_list" t-bind="this">
2+
<div class="card m-3 shadow-sm" style="width: 24rem;">
3+
<div class="card-body">
4+
<h5 class="card-title">Todo List</h5>
5+
<input type="text" class="form-control mb-3" placeholder="Enter a new task" t-on-keyup="addTodo" t-ref="todoInput"/>
6+
<div>
7+
<t t-foreach="todos" t-as="todo" t-key="todo.id">
8+
<TodoItem t-props="{ todo: todo, toggleState: toggleState, removeTodo: removeTodo }"/>
9+
</t>
10+
</div>
11+
</div>
12+
</div>
13+
</t>

awesome_owl/static/src/playground.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,28 @@
11
/** @odoo-module **/
22

3-
import { Component } from "@odoo/owl";
3+
import { Component, markup, useState } from "@odoo/owl";
4+
import { Counter } from "./components/counter/counter"
5+
import { Card } from "./components/card/card"
6+
import { TodoList } from "./components/todo/todo_list/todo-list";
47

58
export class Playground extends Component {
69
static template = "awesome_owl.playground";
10+
static components = {Counter, Card, TodoList};
11+
static props = {};
12+
13+
setup() {
14+
15+
this.state = useState({
16+
sum: 0
17+
});
18+
19+
this.adjustSum = (delta) => {
20+
this.state.sum += delta;
21+
};
22+
23+
24+
this.htmlContent = markup("<div class='text_primary' style='color: DodgerBlue;'>This is bold HTML content</div>");
25+
this.plainContent = "<div class='text_primary'>This will be escaped</div>";
26+
}
27+
728
}

0 commit comments

Comments
 (0)