[FIX] web: search in a m2o block the ui
Before this commit, performing a search in a m2o can block your ui
despite the result already being displayed.
Why?
When you perform your search in a m2o, a set of RPC queries are made
with the different search values (name_search). If the last search value
(the one that will be used by the m2o in its autocomplete) has already
been resolved, but one of the other RPCs has still not been resolved,
then the screen will remain blocked until all the rpc's have been resolved.
Solution:
Since we are only interested in the last rpc, we will cancel all the
others. So with each new rpc, we will cancel the previous one.
How to reproduce:
- Go to a form view with a m2o field
- Edit this field +- slowly (several rpc will be done with different values)
- Receive the result of the search. (The autocomplete is displayed with
the right values)
Before this commit:
If one of the other rpc's is not yet resolved, the ui will block
until all rpc's are resolved.
After this commit:
The ui will not block.
closes odoo/odoo#105269
X-original-commit: 9917d841fa38ccc1e6d67875a665494dc22ef92f
Signed-off-by: Samuel Degueldre <sad@odoo.com>
This commit is contained in:
@@ -99,11 +99,20 @@ export function jsonrpc(env, rpcId, url, params, settings = {}) {
|
||||
request.setRequestHeader("Content-Type", "application/json");
|
||||
request.send(JSON.stringify(data));
|
||||
});
|
||||
promise.abort = function () {
|
||||
/**
|
||||
* @param {Boolean} rejectError Returns an error if true. Allows you to cancel
|
||||
* ignored rpc's in order to unblock the ui and not display an error.
|
||||
*/
|
||||
promise.abort = function (rejectError = true) {
|
||||
if (request.abort) {
|
||||
request.abort();
|
||||
}
|
||||
rejectFn(new ConnectionAbortedError("XmlHttpRequestError abort"));
|
||||
if (!settings.silent) {
|
||||
bus.trigger("RPC:RESPONSE", data.id);
|
||||
}
|
||||
if (rejectError) {
|
||||
rejectFn(new ConnectionAbortedError("XmlHttpRequestError abort"));
|
||||
}
|
||||
};
|
||||
return promise;
|
||||
}
|
||||
|
||||
@@ -164,14 +164,20 @@ export function useListener(eventName, querySelector, handler, options = {}) {
|
||||
// -----------------------------------------------------------------------------
|
||||
// useService
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
function _protectMethod(component, fn) {
|
||||
return async function (...args) {
|
||||
return function (...args) {
|
||||
if (status(component) === "destroyed") {
|
||||
throw new Error("Component is destroyed");
|
||||
return Promise.reject(new Error("Component is destroyed"));
|
||||
}
|
||||
const result = await fn.call(this, ...args);
|
||||
return status(component) === "destroyed" ? new Promise(() => {}) : result;
|
||||
|
||||
const prom = Promise.resolve(fn.call(this, ...args));
|
||||
const protectedProm = prom.then((result) =>
|
||||
status(component) === "destroyed" ? new Promise(() => {}) : result
|
||||
);
|
||||
return Object.assign(protectedProm, {
|
||||
abort: prom.abort,
|
||||
cancel: prom.cancel,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -213,13 +213,17 @@ export class Many2XAutocomplete extends Component {
|
||||
}
|
||||
|
||||
async loadOptionsSource(request) {
|
||||
const records = await this.orm.call(this.props.resModel, "name_search", [], {
|
||||
if (this.lastProm) {
|
||||
this.lastProm.abort(false);
|
||||
}
|
||||
this.lastProm = this.orm.call(this.props.resModel, "name_search", [], {
|
||||
name: request,
|
||||
operator: "ilike",
|
||||
args: this.props.getDomain(),
|
||||
limit: this.props.searchLimit + 1,
|
||||
context: this.props.context,
|
||||
});
|
||||
const records = await this.lastProm;
|
||||
|
||||
const options = records.map((result) => ({
|
||||
value: result[0],
|
||||
|
||||
@@ -247,10 +247,17 @@ QUnit.test("check connection aborted", async (assert) => {
|
||||
const MockXHR = makeMockXHR({}, () => {}, def);
|
||||
patchWithCleanup(browser, { XMLHttpRequest: MockXHR }, { pure: true });
|
||||
const env = await makeTestEnv({ serviceRegistry });
|
||||
env.bus.addEventListener("RPC:REQUEST", () => {
|
||||
assert.step("RPC:REQUEST");
|
||||
});
|
||||
env.bus.addEventListener("RPC:RESPONSE", () => {
|
||||
assert.step("RPC:RESPONSE");
|
||||
});
|
||||
|
||||
const connection = env.services.rpc();
|
||||
connection.abort();
|
||||
assert.rejects(connection, ConnectionAbortedError);
|
||||
assert.verifySteps(["RPC:REQUEST", "RPC:RESPONSE"]);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
|
||||
@@ -69,8 +69,11 @@ export function makeFakeRPCService(mockRPC) {
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
rpcProm.abort = () =>
|
||||
rejectFn(new ConnectionAbortedError("XmlHttpRequestError abort"));
|
||||
rpcProm.abort = (rejectError = true) => {
|
||||
if (rejectError) {
|
||||
rejectFn(new ConnectionAbortedError("XmlHttpRequestError abort"));
|
||||
}
|
||||
};
|
||||
return rpcProm;
|
||||
};
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user