closes #334 closes #336 closes #338 closes #339 closes #341 closes #342 closes #343 closes #346 closes #348
4.3 KiB
🦉 Utils 🦉
Owl export a few useful utility functions, to help with common issues. Those
functions are all available in the owl.utils namespace.
Content
whenReady: executing code when DOM is readyloadJS: loading script filesloadTemplates: loading xml filesescape: sanitizing stringsdebounce: limiting rate of function callsshallowEqual: shallow object comparison
whenReady
The function whenReady returns a Promise resolved when the DOM is ready (if
not ready yet, resolved directly otherwise). If called with a callback as
argument, it executes it as soon as the DOM ready (or directly).
Promise.all([loadTemplates(), owl.utils.whenReady()]).then(function([templates]) {
const qweb = new owl.QWeb(templates);
const app = new App({ qweb });
app.mount(document.body);
});
or alternatively:
owl.utils.whenReady(function() {
const qweb = new owl.QWeb();
const app = new App({ qweb });
app.mount(document.body);
});
loadJS
loadJS takes a url (string) for a javascript resource, and loads it. It returns
a promise, so the caller can properly reacts when it is ready. Also, it is smart:
it maintains a list of urls previously loaded (or currently being loaded), and
prevent doing twice the work.
class MyComponent extends owl.Component {
willStart() {
return owl.utils.loadJS("/static/libs/someLib.js");
}
}
loadTemplates
loadTemplates is a helper function to fetch a template file. It simply
performs a GET request and return the resulting string in a promise. For
example:
async function makeEnv() {
const templates = await owl.utils.loadTemplates("templates.xml");
const qweb = new owl.QWeb(templates);
return { qweb };
}
escape
Sometimes, we need to display dynamic data (for example user-generated data) in
the user interface. If this is done by a QWeb template, it is not an issue:
<div><t t-esc="user.data"/></div>
The QWeb engine will create a div node and add the content of the user.data
string as a text node, so the web browser will not parse it as html. However,
it may be a problem if this is done with some javascript code like this:
class BadComponent extends Component {
// some template with a ref to a div
// some code ...
mounted() {
this.divRef.el.innerHTML = this.state.value;
}
}
In this case, the content of the div will be parsed as html, which may inject
unwanted behaviour. To fix this, the escape function will simply transform a
string into an escaped version of the same string, which will be properly displayed
by the browser, but which will not be parsed as html (for example, "<ok>" is
escaped to the string: "<ok>"). So, the bad example above can be fixed
with the following change:
this.divRef.el.innerHTML = owl.utils.escape(this.state.value);
debounce
The debounce function is useful when we want to limit the number of times some
function/action is perfomed. For example, this may be useful to prevent issue
with people double clicking on a button.
It takes three arguments:
func(function): this is the function that will be rate limitedwait(number): this is the number of milliseconds that we want to use to rate limit the functionfuncimmediate(optional, boolean, default=false): ifimmediateis true, the function will be triggered immediately (leading edge of the interval). If false, the function will be triggered at the end (trailing edge).
It returns a function. For example:
const debounce = owl.utils.debounce
window.addEventListener('mousemove', debounce(doSomething, 100));
As this example shows, it is usualy useful for event handlers which are triggered
very quickly, such as scroll or mousemove events.
shallowEqual
This function checks if two objects have the same values assigned to each keys:
shallowEqual({a:1, b: 2}, {a:1, b:2}); // true
shallowEqual({a:1, b: 2}, {a:1, b:3}); // false
However, for performance reasons, it assumes that the two objects have the same keys. If we are in a situation where this is not guaranteed, the following code will work:
const completeShallowEqual = (a,b) => shallowEqual(a,b) && shallowEqual(b,a);