Skip to content

maorb - Web Framework Tutorial#1305

Open
Mark-Orban wants to merge 2 commits into
odoo:19.0from
odoo-dev:19.0-web-framework-tutorial-maorb
Open

maorb - Web Framework Tutorial#1305
Mark-Orban wants to merge 2 commits into
odoo:19.0from
odoo-dev:19.0-web-framework-tutorial-maorb

Conversation

@Mark-Orban
Copy link
Copy Markdown

No description provided.

@robodoo
Copy link
Copy Markdown

robodoo commented May 28, 2026

Pull request status dashboard

@Mark-Orban Mark-Orban requested a review from Megaaaaaa May 28, 2026 13:52
@Mark-Orban Mark-Orban self-assigned this Jun 1, 2026
Copy link
Copy Markdown

@Megaaaaaa Megaaaaaa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello hello 👋

Here's a review for you! Looks pretty good, it's only a few comments about stuff that we tend to do another way and that I thought it'd be best to show you. 👍

Tell me if anything is unclear or if you have any question!

Comment on lines +5 to +9
<t t-set-slot="layout-buttons">
<button class="btn btn-primary" t-on-click="openCustomers">Customers</button>
<button class="btn btn-primary" t-on-click="openLeads">Leads</button>
</t>
<Layout className="'o_dashboard h-100'">
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any slot meant for a component is usually declared inside the component. As this is not the first time I see this, I suppose this is how they tell you to do it in the tutorial but in my experience, we will always put it inside the component. Just so you know 👍

Suggested change
<t t-set-slot="layout-buttons">
<button class="btn btn-primary" t-on-click="openCustomers">Customers</button>
<button class="btn btn-primary" t-on-click="openLeads">Leads</button>
</t>
<Layout className="'o_dashboard h-100'">
<Layout className="'o_dashboard h-100'">
<t t-set-slot="layout-buttons">

static template = "awesome_dashboard.PieChart";
static props = {
title: String,
data: Object,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The chart data comes from a statistics service that is filled asynchronously, so it is normal for statistics.orders_by_size to be missing on the first render. Declaring data as required makes the child component stricter than the parent data flow can guarantee. Let's make it optional for safety measures 👍

Suggested change
data: Object,
data: {
type: Object,
optional: true,
},

this.renderChart();
});
onPatched(() => {
this.chart.destroy();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't overlook that onPatched can run before a chart exists, especially while the async statistics are still loading. Leaving this unguarded accepts a runtime error path during a normal loading state.

Suggested change
this.chart.destroy();
this.chart?.destroy();

Comment on lines +27 to +28
const labels = Object.keys(this.props.data);
const data = Object.values(this.props.data);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the dashboard statistics are loaded asynchronously, this.props.data can be undefined when the component first renders and calling Object.keys on undefined would crash. Just normalize the props and we're good to go 👍

Suggested change
const labels = Object.keys(this.props.data);
const data = Object.values(this.props.data);
const chartData = this.props.data || {};
const labels = Object.keys(chartData);
const values = Object.values(chartData);

@@ -1,5 +1,17 @@
import { Component } from "@odoo/owl";
import { Component, markup, useState } from "@odoo/owl";
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unused import 😉

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

<div class="p-3">
<div>
hello world
<Counter onChange="() => this.incrementSum()"/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yo ucan use Owl's .bind syntax for passing component methods to children. It works just as good but looks a bit better and also, it's the odo oway 😄

Suggested change
<Counter onChange="() => this.incrementSum()"/>
<Counter onChange.bind="incrementSum"/>

<span t-else="">
<t t-out="props.todo.id"/>. <t t-out="props.todo.description"/>
</span>
<span class="fa fa-remove" t-on-click="onRemove"/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose this is the way the tutorial tells you to do it as I've seen it multiple times but for your information, when we make an action clickable, we really want to put it in a because it works just as good with the mouse but helps a lot with the keyboard usage and also some assistive controls. Don't make much a difference here but now you know 👍

Suggested change
<span class="fa fa-remove" t-on-click="onRemove"/>
<button type="button" class="btn btn-link p-0" t-on-click="onRemove">
<i class="fa fa-remove"/>
</button>

}

addTodo(event) {
if (event.keyCode === 13 && event.target.value != "") {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We prefer using the key name instead of the key code as it is much more self explanatory and we like self explanatory. "When a user clicks 'Enter' then this happens." :)

Also I would have trimmed the input to make sure we do no accept only spaces as an input but that's a preference thing. Altough if you decide to do that, then you need to save event.target.value.trim() instead of event.target.value right below. 👍

Suggested change
if (event.keyCode === 13 && event.target.value != "") {
if (event.key === "Enter" && event.target.value.trim() !== "") {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants