mirror of
https://github.com/odoo/documentation.git
synced 2026-01-03 02:09:27 +07:00
wip
This commit is contained in:
@@ -2,298 +2,286 @@
|
||||
Chapter 1: Build a Clicker game
|
||||
===============================
|
||||
|
||||
In the previous task, we learned how to create fields and views. There is still much more to
|
||||
discover in the feature-rich Odoo web framework, so let's dive in and explore more in this chapter!
|
||||
For this project, we will build together a `clicker game <https://en.wikipedia.org/wiki/Incremental_game>`_,
|
||||
completely integrated with Odoo. In this game, the goal is to accumulate a large number of clicks, and
|
||||
to automate the system. The interesting part is that we will use the Odoo user interface as our playground.
|
||||
For example, we will hide bonuses in some random parts of the web client.
|
||||
|
||||
.. graph TD
|
||||
.. subgraph "Owl"
|
||||
.. C[Component]
|
||||
.. T[Template]
|
||||
.. H[Hook]
|
||||
.. S[Slot]
|
||||
.. E[Event]
|
||||
.. end
|
||||
To get started, you need a running Odoo server and a development environment. Before getting
|
||||
into the exercises, make sure you have followed all the steps described in this
|
||||
:ref:`tutorial introduction <tutorials/master_odoo_web_framework/setup>`.
|
||||
|
||||
.. subgraph "odoo"[Odoo Javascript framework]
|
||||
.. Services
|
||||
.. Translation
|
||||
.. lazy[Lazy loading libraries]
|
||||
.. SCSS
|
||||
.. action --> Services
|
||||
.. rpc --> Services
|
||||
.. orm --> Services
|
||||
.. Fields
|
||||
.. Views
|
||||
.. Registries
|
||||
.. end
|
||||
|
||||
.. odoo[Odoo JavaScript framework] --> Owl
|
||||
|
||||
.. figure:: 01_build_clicker_game/previously_learned.svg
|
||||
:align: center
|
||||
:width: 70%
|
||||
|
||||
This is the progress that we have made in discovering the JavaScript web framework at the end of
|
||||
:doc:`02_create_customize_fields`.
|
||||
|
||||
.. admonition:: Goal
|
||||
|
||||
.. image:: 01_build_clicker_game/kitten_mode.png
|
||||
:align: center
|
||||
|
||||
.. spoiler:: Solutions
|
||||
|
||||
The solutions for each exercise of the chapter are hosted on the
|
||||
`official Odoo tutorials repository
|
||||
<https://github.com/odoo/tutorials/commits/{CURRENT_MAJOR_BRANCH}-solutions/awesome_tshirt>`_.
|
||||
<https://github.com/odoo/tutorials/commits/{CURRENT_MAJOR_BRANCH}-solutions/awesome_clicker>`_.
|
||||
|
||||
1. Interacting with the notification system
|
||||
===========================================
|
||||
|
||||
.. note::
|
||||
This task depends on :doc:`the previous exercises <02_create_customize_fields>`.
|
||||
1. Create a systray item
|
||||
========================
|
||||
|
||||
After using the :guilabel:`Print Label` button for some t-shirt tasks, it is apparent that there
|
||||
should be some feedback that the `print_label` action is completed (or failed, for example, the
|
||||
printer is not connected or ran out of paper).
|
||||
To get started, we want to display a counter in the systray.
|
||||
|
||||
.. exercise::
|
||||
#. Display a :ref:`notification <frontend/services/notification>` message when the action is
|
||||
completed successfully, and a warning if it failed.
|
||||
#. If it failed, the notification should be permanent.
|
||||
#. Create a `clicker_systray_item.js` (and `xml`) file with a hello world component
|
||||
#. Register it to the systray registry, and make sure it is visible
|
||||
#. Update the content of the item so that it displays the following string: `Clicks: 0`, and
|
||||
add a button on the right to increment the value
|
||||
|
||||
.. image:: 01_build_clicker_game/notification.png
|
||||
:align: center
|
||||
:scale: 60%
|
||||
And voila, we have a completely working clicker game!
|
||||
|
||||
.. seealso::
|
||||
`Example: Using the notification service
|
||||
<{GITHUB_PATH}/addons/web/static/src/views/fields/image_url/image_url_field.js>`_
|
||||
2. Count external clicks
|
||||
========================
|
||||
|
||||
2. Add a systray item
|
||||
=====================
|
||||
Well, to be honest, it is not much fun yet. So let us add a new feature: we want all clicks in the
|
||||
user interface to count, so the user is incentivized to use Odoo as much as possible! But obviously,
|
||||
the intentional clicks on the main counter should still count more.
|
||||
|
||||
Our beloved leader wants to keep a close eye on new orders. He wants to see the number of new,
|
||||
unprocessed orders at all time. Let's do that with a systray item.
|
||||
#. Use `useExternalListener` to listen on all clicks on `document.body`
|
||||
#. Each of these clicks should increase the counter value by 1.
|
||||
#. Modify the code so that each click on the counter increased the value by 10
|
||||
|
||||
A :ref:`systray <frontend/registries/systray>` item is an element that appears in the system tray,
|
||||
which is a small area located on the right-hand side of the navbar. The systray is used to display
|
||||
notifications and provide access to certain features.
|
||||
Make sure that a click on the counter does not increase the value by 11!
|
||||
|
||||
.. exercise::
|
||||
3. Create a client action
|
||||
=========================
|
||||
|
||||
#. Create a systray component that connects to the statistics service we made previously.
|
||||
#. Use it to display the number of new orders.
|
||||
#. Clicking on it should open a list view with all of those orders.
|
||||
#. Bonus point: avoid making the initial RPC by adding the information to the session info. The
|
||||
session info is given to the web client by the server in the initial response.
|
||||
Currently, the current user interface is quite small: it is just a systray item. We certainly need
|
||||
more room to display more of our game. To do that, let us create a client action. A client action
|
||||
is a main action, managed by the web client, that displays a component.
|
||||
|
||||
.. image:: 01_build_clicker_game/systray.png
|
||||
:align: center
|
||||
#. Create a `clicker_client_action.js` (and `xml`) file, with a hello world component
|
||||
#. Register that client action in the action registry under the name `clicker_action`
|
||||
#. Add a button on the systray item with the text `Open`. Clicking on it should open the
|
||||
client action `clicker_action` (use the action service to do that)
|
||||
|
||||
.. seealso::
|
||||
- `Example: Systray item <{GITHUB_PATH}/addons/web/static/src/webclient/user_menu/user_menu.js>`_
|
||||
- `Example: Adding some information to the "session info"
|
||||
<{GITHUB_PATH}/addons/barcodes/models/ir_http.py>`_
|
||||
- `Example: Reading the session information
|
||||
<https://github.com/odoo/odoo/blob/1f4e583ba20a01f4c44b0a4ada42c4d3bb074273/
|
||||
addons/barcodes/static/src/barcode_service.js#L5>`_
|
||||
|
||||
3. Real life update
|
||||
===================
|
||||
|
||||
So far, the systray item from above does not update unless the user refreshes the browser. Let us
|
||||
do that by calling periodically (for example, every minute) the server to reload the information.
|
||||
|
||||
.. exercise::
|
||||
|
||||
#. The `tshirt` service should periodically reload its data.
|
||||
|
||||
Now, the question arises: how is the systray item notified that it should re-render itself? It can
|
||||
be done in various ways but, for this training, we choose to use the most *declarative* approach:
|
||||
|
||||
.. exercise::
|
||||
|
||||
2. Modify the `tshirt` service to return a `reactive
|
||||
<{OWL_PATH}/doc/reference/reactivity.md#reactive>`_ object. Reloading data should update the
|
||||
reactive object in place.
|
||||
3. The systray item can then perform a `useState
|
||||
<{OWL_PATH}/doc/reference/reactivity.md#usestate>`_ on the service return value.
|
||||
4. This is not really necessary, but you can also *package* the calls to `useService` and
|
||||
`useState` in a custom hook `useStatistics`.
|
||||
|
||||
.. seealso::
|
||||
- `Documentation on reactivity <{OWL_PATH}/doc/reference/reactivity.md>`_
|
||||
- `Example: Use of reactive in a service
|
||||
<https://github.com/odoo/odoo/blob/1f4e583ba20a01f4c44b0a4ada42c4d3bb074273/
|
||||
addons/web/static/src/core/debug/profiling/profiling_service.js#L30>`_
|
||||
|
||||
4. Add a command to the command palette
|
||||
=======================================
|
||||
|
||||
Now, let us see how we can interact with the command palette. The command palette is a feature that
|
||||
allows users to quickly access various commands and functions within the application. It is accessed
|
||||
by pressing `CTRL+K` in the Odoo interface.
|
||||
|
||||
.. exercise::
|
||||
|
||||
Modify the :ref:`image preview field <tutorials/master_odoo_web_framework/image_preview_field>`
|
||||
to add a command to the command palette to open the image in a new browser tab (or window).
|
||||
|
||||
Ensure the command is only active whenever a field preview is visible on the screen.
|
||||
|
||||
.. image:: 01_build_clicker_game/new_command.png
|
||||
:align: center
|
||||
|
||||
.. seealso::
|
||||
`Example: Using the useCommand hook
|
||||
<https://github.com/odoo/odoo/blob/1f4e583ba20a01f4c44b0a4ada42c4d3bb074273/
|
||||
addons/web/static/src/core/debug/debug_menu.js#L15>`_
|
||||
|
||||
5. Monkey patching a component
|
||||
4. Move the state to a service
|
||||
==============================
|
||||
|
||||
Often, we can achieve what we want by using existing extension points that allow for customization,
|
||||
such as registering something in a registry. Sometimes, however, it happens that we want to modify
|
||||
something that has no such mechanism. In that case, we must fall back on a less safe form of
|
||||
customization: monkey patching. Almost everything in Odoo can be monkey patched.
|
||||
For now, our client action is just a hello world component. We want it to display our game state, but
|
||||
that state is currently only available in the systray item. So it means that we need to change the
|
||||
location of our state to make it available for all our components. This is a perfect use case for services.
|
||||
|
||||
Bafien, our beloved leader, heard about employees performing better if they are constantly being
|
||||
watched. Since he cannot be there in person for each of his employees, he tasked you with updating
|
||||
the user interface to add a blinking red eye in the control panel. Clicking on that eye should open
|
||||
a dialog with the following message: "Bafien is watching you. This interaction is recorded and may
|
||||
be used in legal proceedings if necessary. Do you agree to these terms?"
|
||||
#. Create a `clicker_service.js` file with the corresponding service
|
||||
#. This service should export a reactive value (the number of clicks) and a few functions to update it:
|
||||
|
||||
.. exercise::
|
||||
.. code-block:: js
|
||||
|
||||
#. :ref:`Inherit <reference/qweb/template_inheritance>` the `web.Breadcrumbs` template of the
|
||||
`ControlPanel component <{GITHUB_PATH}/addons/web/static/src/search/control_panel>`_ to add an
|
||||
icon next to the breadcrumbs. You might want to use the `fa-eye` or `fa-eyes` icons.
|
||||
#. :doc:`Patch </developer/reference/frontend/patching_code>` the component to display the
|
||||
message on click by using `the dialog service
|
||||
<{GITHUB_PATH}/addons/web/static/src/core/dialog/dialog_service.js>`_. You can use
|
||||
`ConfirmationDialog
|
||||
<{GITHUB_PATH}/addons/web/static/src/core/confirmation_dialog/confirmation_dialog.js>`_.
|
||||
#. Add the CSS class `blink` to the element representing the eye and paste the following code in
|
||||
a new CSS file located in your patch's directory.
|
||||
const state = reactive({ clicks: 0 });
|
||||
...
|
||||
return {
|
||||
state,
|
||||
increment(inc) {
|
||||
state.clicks += inc
|
||||
}
|
||||
};
|
||||
|
||||
#. Access the state in both the systray item and the client action (don't forget to `useState` it). Modify
|
||||
the systray item to remove its own local state and use it. Also, you can remove the `+10 clicks` button.
|
||||
#. Display the state in the client action, and add a `+10` clicks button in it.
|
||||
|
||||
.. code-block:: css
|
||||
5. Humanize the displayed value
|
||||
===============================
|
||||
|
||||
.blink {
|
||||
animation: blink-animation 1s steps(5, start) infinite;
|
||||
-webkit-animation: blink-animation 1s steps(5, start) infinite;
|
||||
}
|
||||
@keyframes blink-animation {
|
||||
to {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes blink-animation {
|
||||
to {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
We will in the future display large numbers, so let us get ready for that. There is a `humanize` function that
|
||||
format numbers in a easier to comprehend way: for example, `1234` could be formatted as `1.2k`
|
||||
|
||||
.. image:: 01_build_clicker_game/bafien_eye.png
|
||||
:align: center
|
||||
:scale: 60%
|
||||
#. Use it to display our counters (both in the systray item and the client action)
|
||||
#. Wrap the value in a span element with a tooltip that display the exact value
|
||||
#. Factorize both of these use in a `ClickValue` component
|
||||
|
||||
.. image:: 01_build_clicker_game/confirmation_dialog.png
|
||||
:align: center
|
||||
:scale: 60%
|
||||
6. Buy ClickBots
|
||||
==================
|
||||
|
||||
.. seealso::
|
||||
- `Code: The patch function
|
||||
<https://github.com/odoo/odoo/blob/1f4e583ba20a01f4c44b0a4ada42c4d3bb074273/
|
||||
addons/web/static/src/core/utils/patch.js#L16>`_
|
||||
- `The Font Awesome website <https://fontawesome.com/>`_
|
||||
- `Example: Using the dialog service
|
||||
<https://github.com/odoo/odoo/blob/1f4e583ba20a01f4c44b0a4ada42c4d3bb074273/
|
||||
addons/board/static/src/board_controller.js#L88>`_
|
||||
Let us make our game even more interesting: once a player get to 1000 clicks for the first time, the game
|
||||
should unlock a new feature: the player can buy robots for 1000 clicks. These robots will generate 10 clicks
|
||||
every 10 seconds.
|
||||
|
||||
6. Fetching orders from a customer
|
||||
==================================
|
||||
#. Add a `unlockLevel` number to our state. This is a number that will be incremented at some milestones, and
|
||||
open new features
|
||||
#. Add a `clickBots` number to our state. It represents the number of robots that have been purchased.
|
||||
#. Modify the client action to display the number of click bots (only if `unlockLevel >= 1`), with a `Buy`
|
||||
button that is enabled if `clicks >= 1000`. The `Buy` button should increment the number of clickbots by 1.
|
||||
|
||||
Let's see how to use some standard components to build a powerful feature combining autocomplete,
|
||||
fetching data, and fuzzy lookup. We will add an input in our dashboard to easily search all orders
|
||||
from a given customer.
|
||||
#. Set a 10s interval in the service that will increment the number of clicks by `10*clickBots`.
|
||||
|
||||
.. exercise::
|
||||
7. Notify when a milestone is reached
|
||||
=====================================
|
||||
|
||||
#. Update :file:`tshirt_service.js` to add a `loadCustomers` method, which returns a promise that
|
||||
returns the list of all customers (and only performs the call once).
|
||||
#. Add the `AutoComplete component <{GITHUB_PATH}/addons/web/static/src/core/autocomplete>`_ to
|
||||
the dashboard, next to the buttons in the control panel.
|
||||
#. Fetch the list of customers with the tshirt service, and display it in the AutoComplete
|
||||
component, filtered by the `fuzzyLookup
|
||||
<{GITHUB_PATH}/addons/web/static/src/core/utils/search.js>`_ method.
|
||||
There is not much feedback that something changed when we reached 1k clicks. Let us use the `effect` service
|
||||
to communicate that information clearly.
|
||||
|
||||
.. image:: 01_build_clicker_game/autocomplete.png
|
||||
:align: center
|
||||
:scale: 60%
|
||||
#. When we reach 1000 clicks, use the `effect` service to display a rainbow man.
|
||||
#. Add some text to explain that the user can now buy clickbots.
|
||||
|
||||
7. Reintroduce Kitten Mode
|
||||
8. Add BigBots
|
||||
==============
|
||||
|
||||
Clearly, we need a way to provide the player with more choices. Let us add a new type of clickbot: `BigBots`,
|
||||
which are just more powerful: they provide with 100 clicks each 10s, but they cost 5000 clicks
|
||||
|
||||
#. increment `unlockLevel` when it gets to 5k (so it should be 2)
|
||||
#. Update the state to keep track of bigbots
|
||||
#. bigbots should be available at `unlockLevel >=2`
|
||||
#. Add the corresponding information to the client action
|
||||
|
||||
9. Add a new type of resource: power
|
||||
====================================
|
||||
|
||||
Now, to add another scaling point, let us add a new type of resource: a power multiplier. This is a number
|
||||
that can be increased at `unlockLevel >= 3`, and multiplies the action of the bots (so, instead of providing
|
||||
one click, clickbots now provide us with `multiplier` clicks).
|
||||
|
||||
#. increment `unlockLevel` when it gets to 100k (so it should be 3)
|
||||
#. update the state to keep track of the power (initial value is 1)
|
||||
#. change bots to use that number as a multiplier
|
||||
#. Update the user interface to display and let the user purchase a new power level (costs: 50k)
|
||||
|
||||
|
||||
10. Define some random rewards
|
||||
==============================
|
||||
|
||||
We want the user to obtain sometimes bonuses, to reward using Odoo.
|
||||
|
||||
#. Define a list of rewards in `click_rewards.js`. A reward is an object with:
|
||||
- a `description` string
|
||||
- a `apply` function that take the game state in argument and can modify it
|
||||
- a `minLevel` number (optional) that describes at which unlock level the bonus is available
|
||||
- a `maxLevel` number (optional) that describes at which unlock level a bonus is no longer available.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
export const rewards = [
|
||||
{
|
||||
description: "Get 1 click bot",
|
||||
apply(state) {
|
||||
state.clickbots += 1;
|
||||
},
|
||||
maxLevel: 3,
|
||||
},
|
||||
{
|
||||
description: "Get 10 click bot",
|
||||
apply(state) {
|
||||
state.clickbots += 10;
|
||||
},
|
||||
minLevel: 3,
|
||||
maxLevel: 4,
|
||||
},
|
||||
{
|
||||
description: "Increase bot power!",
|
||||
apply(state) {
|
||||
state.power += 1;
|
||||
},
|
||||
minLevel: 3,
|
||||
},
|
||||
];
|
||||
|
||||
You can add whatever you want to that list!
|
||||
|
||||
#. Define a function `getReward` that will select a random reward from the list of rewards that matches
|
||||
the current unlock level.
|
||||
|
||||
|
||||
11. Provide a reward when opening a form view
|
||||
=============================================
|
||||
|
||||
#. Patch the form controller. Each time a form controller is created, it should randomly decides (1% chance)
|
||||
if a reward should be given
|
||||
#. If the answer is yes, call a method `giveReward` on the service
|
||||
#. That method should choose a reward, send a sticky notification, with a button `Collect` that will
|
||||
then apply the reward, and finally, it should open the `clicker` client action
|
||||
|
||||
12. Only Open the client action if necessary
|
||||
============================================
|
||||
|
||||
Now, the previous exercise has a small flaw: imagine that the player opens a form view, get a reward notification,
|
||||
then open the client action from the systray item, and finally collect the reward: the game will then open
|
||||
the client action twice (look at the breadcrumbs).
|
||||
|
||||
This is actually quite a tricky situation: we want to open the `clicker` client action only if it is not
|
||||
currently being open. This is easy to solve: the action service provides us with a way to check what the current
|
||||
action controller is: `getCurrentController`.
|
||||
|
||||
#. Use `getCurrentController` from the action service to check if the current action is the game, and only open
|
||||
it if it is not true.
|
||||
|
||||
|
||||
11. Add commands in command palette
|
||||
===================================
|
||||
|
||||
#. Add a command `Open Clicker Game` to the command palette
|
||||
#. Add another command: `Buy 1 click bot`
|
||||
|
||||
|
||||
12. Add yet another resource: trees
|
||||
===================================
|
||||
|
||||
It is now time to introduce a completely new type of resources. Here is one that should not be too controversial: trees.
|
||||
We will now allow the user to plant (collect?) fruit trees. A tree costs 1 million clicks, but it will provide us with
|
||||
fruits (either pears or cherries).
|
||||
|
||||
#. Update the state to keep track of various types of trees: pear/cherries, and their fruits
|
||||
#. Add a function that computes the total number of trees and fruits
|
||||
#. Define a new unlock level at `clicks >= 1 000 000`
|
||||
#. Update the client user interface to display the number of trees and fruits, and also, to buy trees
|
||||
|
||||
13. Use a dropdown menu for the systray item
|
||||
============================================
|
||||
|
||||
Our game starts to become interesting. But for now, the systray only displays the total number of clicks. We
|
||||
want to see more information: the total number of trees and fruits. Also, it would be useful to have a quick
|
||||
access to some commands and some more information. Let us use a dropdown menu!
|
||||
|
||||
#. Replace the systray item by a dropdown menu
|
||||
#. It should display the numbers of clicks, trees, and fruits, each with a nice icon
|
||||
#. Clicking on it should open a dropdown menu that displays more detailed information: each types of trees
|
||||
and fruits
|
||||
#. Also, a few dropdown items with some commands: open the clicker game, buy a clickbot, ...
|
||||
|
||||
14. Use a Notebook component
|
||||
============================
|
||||
|
||||
We now keep track of a lot more information. Let us improve our client interface by organizing the information
|
||||
and features in various tabs, with the `Notebook` component:
|
||||
|
||||
#. Use the `Notebook` component
|
||||
#. All `click` content should be displayed in one tab,
|
||||
#. All `tree/fruits` content should be displayed in another tab
|
||||
|
||||
15. Persist the game state
|
||||
==========================
|
||||
|
||||
Let us add a special mode to Odoo: whenever the URL contains `kitten=1`, we will display a kitten in
|
||||
the background of Odoo, because we like kittens.
|
||||
You certainly noticed a big flaw in our game: it is transient. The game state is lost each time the user closes the
|
||||
browser tab. Let us fix that. We will use the local storage to persist the state.
|
||||
|
||||
.. exercise::
|
||||
#. Use the `localstorage` service
|
||||
#. Serialize the state every 10s (in the same interval code) and store it on the local storage
|
||||
#. When the `clicker` service is started, it should load the state from the local storage (if any), or initialize itself
|
||||
otherwise
|
||||
|
||||
#. Create a `kitten` service, which should check the content of the active URL hash with the
|
||||
help of the :ref:`router service <frontend/services/router>`. If `kitten` is set in the URL,
|
||||
add the class `o-kitten-mode` to the document body.
|
||||
#. Add the following SCSS in :file:`kitten_mode.scss`:
|
||||
16. Introduce state migration system
|
||||
====================================
|
||||
|
||||
.. code-block:: css
|
||||
Once you persist state somewhere, a new problem arises: what happens when you update your code, so the shape of the state
|
||||
changes, and the user opens its browser with a state that was created with an old version? Welcome to the world of
|
||||
migration issues!
|
||||
|
||||
.o-kitten-mode {
|
||||
background-image: url(https://upload.wikimedia.org/wikipedia/commons/5/58/Mellow_kitten_%28Unsplash%29.jpg);
|
||||
background-size: cover;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
It is probably wise to tackle the problem early. What we will do here is add a version number to the state, and introduce
|
||||
a system to automatically update the states if it is not up to date.
|
||||
|
||||
.o-kitten-mode > * {
|
||||
opacity: 0.9;
|
||||
}
|
||||
#. Add a version number to the state
|
||||
#. Define an (empty) list of migrations. A migration is an object with a `fromVersion` number, and a `apply` function
|
||||
#. Whenever the code loads the state from the local storage, it should check the version number. If the state is not
|
||||
uptodate, it should apply all necessary migrations
|
||||
|
||||
#. Add a command to the command palette to toggle the kitten mode. Toggling the kitten mode
|
||||
should toggle the class `o-kitten-mode` and update the current URL accordingly.
|
||||
|
||||
.. image:: 01_build_clicker_game/kitten_mode.png
|
||||
:align: center
|
||||
|
||||
8. Lazy loading our dashboard
|
||||
17. Add another type of trees
|
||||
=============================
|
||||
|
||||
This is not really necessary, but the exercise is interesting. Imagine that our awesome dashboard is
|
||||
a large application with potentially multiple external libraries and lots of code/styles/templates.
|
||||
Also, suppose that the dashboard is used only by some users in some business flows. It would be
|
||||
interesting to lazy load it in order to speed up the loading of the web client in most cases.
|
||||
To test our migration system, let us add a new type of trees: peaches.
|
||||
|
||||
So, let us do that!
|
||||
|
||||
.. exercise::
|
||||
|
||||
#. Modify the manifest to create a new :ref:`bundle <reference/assets_bundle>`
|
||||
`awesome_tshirt.dashboard`.
|
||||
#. Add the awesome dashboard code to this bundle. Create folders and move files if needed.
|
||||
#. Remove the code from the `web.assets_backend` bundle so that it is not loaded twice.
|
||||
|
||||
So far, we only removed the dashboard from the main bundle; we now want to lazy load it. Currently,
|
||||
no client action is registered in the action registry.
|
||||
|
||||
.. exercise::
|
||||
|
||||
4. Create a new file :file:`dashboard_loader.js`.
|
||||
5. Copy the code registering `AwesomeDashboard` to the dashboard loader.
|
||||
6. Register `AwesomeDashboard` as a `LazyComponent
|
||||
<https://github.com/odoo/odoo/blob/1f4e583ba20a01f4c44b0a4ada42c4d3bb074273/
|
||||
addons/web/static/src/core/assets.js#L265-L282>`_.
|
||||
7. Modify the code in the dashboard loader to use the lazy component `AwesomeDashboard`.
|
||||
|
||||
If you open the :guilabel:`Network` tab of your browser's dev tools, you should see that
|
||||
:file:`awesome_tshirt.dashboard.min.js` is now loaded only when the Dashboard is first accessed.
|
||||
|
||||
.. seealso::
|
||||
:ref:`Documentation on assets <reference/assets>`
|
||||
#. Add `peach` trees
|
||||
#. Increment the state version number
|
||||
#. Define a migration
|
||||
Reference in New Issue
Block a user