-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[ADD] developer: add page on services (js) #1194
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,187 @@ | ||
| ======== | ||
| Services | ||
| ======== | ||
|
|
||
| Services are long lived pieces of code that provide a feature. They may be | ||
| imported by components (with ``useService``) or by other services. Also, they | ||
| can declare a set of dependencies. In that sense, services are basically a | ||
| DI :dfn:`dependency injection` system. For example, the ``notification`` service | ||
| provides a way to display a notification, or the ``rpc`` service is the proper | ||
| way to perform a request to the Odoo server. | ||
|
|
||
| The following example registers a simple service that displays a notification | ||
| every 5 seconds: | ||
|
|
||
| .. code-block:: javascript | ||
|
|
||
| import { registry } from "./core/registry"; | ||
|
|
||
| const myService = { | ||
| dependencies: ["notification"], | ||
| start(env, { notification }) { | ||
| let counter = 1; | ||
| setInterval(() => { | ||
| notification.add(`Tick Tock ${counter++}`); | ||
| }, 5000); | ||
| } | ||
| }; | ||
|
|
||
| serviceRegistry.add("myService", myService); | ||
|
|
||
|
|
||
| .. note:: | ||
|
|
||
| Most code that is not a component should be *packaged* in a service, in | ||
| particular if it performs some side effect. This is very useful for testing | ||
| purposes: tests can choose which services are active, so there are less chance | ||
| for unwanted side effects interfering with the code being tested. | ||
|
|
||
| Service API | ||
| =========== | ||
|
|
||
| A service needs to implement the following interface: | ||
|
|
||
| dependencies (optional, string[]) | ||
| The list of all dependencies that this service needs | ||
|
|
||
| start(env, deps) | ||
| .. code-block:: text | ||
|
|
||
| @param {Environment} env | ||
| @param {{[key: string]: any}} deps all requested dependencies | ||
| @returns {value of service or Promise<value of service>} | ||
|
|
||
| This is the main definition for the service. It can return either a value or | ||
| a promise. In that case, the service loader simply waits for the promise to | ||
| resolve to a value, which is then the value of the service. | ||
|
|
||
| Some services do not export any value. They may just do their work without a | ||
| need to be directly called by other code. In that case, their value will be | ||
| set to ``null`` in ``env.services``. | ||
|
|
||
| async (optional, true or string[]) | ||
| Some services need to provide an asynchronous API. For example, the `rpc` | ||
| service is an asynchronous function, or the `orm` service provides a set of | ||
| functions to call the Odoo server. | ||
|
|
||
| In that case, it is possible for components that use a service to be | ||
| destroyed before the end of an asynchronous function call. Most of the time, | ||
| the asynchronous function call needs to be ignored. Doing otherwise is | ||
| potentially very risky, because the underlying component is no longer active. | ||
| The `async` flag is a way to do just that: it signals to the service creator | ||
| that all asynchronous calls coming from components should be left pending if | ||
| the component is destroyed. | ||
|
|
||
|
|
||
| At startup, the web client starts all services present in the `services` | ||
| registry. Note that the name used in the registry is the name of the service. | ||
|
|
||
| Using a service | ||
| =============== | ||
|
|
||
| A service that depends on other services and has properly declared its | ||
| ``dependencies`` simply receives a reference to the corresponding services | ||
| in the second argument of the ``start`` method. | ||
|
|
||
| The ``useService`` hook is the proper way to use a service in a component. It | ||
| simply returns a reference to the service value, that can then be used by the | ||
| component later. For example: | ||
|
|
||
| .. code-block:: javascript | ||
|
|
||
| import { useService } from "@web/core/utils/hooks"; | ||
|
|
||
| class MyComponent extends Component { | ||
| setup() { | ||
| const rpc = useService("rpc"); | ||
|
|
||
| onWillStart(async () => { | ||
| this.someValue = await rpc(...); | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| The `rpc` service | ||
| ================= | ||
|
|
||
| The `rpc` service provides a single asynchronous function to send requests to | ||
| the server. It has the following signature: | ||
|
|
||
| rpc(route, params, settings) | ||
| .. code-block:: text | ||
|
|
||
| @param {string} route | ||
| @param {Object} [params] parameters sent to the server | ||
| @param {Object} [settings] optional settings (see below) | ||
|
|
||
| The ``settings`` object can contain: | ||
|
|
||
| - ``xhr``, which should be a ``XMLHTTPRequest`` object. In that case, | ||
| the ``rpc`` method will simply use it instead of creating a new one. This | ||
| is useful when one access to advanced features of the `XMLHTTPRequest` API. | ||
| - ``silent (boolean)`` If set to ``true``, the web client will not provide | ||
| a feedback that there is a pending rpc. | ||
|
|
||
| .. note:: | ||
|
|
||
| Note that the ``rpc`` service is considered a low-level service. It should | ||
| only be used to interact with Odoo controllers. To work with models (which | ||
| is by far the most important usecase), one should use the ``orm`` service | ||
| instead. | ||
|
|
||
| Calling a controller is very simple: the route should be the first argument, and | ||
| optionally, a ``params`` object can be given as a second argument. | ||
|
|
||
| .. code-block:: javascript | ||
|
|
||
| const result = await this.rpc("/my/route", { some: "value" }); | ||
|
|
||
|
|
||
| The ``rpc`` service communicates with the server by using a ``XMLHTTPRequest`` | ||
| object, configured to work with the ``application/json`` content type. So clearly | ||
| the content of the request should be JSON serializable. Each request done by | ||
| this service uses the ``POST`` http method. | ||
|
|
||
| Server errors actually return the response with an http code 200. But the ``rpc`` | ||
| service will treat them as error. | ||
|
|
||
| Error Handling | ||
| -------------- | ||
|
|
||
| An rpc can fail for two main reasons: | ||
|
|
||
| * either the odoo server returns an error (so, we call this a ``server`` error). | ||
| In that case the http request will return with an http code 200 BUT with a | ||
| response object containing an ``error`` key. | ||
|
|
||
| * or there is some other kind of network error | ||
|
|
||
| When a rpc fails, then: | ||
|
|
||
| * the promise representing the rpc is rejected, so the calling code will crash, | ||
| unless it handles the situation | ||
| * | ||
| an event ``RPC_ERROR`` is triggered on the main application bus. The event payload | ||
| contains a description of the cause of the error: | ||
|
|
||
| If it is a server error (the server code threw an exception). In that case | ||
| the event payload will be an object with the following keys: | ||
|
|
||
|
|
||
| * ``type = 'server'`` | ||
| * ``message(string)`` | ||
| * | ||
| ``code(number)`` | ||
|
|
||
| * | ||
| ``name(string)`` (optional, used by the error service to look for an appropriate | ||
| dialog to use when handling the error) | ||
|
|
||
| * ``subType(string)`` (optional, often used to determine the dialog title) | ||
| * ``data(object)`` (optional object that can contain various keys among which | ||
| ``debug`` : the main debug information, with the call stack) | ||
|
|
||
| If it is a network error, then the error description is simply an object | ||
| ``{type: 'network'}``. | ||
| When a network error occurs, a notification is displayed and the server is regularly | ||
| contacted until it responds. The notification is closed as soon as the server responds. | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.