[FIX] tutorials/discover_js_framework: clarify instructions
closes odoo/documentation#8182
X-original-commit: 632add350d
Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
This commit is contained in:
parent
cb511183db
commit
3b8bb15933
@ -22,8 +22,7 @@ into the exercises, make sure you have followed all the steps described in this
|
||||
|
||||
In this chapter, we use the `awesome_owl` addon, which provides a simplified environment that
|
||||
only contains Owl and a few other files. The goal is to learn Owl itself, without relying on Odoo
|
||||
web client code. To get started, open the `/awesome_owl` route with your browser: it
|
||||
should display an Owl component with the text *hello world*.
|
||||
web client code.
|
||||
|
||||
.. spoiler:: Solutions
|
||||
|
||||
@ -41,9 +40,11 @@ button.
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
/** @odoo-module **/
|
||||
|
||||
import { Component, useState } from "@odoo/owl";
|
||||
|
||||
class Counter extends Component {
|
||||
export class Counter extends Component {
|
||||
static template = "my_module.Counter";
|
||||
|
||||
setup() {
|
||||
@ -79,8 +80,8 @@ As a first exercise, let us modify the `Playground` component located in
|
||||
`/awesome_owl` route with your browser.
|
||||
|
||||
|
||||
#. Modify :file:`playground.js` so that it acts as a counter like in the example above. You will
|
||||
need to use the `useState hook
|
||||
#. Modify :file:`playground.js` so that it acts as a counter like in the example above.
|
||||
Keep `Playground` for the class name. You will need to use the `useState hook
|
||||
<{OWL_PATH}/doc/reference/hooks.md#usestate>`_ so that the component is re-rendered
|
||||
whenever any part of the state object that has been read by this component is modified.
|
||||
#. In the same component, create an `increment` method.
|
||||
@ -90,6 +91,10 @@ As a first exercise, let us modify the `Playground` component located in
|
||||
<{OWL_PATH}/doc/reference/event_handling.md#event-handling>`_ attribute in the button to
|
||||
trigger the `increment` method whenever the button is clicked.
|
||||
|
||||
.. important::
|
||||
Don't forget :code:`/** @odoo-module **/` in your JavaScript files. More information on this can
|
||||
be found :ref:`here <frontend/modules/native_js>`.
|
||||
|
||||
.. tip::
|
||||
The Odoo JavaScript files downloaded by the browser are minified. For debugging purpose, it's
|
||||
easier when the files are not minified. Switch to
|
||||
@ -110,7 +115,8 @@ see how to create a `sub-component <{OWL_PATH}/doc/reference/component.md#sub-co
|
||||
#. You can do it in the same file first, but once it's done, update your code to move the
|
||||
`Counter` in its own folder and file. Import it relatively from `./counter/counter`. Make sure
|
||||
the template is in its own file, with the same name.
|
||||
#. Add two counters in your playground.
|
||||
#. Use `<Counter/>` in the template of the `Playground` component to add two counters in your
|
||||
playground.
|
||||
|
||||
.. image:: 01_owl_components/double_counter.png
|
||||
:align: center
|
||||
@ -120,10 +126,6 @@ see how to create a `sub-component <{OWL_PATH}/doc/reference/component.md#sub-co
|
||||
as the component. For example, if we have a `TodoList` component, its code should be in
|
||||
`todo_list.js`, `todo_list.xml` and if necessary, `todo_list.scss`
|
||||
|
||||
.. important::
|
||||
Don't forget :code:`/** @odoo-module **/` in your JavaScript files. More information on this can
|
||||
be found :ref:`here <frontend/modules/native_js>`.
|
||||
|
||||
.. _tutorials/discover_js_framework/simple_card:
|
||||
|
||||
3. A simple `Card` component
|
||||
@ -163,7 +165,7 @@ The above example should produce some html using bootstrap that look like this:
|
||||
4. Using `markup` to display html
|
||||
=================================
|
||||
|
||||
If you used `t-esc` in the previous exercise, then you may have noticed that Owl will automatically escape
|
||||
If you used `t-esc` in the previous exercise, then you may have noticed that Owl automatically escapes
|
||||
its content. For example, if you try to display some html like this: `<Card title="'my title'" content="this.html"/>`
|
||||
with `this.html = "<div>some content</div>""`,
|
||||
the resulting output will simply display the html as a string.
|
||||
@ -233,7 +235,7 @@ be called whenever the `Counter` component is incremented.
|
||||
.. important::
|
||||
|
||||
There is a subtlety with callback props: they usually should be defined with the `.bind`
|
||||
suffix. See the `documentation <{OWL_PATH}/doc/reference/props.md#binding-function-props>`_
|
||||
suffix. See the `documentation <{OWL_PATH}/doc/reference/props.md#binding-function-props>`_.
|
||||
|
||||
7. A todo list
|
||||
==============
|
||||
@ -249,7 +251,7 @@ For this tutorial, a `todo` is an object that contains three values: an `id` (nu
|
||||
|
||||
{ id: 3, description: "buy milk", isCompleted: false }
|
||||
|
||||
#. Create a `TodoList` and a `TodoItem` components
|
||||
#. Create a `TodoList` and a `TodoItem` components.
|
||||
#. The `TodoItem` component should receive a `todo` as a prop, and display its `id` and `description` in a `div`.
|
||||
#. For now, hardcode the list of todos:
|
||||
|
||||
@ -258,20 +260,20 @@ For this tutorial, a `todo` is an object that contains three values: an `id` (nu
|
||||
// in TodoList
|
||||
this.todos = useState([{ id: 3, description: "buy milk", isCompleted: false }]);
|
||||
|
||||
#. Use `t-foreach <{OWL_PATH}/doc/reference/templates.md#loops>`_ to display each todo in a `TodoItem`
|
||||
#. Display a `TodoList` in the playground
|
||||
#. Add props validation to `TodoItem`
|
||||
#. Use `t-foreach <{OWL_PATH}/doc/reference/templates.md#loops>`_ to display each todo in a `TodoItem`.
|
||||
#. Display a `TodoList` in the playground.
|
||||
#. Add props validation to `TodoItem`.
|
||||
|
||||
.. image:: 01_owl_components/todo_list.png
|
||||
:align: center
|
||||
|
||||
Note that the `t-foreach` directive is not exactly the same in Owl as the QWeb python implementation: it
|
||||
requires a `t-key` unique value, so Owl can properly reconciliate each element.
|
||||
|
||||
.. tip::
|
||||
|
||||
Since the `TodoList` and `TodoItem` components are so tightly coupled, it makes
|
||||
sense to put them in the same folder
|
||||
sense to put them in the same folder.
|
||||
|
||||
.. note::
|
||||
The `t-foreach` directive is not exactly the same in Owl as the QWeb python implementation: it
|
||||
requires a `t-key` unique value, so that Owl can properly reconcile each element.
|
||||
|
||||
8. Use dynamic attributes
|
||||
=========================
|
||||
@ -281,7 +283,7 @@ using a `dynamic attributes <{OWL_PATH}/doc/reference/templates.md#dynamic-attri
|
||||
|
||||
#. Add the Bootstrap classes `text-muted` and `text-decoration-line-through` on the `TodoItem` root element
|
||||
if it is completed.
|
||||
#. Change the hardcoded `todo` value to check that it is properly displayed.
|
||||
#. Change the hardcoded `this.todos` value to check that it is properly displayed.
|
||||
|
||||
Even though the directive is named `t-att` (for attribute), it can be used to set a `class` value (and
|
||||
html properties such as the `value` of an input).
|
||||
@ -305,7 +307,7 @@ html properties such as the `value` of an input).
|
||||
So far, the todos in our list are hard-coded. Let us make it more useful by allowing the user to add
|
||||
a todo to the list.
|
||||
|
||||
#. Remove the hardcoded values in the `TodoList` component
|
||||
#. Remove the hardcoded values in the `TodoList` component:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
@ -381,7 +383,7 @@ hook functions have to be called in the `setup` method, and no later!
|
||||
|
||||
|
||||
An Owl component goes through a lot of phases: it can be instantiated, rendered,
|
||||
mounted, updated, detached, destroyed, ... This is the `component lifecycle <{OWL_PATH}/doc/reference/component.md#lifecycle>`_.
|
||||
mounted, updated, detached, destroyed... This is the `component lifecycle <{OWL_PATH}/doc/reference/component.md#lifecycle>`_.
|
||||
The figure above show the most important events in the life of a component (hooks are shown in purple).
|
||||
Roughly speaking, a component is created, then updated (potentially many times), then is destroyed.
|
||||
|
||||
@ -444,7 +446,7 @@ component is mounted.
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
this.inputRef = useRef('refname');
|
||||
this.inputRef = useRef('input');
|
||||
|
||||
11. Toggling todos
|
||||
==================
|
||||
@ -452,7 +454,7 @@ component is mounted.
|
||||
Now, let's add a new feature: mark a todo as completed. This is actually trickier than one might
|
||||
think. The owner of the state is not the same as the component that displays it. So, the `TodoItem`
|
||||
component needs to communicate to its parent that the todo state needs to be toggled. One classic
|
||||
way to do this is by using a `callback prop
|
||||
way to do this is by adding a `callback prop
|
||||
<{OWL_PATH}/doc/reference/props.md#binding-function-props>`_ `toggleState`.
|
||||
|
||||
#. Add an input with the attribute :code:`type="checkbox"` before the id of the task, which must
|
||||
@ -463,7 +465,7 @@ way to do this is by using a `callback prop
|
||||
falsy value.
|
||||
|
||||
#. Add a callback props `toggleState` to `TodoItem`.
|
||||
#. Add a `click` event handler on the input in the `TodoItem` component and make sure it calls the
|
||||
#. Add a `change` event handler on the input in the `TodoItem` component and make sure it calls the
|
||||
`toggleState` function with the todo id.
|
||||
#. Make it work!
|
||||
|
||||
@ -503,19 +505,19 @@ The final touch is to let the user delete a todo.
|
||||
|
||||
In a :ref:`previous exercise <tutorials/discover_js_framework/simple_card>`, we built
|
||||
a simple `Card` component. But it is honestly quite limited. What if we want
|
||||
to display some arbitrary content inside a card, such as a sub component? Well,
|
||||
to display some arbitrary content inside a card, such as a sub-component? Well,
|
||||
it does not work, since the content of the card is described by a string. It would
|
||||
however be very convenient if we could describe the content as a piece of template.
|
||||
|
||||
This is exactly what Owl `slot <{OWL_PATH}/doc/reference/slots.md>`_ system is designed
|
||||
This is exactly what Owl's `slot <{OWL_PATH}/doc/reference/slots.md>`_ system is designed
|
||||
for: allowing to write generic components.
|
||||
|
||||
Let us modify the `Card` component to use slots:
|
||||
|
||||
#. Remove the `content` prop
|
||||
#. Use the default slot to define the body
|
||||
#. Insert a few cards with arbitrary content, such as a `Counter` component
|
||||
#. (bonus) Add prop validation
|
||||
#. Remove the `content` prop.
|
||||
#. Use the default slot to define the body.
|
||||
#. Insert a few cards with arbitrary content, such as a `Counter` component.
|
||||
#. (bonus) Add prop validation.
|
||||
|
||||
.. image:: 01_owl_components/generic_card.png
|
||||
:align: center
|
||||
@ -526,6 +528,8 @@ Let us modify the `Card` component to use slots:
|
||||
14. Minimizing card content
|
||||
===========================
|
||||
|
||||
.. TODO: This exercise shows no new concept; it should probably be removed.
|
||||
|
||||
Finally, let's add a feature to the `Card` component, to make it more interesting: we
|
||||
want a button to toggle its content (show it or hide it)
|
||||
|
||||
|
@ -74,10 +74,10 @@ Theory: Services
|
||||
In practice, every component (except the root component) may be destroyed at any time and replaced
|
||||
(or not) with another component. This means that each component internal state is not persistent.
|
||||
This is fine in many cases, but there certainly are situations where we want to keep some data around.
|
||||
For example, all discuss messages should not be reloaded every time we display a channel.
|
||||
For example, all Discuss messages should not be reloaded every time we display a channel.
|
||||
|
||||
Also, it may happen that we need to write some code that is not a component. Maybe something that
|
||||
process all barcodes, or that manages the user configuration (context, ...).
|
||||
process all barcodes, or that manages the user configuration (context, etc.).
|
||||
|
||||
The Odoo framework defines the idea of a :ref:`service <frontend/services>`, which is a persistent
|
||||
piece of code that exports state and/or functions. Each service can depend on other services, and
|
||||
@ -90,13 +90,13 @@ The following example registers a simple service that displays a notification ev
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
const myService = {
|
||||
dependencies: ["notification"],
|
||||
start(env, { notification }) {
|
||||
let counter = 1;
|
||||
setInterval(() => {
|
||||
notification.add(`Tick Tock ${counter++}`);
|
||||
}, 5000);
|
||||
},
|
||||
dependencies: ["notification"],
|
||||
start(env, { notification }) {
|
||||
let counter = 1;
|
||||
setInterval(() => {
|
||||
notification.add(`Tick Tock ${counter++}`);
|
||||
}, 5000);
|
||||
},
|
||||
};
|
||||
|
||||
registry.category("services").add("myService", myService);
|
||||
@ -110,18 +110,17 @@ state:
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
const sharedStateService = {
|
||||
start(env) {
|
||||
let state = {};
|
||||
|
||||
return {
|
||||
getValue(key) {
|
||||
return state[key];
|
||||
},
|
||||
setValue(key, value) {
|
||||
state[key] = value;
|
||||
},
|
||||
};
|
||||
},
|
||||
start(env) {
|
||||
let state = {};
|
||||
return {
|
||||
getValue(key) {
|
||||
return state[key];
|
||||
},
|
||||
setValue(key, value) {
|
||||
state[key] = value;
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
registry.category("services").add("shared_state", sharedStateService);
|
||||
@ -141,6 +140,8 @@ Then, any component can do this:
|
||||
2. Add some buttons for quick navigation
|
||||
========================================
|
||||
|
||||
.. TODO: Add ref to the action service when it's documented.
|
||||
|
||||
One important service provided by Odoo is the `action` service: it can execute
|
||||
all kind of standard actions defined by Odoo. For example, here is how one
|
||||
component could execute an action by its xml id:
|
||||
@ -163,39 +164,39 @@ Let us now add two buttons to our control panel:
|
||||
exists, so you should use `its xml id
|
||||
<https://github.com/odoo/odoo/blob/1f4e583ba20a01f4c44b0a4ada42c4d3bb074273/odoo/addons/base/views/res_partner_views.xml#L510>`_).
|
||||
|
||||
#. A button `Leads`, which opens a dynamic action on the `crm.lead` model with a list and a form view.
|
||||
#. A button `Leads`, which opens a dynamic action on the `crm.lead` model with a list and a form
|
||||
view. Follow the example of `this use of the action service
|
||||
<https://github.com/odoo/odoo/blob/ef424a9dc22a5abbe7b0a6eff61cf113826f04c0/addons/account
|
||||
/static/src/components/journal_dashboard_activity/journal_dashboard_activity.js#L28-L35>`_.
|
||||
|
||||
.. image:: 02_web_framework/navigation_buttons.png
|
||||
:align: center
|
||||
|
||||
.. seealso::
|
||||
- `Example: doAction use
|
||||
<{GITHUB_PATH}/addons/account/static/src/components/journal_dashboard_activity
|
||||
/journal_dashboard_activity.js#L35>`_
|
||||
- `Code: action service
|
||||
<{GITHUB_PATH}/addons/web/static/src/webclient/actions/action_service.js>`_
|
||||
`Code: action service
|
||||
<{GITHUB_PATH}/addons/web/static/src/webclient/actions/action_service.js>`_
|
||||
|
||||
3. Add a DashboardItem
|
||||
======================
|
||||
3. Add a dashboard item
|
||||
=======================
|
||||
|
||||
Let us now improve our content.
|
||||
|
||||
#. Create a generic `DashboardItem` component that display its default slot in a nice card layout
|
||||
It should take an optional `size` number props, that default to `1`
|
||||
The width should be hardcoded to `(18*size)rem`.
|
||||
#. Add a few cards in the dashboard, with no size and a size of 2.
|
||||
#. Create a generic `DashboardItem` component that display its default slot in a nice card layout.
|
||||
It should take an optional `size` number props, that default to `1`. The width should be
|
||||
hardcoded to `(18*size)rem`.
|
||||
#. Add two cards to the dashboard. One with no size, and the other with a size of 2.
|
||||
|
||||
.. image:: 02_web_framework/dashboard_item.png
|
||||
:align: center
|
||||
|
||||
.. seealso::
|
||||
- `Owl slot system <{OWL_PATH}/doc/reference/slots.md>`_
|
||||
`Owl's slot system <{OWL_PATH}/doc/reference/slots.md>`_
|
||||
|
||||
4. Call the server, add some statistics
|
||||
=======================================
|
||||
|
||||
Let's improve the dashboard by adding a few dashboard items to display *real* business data.
|
||||
The *awesome_dashboard* addon provides a `/awesome_dashboard/statistics` route that is meant
|
||||
The `awesome_dashboard` addon provides a `/awesome_dashboard/statistics` route that is meant
|
||||
to return some interesting information.
|
||||
|
||||
To call a specific controller, we need to use the :ref:`rpc service <frontend/services/rpc>`.
|
||||
@ -226,11 +227,7 @@ A basic request could look like this:
|
||||
:align: center
|
||||
|
||||
.. seealso::
|
||||
|
||||
- `Code: rpc service <{GITHUB_PATH}/addons/web/static/src/core/network/rpc_service.js>`_
|
||||
- `Example: calling a route in onWillStart
|
||||
<https://github.com/odoo/odoo/blob/1f4e583ba20a01f4c44b0a4ada42c4d3bb074273/
|
||||
addons/lunch/static/src/views/search_model.js#L21>`_
|
||||
`Code: rpc service <{GITHUB_PATH}/addons/web/static/src/core/network/rpc_service.js>`_
|
||||
|
||||
5. Cache network calls, create a service
|
||||
========================================
|
||||
@ -246,9 +243,9 @@ would prefer to do it only the first time, so we actually need to maintain some
|
||||
always return the same information.
|
||||
#. Use the `memoize <https://github.com/odoo/odoo/blob/1f4e583ba20a01f4c44b0a4ada42c4d3bb074273/
|
||||
addons/web/static/src/core/utils/functions.js#L11>`_ utility function from
|
||||
`@web/core/utils/functions` that will allow caching the statistics.
|
||||
`@web/core/utils/functions` that allows caching the statistics.
|
||||
#. Use this service in the `Dashboard` component.
|
||||
#. Check that it works as expected
|
||||
#. Check that it works as expected.
|
||||
|
||||
.. seealso::
|
||||
- `Example: simple service <{GITHUB_PATH}/addons/web/static/src/core/network/http_service.js>`_
|
||||
@ -266,15 +263,15 @@ by the graph view. However, it is not loaded by default, so we will need to eith
|
||||
assets bundle, or lazy load it. Lazy loading is usually better since our users will not have to load
|
||||
the chartjs code every time if they don't need it.
|
||||
|
||||
#. Create a `PieChart` component
|
||||
#. Create a `PieChart` component.
|
||||
#. In its `onWillStart` method, load chartjs, you can use the `loadJs
|
||||
<https://github.com/odoo/odoo/blob/1f4e583ba20a01f4c44b0a4ada42c4d3bb074273/
|
||||
addons/web/static/src/core/assets.js#L23>`_ function to load
|
||||
:file:`/web/static/lib/Chart/Chart.js`.
|
||||
#. Use the `PieChart` component in a `DashboardItem` to display a `pie chart
|
||||
<https://www.chartjs.org/docs/2.8.0/charts/doughnut.html>`_ that shows the
|
||||
correct quantity for each sold t-shirts in each size (that information is available in the
|
||||
statistics route). Note that you can use the `size` property to make it look larger
|
||||
quantity for each sold t-shirts in each size (that information is available in the
|
||||
`/statistics` route). Note that you can use the `size` property to make it look larger.
|
||||
#. The `PieChart` component will need to render a canvas, and draw on it using `chart.js`.
|
||||
#. Make it work!
|
||||
|
||||
@ -293,11 +290,11 @@ the chartjs code every time if they don't need it.
|
||||
7. Real life update
|
||||
===================
|
||||
|
||||
Since we moved the data loading in a cache, it does not ever updates. But let us say that we
|
||||
Since we moved the data loading in a cache, it never updates. But let us say that we
|
||||
are looking at fast moving data, so we want to periodically (for example, every 10min) reload
|
||||
fresh data.
|
||||
|
||||
This is quite simple to implement, with a `setTimeout` or `setInterval` in the dashboard service.
|
||||
This is quite simple to implement, with a `setTimeout` or `setInterval` in the statistics service.
|
||||
However, here is the tricky part: if the dashboard is currently being displayed, it should be
|
||||
updated immediately.
|
||||
|
||||
@ -306,7 +303,7 @@ but not linked to any component. A component can then do a `useState` on it to s
|
||||
changes.
|
||||
|
||||
|
||||
#. Update the dashboard service to reload data every 10 minutes (to test it, use 10s instead!)
|
||||
#. Update the statistics service to reload data every 10 minutes (to test it, use 10s instead!)
|
||||
#. Modify it to return a `reactive <{OWL_PATH}/doc/reference/reactivity.md#reactive>`_ object.
|
||||
Reloading data should update the reactive object in place.
|
||||
#. The `Dashboard` component can now use it with a `useState`
|
||||
@ -328,13 +325,13 @@ look at it.
|
||||
To do that, we will need to create a new bundle containing all our dashboard assets,
|
||||
then use the `LazyComponent` (located in `@web/core/assets`).
|
||||
|
||||
#. Move all dashboard assets into a sub folder `/dashboard` to make it easier to
|
||||
#. Move all dashboard assets into a sub folder :file:`/dashboard` to make it easier to
|
||||
add to a bundle.
|
||||
#. Create a `awesome_dashboard.dashboard` assets bundle containing all content of
|
||||
the `/dashboard` folder
|
||||
#. Modify `dashboard.js` to register itself in the `lazy_components` registry, and not
|
||||
the `/dashboard` folder.
|
||||
#. Modify :file:`dashboard.js` to register itself in the `lazy_components` registry, and not
|
||||
in the `action` registry.
|
||||
#. Add in `src/` a file `dashboard_action` that import `LazyComponent` and register
|
||||
#. Add in :file:`src/` a file :file:`dashboard_action` that imports `LazyComponent` and registers
|
||||
it to the `action` registry
|
||||
|
||||
9. Making our dashboard generic
|
||||
@ -342,15 +339,15 @@ then use the `LazyComponent` (located in `@web/core/assets`).
|
||||
|
||||
So far, we have a nice working dashboard. But it is currently hardcoded in the dashboard
|
||||
template. What if we want to customize our dashboard? Maybe some users have different
|
||||
needs, and want to see some other data.
|
||||
needs and want to see other data.
|
||||
|
||||
So, the next step is then to make our dashboard generic: instead of hardcoding its content
|
||||
So, the next step is to make our dashboard generic: instead of hard-coding its content
|
||||
in the template, it can just iterate over a list of dashboard items. But then, many
|
||||
questions comes up: how to represent a dashboard item, how to register it, what data
|
||||
questions come up: how to represent a dashboard item, how to register it, what data
|
||||
should it receive, and so on. There are many different ways to design such a system,
|
||||
with different trade offs.
|
||||
with different trade-offs.
|
||||
|
||||
For this tutorial, we will say that a dashboard item is an object with the folowing structure:
|
||||
For this tutorial, we will say that a dashboard item is an object with the following structure:
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
@ -367,7 +364,7 @@ For this tutorial, we will say that a dashboard item is an object with the folow
|
||||
};
|
||||
|
||||
The `description` value will be useful in a later exercise to show the name of items that the
|
||||
user can choose to add to his dashboard. The `size` number is optional, and simply describes
|
||||
user can add to their dashboard. The `size` number is optional, and simply describes
|
||||
the size of the dashboard item that will be displayed. Finally, the `props` function is optional.
|
||||
If not given, we will simply give the `statistics` object as data. But if it is defined, it will
|
||||
be used to compute specific props for the component.
|
||||
@ -383,16 +380,16 @@ The goal is to replace the content of the dashboard with the following snippet:
|
||||
</DashboardItem>
|
||||
</t>
|
||||
|
||||
Note that the above example features two advanced features of Owl: dynamic components, and dynamic props.
|
||||
Note that the above example features two advanced features of Owl: dynamic components and dynamic props.
|
||||
|
||||
We currently have two kinds of item components: number cards, with a title and a number, and pie cards, with
|
||||
We currently have two kinds of item components: number cards with a title and a number, and pie cards with
|
||||
some label and a pie chart.
|
||||
|
||||
#. create and implement two components: `NumberCard` and `PieChartCard`, with the corresponding props
|
||||
#. create a file `dashboard_items.js` in which you define and export a list of items, using `NumberCard`
|
||||
and `PieChartCard` to recreate our current dashboard
|
||||
#. import that list of items in our `Dashboard` component, add it to the component, and update the template
|
||||
to use a `t-foreach` like shown above
|
||||
#. Create and implement two components: `NumberCard` and `PieChartCard`, with the corresponding props.
|
||||
#. Create a file :file:`dashboard_items.js` in which you define and export a list of items, using `NumberCard`
|
||||
and `PieChartCard` to recreate our current dashboard.
|
||||
#. Import that list of items in our `Dashboard` component, add it to the component, and update the template
|
||||
to use a `t-foreach` like shown above.
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
@ -410,27 +407,26 @@ However, the content of our item list is still hardcoded. Let us fix that by usi
|
||||
#. Instead of exporting a list, register all dashboard items in a `awesome_dashboard` registry
|
||||
#. Import all the items of the `awesome_dashboard` registry in the `Dashboard` component
|
||||
|
||||
The dashboard is now easily extensible. Any other odoo addon that want to register a new item to the
|
||||
The dashboard is now easily extensible. Any other Odoo addon that wants to register a new item to the
|
||||
dashboard can just add it to the registry.
|
||||
|
||||
11. Add and remove dashboard items
|
||||
==================================
|
||||
|
||||
Let us see how we can make our dashboard customizable. To make it simple, we will save the user
|
||||
dashboard configuration in the local storage, so it is persistent, but we don't have to deal
|
||||
dashboard configuration in the local storage so that it is persistent, but we don't have to deal
|
||||
with the server for now.
|
||||
|
||||
The dashboard configuration will be saved as a list of removed item ids.
|
||||
|
||||
#. Add a button in the control panel with a gear icon, to indicate that it is a settings button
|
||||
#. Clicking on that button should open a dialog
|
||||
#. In that dialog, we want to see a list of all existing dashboard items, each with a checkbox
|
||||
#. Add a button in the control panel with a gear icon to indicate that it is a settings button.
|
||||
#. Clicking on that button should open a dialog.
|
||||
#. In that dialog, we want to see a list of all existing dashboard items, each with a checkbox.
|
||||
#. There should be a `Apply` button in the footer. Clicking on it will build a list of all item ids
|
||||
that are unchecked
|
||||
#. We want to store that value in the local storage
|
||||
that are unchecked.
|
||||
#. We want to store that value in the local storage.
|
||||
#. And modify the `Dashboard` component to filter the current items by removing the ids of items
|
||||
from the configuration
|
||||
|
||||
from the configuration.
|
||||
|
||||
.. image:: 02_web_framework/items_configuration.png
|
||||
:width: 80%
|
||||
@ -443,10 +439,10 @@ Here is a list of some small improvements you could try to do if you have the ti
|
||||
|
||||
#. Make sure your application can be :ref:`translated <reference/translations>` (with
|
||||
`env._t`).
|
||||
#. Clicking on a section of the pie chart should open a list view of all orders which have the
|
||||
#. Clicking on a section of the pie chart should open a list view of all orders that have the
|
||||
corresponding size.
|
||||
#. Save the content of the dashboard in a user settings on the server!
|
||||
#. Make it responsive: in mobile mode, each card should take 100% of the width
|
||||
#. Save the content of the dashboard in a user setting on the server!
|
||||
#. Make it responsive: in mobile mode, each card should take 100% of the width.
|
||||
|
||||
.. seealso::
|
||||
- `Example: use of env._t function
|
||||
|
Loading…
Reference in New Issue
Block a user