odoo for all ages

57
OpenERP Web Training Release 1.0 OpenERP SA September 16, 2013

Upload: ronald-simmons

Post on 02-Jun-2018

218 views

Category:

Documents


0 download

TRANSCRIPT

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 1/57

OpenERP Web Training

Release 1.0 

OpenERP SA

September 16, 2013

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 2/57

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 3/57

CONTENTS

1 Introduction   3

1.1 Reminder about OpenERP Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

1.2 About this Guide   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

2 JavaScript Basics   5

2.1 Preamble   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

2.2 Command Line Interpreter   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2.3 Basic Data Types   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2.4 Implicit Type Conversions   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

2.5 Control Structures   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

2.6 Functions   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

2.7 Variables and Scopes  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

2.8 Arrays   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

2.9 Objects   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

3 JavaScript Libraries   13

3.1 A First JavaScript Application   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

3.2 Underscore.js   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

3.3 HTML Manipulations with jQuery   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173.4 HTTP Requests with jQuery  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

4 OpenERP Web Framework   27

4.1 A Simple Module to Test the Web Framework    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

4.2 OpenERP JavaScript Module   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

4.3 Classes   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

4.4 Widgets Basics  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

4.5 The QWeb Template Engine   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

4.6 Widget Events and Properties   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

4.7 Widget Helpers   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

4.8 Translations   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

4.9 Communication with the OpenERP Server   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

4.10 Exercises   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

5 Indices and tables   53

i

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 4/57

ii

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 5/57

OpenERP Web Training, Release 1.0

See also the OpenERP Web Reference Documentation.

Contents:

CONTENTS 1

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 6/57

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 7/57

CHAPTER

ONE

INTRODUCTION

1.1 Reminder about OpenERP Structure

OpenERP contains the following parts:

• The OpenERP server contains the server-side framework and handles requests coming from clients.

• The PostgreSQL database contains our data.

• The modules implement the business logic.

• The client web application communicates with the server and displays a graphical user interface.

This guide is all about web applications development and the OpenERP’s web client.

1.2 About this Guide

This guide is a training material to teach OpenERP integrators how to create web modules for OpenERP. The covered

subjects are the following:

• Javascript basics and good practices

• Basics of jQuery and Underscore (Javascript libraries used in OpenERP’s web client)

• OpenERP’s Javascript and web applications framework 

• Extension points for OpenERP’s web client

This guide assumes the reader followed the technical training about OpenERP modules creation provided by OpenERP

SA or have a good knowledge of Python modules development. This guide also assume the reader is experienced in

3

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 8/57

OpenERP Web Training, Release 1.0

object-oriented programming and has basic knowledge of at least one programming language whose syntax is based

on C (C++, Java, C#,...). It may also refer to the Bazaar version control system. Finally, it is necessary to have a

minimal knowledge of HTML and CSS.

All provided examples assume you are developing under a Linux operating system, most specifically Ubuntu or

Debian. If you are using Windows, we recommend you to get a virtual machine with Ubuntu. The website

http://virtualboxes.org/  provides pre-installed virtual machines images for a lot of Linux distributions for free. They

only require to install Virtual Box which is also free.

4 Chapter 1. Introduction

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 9/57

CHAPTER

TWO

JAVASCRIPT BASICS

2.1 Preamble

2.1.1 What is a Web Application ?

A web application is simply an application that is delivered and used through a web browser, but the term has recently

taken a more specific meaning.

The old way to make a web application, and the way OpenERP worked until version 6.0 is to make the server send

to the user complete HTML documents representing the state of the application’s GUI. This means the server has to

compute and send a new HTML document for each interaction; buttons clicks, searches, history navigation all require

the server to resend a document.

This puts a large load on the server and thus severely impact the number of concurrent users that can be served. It also

creates a large latency in the application that makes the implementation of many features impossible, and limits what

can be achieved in terms of usability.

The solution is to create a complete and standalone application in JavaScript that runs on the user’s web browser.

That type of application comes to have much more in common with traditional desktop applications (example: GTK,

Swing, Qt, Windows Forms,...) than PHP-like web sites. The only difference with desktop applications, besides the

programming language and libraries used, is that the web client is downloaded and run by the user’s browser each timehe visits the OpenERP website.

2.1.2 A Note about JavaScript

JavaScript is the language understood by web browsers and thus a de-facto language for web applications. If you want

to develop for OpenERP’s Web client you’ll need to know JavaScript.

Objectively, JavaScript is a not a good programming language. It was designed by Netscape in 1995 for commercial

purpose by a small team with a short development time. It was not meant to be the most universal programming

language in the History. It has a lot a initial design problems and, due to backward-compatibility necessity, it was not

really improved since its creation.

Additionally JavaScript suffers from its wide popularity. This results in a lot of Google search results about JavaScriptbeing articles written by people that do not have a formal training in computer programming or that can’t even program

at all but still manage to get some effects by copy-pasting code.

Still, despite its problems, the core of the language contains good ideas allowing a lot of creativity for programmers.

(Like prototype-based programming and functional programming.) Most of JavaScript’s shortcomings can be erased

by using the correct patterns and the correct tools. It also has strong advantages on its own. First of all, JavaScript

is very fast. Fast to start, fast to run and fast to deploy. The ability to use HTML and the multimedia API of the

browsers also allows to create very nice looking applications and good productivity compared to desktop application

5

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 10/57

OpenERP Web Training, Release 1.0

programming. The decisive point is probably the fact that JavaScript virtual machines are available on 99.99% of the

desktop computers on the planet.

In the end, if you’re a good programmers with the good libraries, the advantages far outweighs the inconveniences and

makes JavaScript and the browser one of the best environment to develop applications for the general public.

2.2 Command Line Interpreter

To test the basic features of the language, we recommend you to begin by using a command line interpreter. Most

modern web browsers will provide a console to use JavaScript, but it is recommended to use   Google Chrome for

OpenERP’s web module development. All this guide will assume you this particular browser.

Once Chrome is installed, open any web page then go in the configuration menu of Chrome and select   Tools >

 Developer Tools or use Ctrl + Shift + I . This should reveal a new section at the bottom of the window. Now select the

Console panel. You should have a screen looking like this:

You will now be able to test the code snippets given in the next part.

2.3 Basic Data Types

2.3.1 Numbers

>   ((3   +   3)   /   1.5)   *   2;

8

Please note   JavaScript do not have integers. All numbers are floats. This is a major difference with most other

programming languages. This has impacts on some mathematical operations. Example:

>   3   /   2;

1.5

In C, Java or Python the result would be 1, except if one of the members was casted to float or explicitly declared as

float using a different notation (2.0 or  2.).

6 Chapter 2. JavaScript Basics

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 11/57

OpenERP Web Training, Release 1.0

2.3.2 Booleans

>   5   ==   2;

false

>   true   ==   true;

true

>   true   ==   false;

false

As simple as booleans can be.

2.3.3 Strings

>   "Hello World";

"Hello World"

>   ’Hello World’;

"Hello World"

Strings can be declared using single quotes or double quotes. Like most high level programming languages, strings

have methods for many operations.

>   "Hello World".charAt(3);

"l"   // like Python, there is not char type in JavaScript, a char is a string of size 1

>   "Hello World".slice(6,   9);

"Wor"   // slice() is used to obtain a sub-string 

>   "Hello World".length;

11

Strings also use the + operator for concatenation:

>   "Hello "   +   "World";

"Hello World"

2.3.4 Null

>   var   x   =   null;

>   x;

null

Similar to a lot of languages or  None in Python.

2.3.5 Undefined

If you declare a variable but do not assign it it will have a special value. This value is not the same as  null.

>   var   x;>   x;

undefined 

2.4 Implicit Type Conversions

JavaScript provides automatic type conversion for most operators.

2.4. Implicit Type Conversions 7

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 12/57

OpenERP Web Training, Release 1.0

>   "Test"   +   5;

"Test5"

Practically that behavior can be useful in some cases, like implicit conversion to strings, but can also create strange

behaviors, the typical case being comparisons. Here are some examples:

>   "5"   ==   5;

true>   ""   ==   0;

true

>   "0"   ==   false;

true

Like in C, numbers are considered as   false   if they are   0  and   true  otherwise. Strings are considered as   true

except if they are empty.

During any operations involving different types, multiple castings can occur which are quite complicated to predict

for the programmer. That’s why it is considered safer to always use the  ===  and  !==  operators.

>   "5"   ===   5;

false

>   ""   ===   0;false

>   "0"   ===   false;

false

These operators will always return false if the types to compare are different.

2.5 Control Structures

JavaScript provides the same control structures than C. (To test with Chrome’s console, you can use  Shift + Enter  to

enter multiple lines of code.)

>   if   (true   ==   true) {console.log("true is true");

}

true   is   true

>   var   x   =   0;

>   while   (x   <   3) {

console.log(x);

x++;

}

1

2

3

>   for   (var   i   =   5; i   <   8; i++) {

console.log(i);

}5

6

7

JavaScript also provides a specialized for structure to loop on objects. ( for (... in ...).) It should be noted

that, due to bad conception of the language involving variable scopes, functional programming, performances issues

and bad behavior with arrays, almost all experienced programmers will avoid the usage of that structure and rather use

functions provided by non-standard libraries like jQuery or Underscore. We recommend you to use _.each provided

by Underscore most of the time. (More information about that library is given later in this document.)

8 Chapter 2. JavaScript Basics

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 13/57

OpenERP Web Training, Release 1.0

2.6 Functions

Functions can be declared like this:

>   function   talk() {

console.log("Hello World");

}>   talk();

Hello World

In JavaScript, functions are also a complete type by themselves. They can be declared as expressions and stored in

variables.

>   var   talk   =   function() {

console.log("Hello World");

}

>   talk();

Hello World

>   var   talkAgain   =   talk;

>   talkAgain();

Hello World>   function   executeFunc(func) {

func();

}

>   executeFunc(talk);

Hello World

Function arguments are declared like in most languages, except without types. Please note that the JavaScript virtual

machine never checks the number of arguments when a function is called. If there are more arguments the function

will be called anyway, if there are fewer arguments the remaining ones will be  undefined.

>   var   print   =   function(a, b) {

console.log(a);

console.log(b);

}

>   print("hello");

hello

undefined 

>   print("nice",   "to",   "meet",   "you");

nice

to

2.7 Variables and Scopes

A variable is declared by preceding its name by  var. Unlike C++ and Java, a scope is not defined by the existence of 

braces. A scope is defined by a function.

>   function   func1() {

var   x;   // x is inside the scope of func1

function   func2() {   // func2 is inside the scope of func1

var   y;   // y is in the scope of func2

}

while   (true) {

var   z;   // z is not in a new scope, it is the same scope than x 

}

}

2.6. Functions 9

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 14/57

OpenERP Web Training, Release 1.0

In this example,   z  is not a variable which is re-created at each iteration of the   while   loop, it is always the same

variable for each iteration because the variable is defined in the scope of  func1.

Functions can also access the variables defined above them.

>   function   func1() {

var   x   =   "hello";

function   func2() {console.log(x);

}

func2();

x   =   "world";

func2();

}

>   func1();

hello

world

When a variable is declared directly at the root of a source file, not inside any function, it exists in the global scope.

Unlike Python, the global scope is not a scope specific to each source file.  The global scope is shared amongst all

 pieces of JavaScript code that are executed by an instance of the virtual machine, which means on the same web page.

// file source1.js

var   x   =   "value1";

// file source2.js

var   x   =   "value2";

If those two files are loaded by the same web page the variable x can only have one value, "value1" or "value2",

depending on which file was loaded last. This is obviously a problem and it can be solved using the Module Pattern

(see later).

One last note about scopes is that any value which is assigned but not declared will be implicitly considered as being

part of the global scope. This means if you ever forget to use the var keyword the code will not crash but the variable

will be global to the application instead of local to the current function. This is a very common source of errors in

JavaScript.>   function   func1() {

x   =   "hello";

}

>   function   func2() {

console.log(x);

}

>   func2();

ReferenceError:   x is not defined

>   func1();

>   func2();

hello

>   x   =   "world";

>   func2();

world

2.8 Arrays

The syntax of the arrays is pretty similar to Python:

10 Chapter 2. JavaScript Basics

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 15/57

OpenERP Web Training, Release 1.0

>   var   array   =   ["hello",   "world"];

>   for   (var   i   =   0; i   <   array.length; i++) {

console.log(array[i]);

}

hello world

Please note the above syntax to iterate on arrays works but is ineffective, for real use-cases you should use  _.each()

or a similar function provided by a third-party library.

Like strings, the arrays have methods for different operations:

>   var   array   =   [];

>   array.push("banana");   // adds an element at the end 

>   array.push("tomato");

>   array;

["banana",   "tomato"]

>   array.pop();   // removes the last element and returns it

"tomato"

>   array;

["banana"]

2.9 Objects

Object-oriented programming is possible in JavaScript, but it is very different compared to most other programming

language (except if you know Lua).

First of all, objects are dictionaries and dictionaries are objects. There is no difference in JavaScript. The syntax is

quite similar to Python’s dictionaries but has alternative syntactic sugar depending if you prefer use a dictionary-like

or an object-like syntax. Demonstration:

>   var   obj   =   {

"key1":   "hello",   // dictionary-like declaration

key2:   "world",   // object-like declaration

};>   console.log(obj["key1"]);   // dictionary-like lookup

hello

>   console.log(obj.key2);   // object-like lookup

world

obj["key"] and  obj.key have the exact same meaning. The first one will, by convention, be mostly used if you

want to make a lookup in a dictionary and the second one will be mostly used to access an object’s attribute.

Methods can simply be defined by putting a function inside an object:

>   var   person   =   {

name:   "John Smith",

presentYourself:   function() {

return   "Hello, my name is "   +   this.name;

},

};

>  person.presentYourself();

"Hello, my name is John Smith"

>   person.name   =   "John Doe";

>  person.presentYourself();

"Hello, my name is John Doe"

In JavaScript, each time a function is called it has and additional, implicitly declared variable called  this. When a

method is called on an object (using the usual   object.method(arguments...)   syntax), the  this  variable

2.9. Objects 11

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 16/57

OpenERP Web Training, Release 1.0

will be a reference to the current object.

In the above example, we define a unique object containing all the attributes and methods necessary to make it work.

But that’s not how most programming languages will handle object-oriented programming. They have a concept of 

class. A class will contain the properties common to all its instances. There are no classes in JavaScript but it is

possible to reproduce that concept using  prototypes.

>   var   Person   =   function() {   // this function will represent a class "Person"this.name   =   "JohnSmith";

};

>   Person.prototype   =   {

presentYourself:   function() {

return   "Hello, my name is "   +   this.name;

},

};

>   var   person   =   new   Person();

>  person.presentYourself();

"Hello, my name is John Smith"

Since prototype-based oriented object programming is a vast subject we will not cover it more in this guide, though

you can easily find some information about it on Internet.

Due to differences between prototype-based and class-based oriented object programming, and the habits of most

programmers, in OpenERP we chose to not use directly prototype-based oriented object programming. We use a high

level API that allows programmers to easily declare classes in a way that seems natural to people that are used to more

conventional programming languages. That subject will be covered later in this guide.

12 Chapter 2. JavaScript Basics

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 17/57

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 18/57

OpenERP Web Training, Release 1.0

3.1.2 Architecture of the Application

Let’s take a look at the only HTML file of the application which is located in  static/index.html:

<!DOCTYPE html>

<html>

<head>

<link   rel="stylesheet" href="static/css/app.css" type="text/css"></link>

<script   type="application/javascript" src="/static/js/jquery.js"></script>

<script   type="application/javascript" src="/static/js/underscore.js"></script>

<script   type="application/javascript" src="/static/js/app.js"></script>

<script  type="application/javascript">

$(function() {

app.main();});

</script>

</head>

<body>

<div   class="main_block">

<div   class="main_content">

</div>

<div   class="right_side">

<button>Click Me</button>

</div>

</div>

</body>

</html>

This web page contains a few HTML elements which are styled by the  static/css/app.css css file.

This HTML file also import three JavaScript files. Two of those are well-known libraries in the JavaScript community:

 jQuery and Underscore. The last JavaScript file,  /static/js/app.js is the file that will contain the logic of this

application.

The last <script/> block is a little harder to understand:

• The call to the enigmatic $  function, to which we pass an anonymous function. The $  function is part of the

14 Chapter 3. JavaScript Libraries

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 19/57

OpenERP Web Training, Release 1.0

 jQuery library. This function can have multiple uses that will be explained later. For now, just remember that

when you call it with that precise syntax it will execute a piece of JavaScript code once the browser has finished

loading the HTML file. This is the right time if we want to modify the content with JavaScript code.

• The function app.main is part of our JavaScript file. See the next section.

3.1.3 The Module Pattern

As stated earlier in this guide, when you simply declare a variable in JavaScript, outside of any function, it will be a

global variable:

var   my_var   =   "my value";

This means the  my_var  variable will be accessible in all the JavaScript files imported by the HTML file. This is

generally considered a bad programming style so we will try to avoid it.

Instead we will use what is known as the  Module Pattern. It is a pattern used to declare variables and functions that

will be local to a file. Here is what it looks like for the  app.js file:

(function() {

app   =   {};

function   main() {

console.log("launch application");

};

app.main   =   main;

})();

We can decompose the Module Pattern this way:

• All the declarations of the file are wrapped in an anonymous function which is directly called.

• Now we can declare anything we want inside the function. Any  var  or  function declaration will be local to

the module’s function and thus it will be a private member of our module.

• We declare one and only one global variable. That variable is a dictionary and it is declared using the syntax

app = {};. Remember: when you declare a variable in JavaScript without using the keyword  var  it will be

a global variable. The app  dictionary is the  namespace of our JavaScript module.

• To expose anything to the outside of the module, to make it public, we add it to the namespace app.

In this module, we only have one public function:  main. If you take a look in the HTML file you will see how to call

that function:

app.main();

The only thing this function does right now is printing a message in the debugging console.

3.1.4 Debugging Tools

Like in a lot of dynamic languages, in JavaScript it is very useful for development to be able to debug your code. But,

unlike languages like Java or C, most JavaScript editors do not provide a system to mark debug points. This can be

replaced with the debugger keyword. Modify your source to add that keyword before the call to console.log():

3.1. A First JavaScript Application 15

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 20/57

OpenERP Web Training, Release 1.0

(function() {

app   =   {};

function   main() {

debugger;

console.log("launch application");

};

app.main   =   main;

})();

Launch the application and open the developer tools (Ctrl+Shift+I  or select   Tools > Developer Tools

in Chrome’s menu). Now reload the application using  F5.

When the developer tools window is shown, Chrome will automatically stop when it reaches a  debugger keyword

and jump to the Sources tab. Now you can use the debugger’s buttons on the top right to advance step, resume, etc...

Also note you can put debugging marks in the debugger by clicking on the line numbers on the left, but this can be

less ergonomic if you were editing code in your text editor.

Another noteworthy debugging tool is the  console.log() function. It will print messages that can be read in the

Console tab of the developer tools.

3.2 Underscore.js

Underscore.js is a library providing various utility functions for JavaScript. It is heavily used in OpenERP and we

recommend you to learn how to use it. Underscore.js functions are all wrapped in a namespace contained in the  _ 

variable.

One the most used function of Underscore is  _.each(). As explained in the JavaScript Basics part of this guide, it

is considered a bad practice to use the  for  loop of JavaScript.  _.each() is a good replacement for it. Example:

var   array   =   ["hello",   "world"]

 _.each(array,   function(x) {

console.log(x);

});

// outputs:

// hello// world 

The first argument of  _.each() must be an array or a dictionary. The second one must be a function. When called

with an array as first argument, the function will receive one parameter: the current value of the array.

When called with a dictionary, the function will receive two parameter. The first one is the current value and the

second one the current key. Example:

16 Chapter 3. JavaScript Libraries

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 21/57

OpenERP Web Training, Release 1.0

var   dict   =   {"banana":   2,   "potato":   3,   "apple":   5};

 _.each(dict,   function(v, k) {

console.log(k, v);

});

// outputs:

// banana 2

// potato 3

// apple 5

Another example of a useful function in underscore is _.range(). It generates a list of numbers:

console.log(_.range(0,   5));

// outputs:

// [0, 1, 2, 3, 4]

Exercise - Usage of Underscore.js

In the  main()  function of the start application, create a piece a code that will add all numbers from 1 to 100

and print the result in the console. Use  _.each() and  _.range().

Solution:

(function() {

app   =   {};

function   main() {

var   x   =   0;

 _.each(_.range(1,   101),   function(i) {

x   +=   i;

});

console.log("Result", x);

};

app.main   =   main;

})();

3.3 HTML Manipulations with jQuery

 jQuery is a JavaScript library just like Underscore whose main goal is to provide an abstraction layer over the web

browser’s API to help common operations and improve compatibility between different browsers. One of the main

usages of jQuery is to help the developers to manipulate the HTML displayed in the browser.

3.3.1 The DOM

The DOM (Document Object Model) is a conceptual representation of the content of the web page. When a JavaScript

piece of code wants to modify the visual content of a page it will modify the DOM, and the browser will alter the

graphical aspect in consequence.

Use Chrome’s developer tools, you can consult the actual state of the DOM using the Elements tab:

3.3. HTML Manipulations with jQuery 17

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 22/57

OpenERP Web Training, Release 1.0

If we execute a simple JavaScript code in the  Console tab to modify the DOM, we will see the modifications in the

Elements tab as well as in the browser itself. Example:

$("body").text("I changed the DOM")

This piece of code will erase the content of the web page and put the text “I Changed the DOM” instead. You can also

see the modifications in the DOM explorer:

If you want, you can also use the  Elements tab to modify the DOM. Than can sometimes be useful to test multiple

layouts as example.

3.3.2 jQuery Selectors

To select a part of the DOM, we use what is called a jQuery selector. To do so, we call the jQuery method and give it

a string as argument:$("body")

When the jQuery library is loaded in a project, it defines two variables:   jQuery and  $. These two variables are in

reality the exact same thing: the jQuery function. When called with as first argument, it will try to find one or more

elements in the DOM that match the given expression.

A lot of readers could already know XPath, which is an language used to define expression to search elements in XML.

That language could have been chosen by jQuery’s creators to select HTML elements, but they decided to use another

syntax that is more similar to CSS.

18 Chapter 3. JavaScript Libraries

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 23/57

OpenERP Web Training, Release 1.0

To select all elements with a certain tag you can just put the name of the tag in the expression:

$("input")   // all <input> elements

$("div")   // all <div> elements

To select an element with a precise  id  you must put the  #  character in front of the identifier:

$("#content")   // the element with id ’content’

The select all the elements that have a precise CSS class you must use the  .  character:

$(".title")   // the elements with the ’title’ class

Just like in CSS you can also combine these selectors:

$("span.title")   // all <span> elements with the ’title’ class

$("#content table")   // all <table> elements that are children of the element with id ’content

When the $()  is called, it will return a  jQuery object . That jQuery object can be seen as an array with a pointer to all

the elements in the DOM that were matched by given expression, except it has a lot of useful methods.

More documentation about the jQuery selectors can be found in the jQuery documentation:

http://api.jquery.com/category/selectors/   .

3.3.3 jQuery Events

Most HTML elements are able to produce events. jQuery can make it easy to catch these events and execute a piece

of JavaScript code. In the following example we catch the  click event on the button displayed on the page to print a

message in the console:

$("button").click(function() {

console.log("someone clicked on the button");

});

Here we pass a function to the  click() method on the jQuery object. When the event is fired, the function will be

called. Please note that, if there are multiple elements selected by our call of  $(), the click event will be bound to

all these buttons.

There is a wide array of events that can be fired, e.g. the mouse passing over an element, the user pushing a key on the

keyboard, an <input> element being modified, etc... More documentation about events can be found in the jQuery

documentation:  http://api.jquery.com/category/events/ 

3.3.4 DOM Modifications with jQuery

 jQuery provides many functions that can modify DOM elements.

To replace the content of a jQuery tag and put new HTML inside it, you can use the  html() method:

$(".main_content").html(’<div style="color: white">Hello world!</div>’);

To avoid replacing the whole content and simply add some HTML at the end of the element, use the   append()

method:

$(".main_content").append(’<div style="color: red">Hello world again!</div>’);

To do the opposite, put some HTML at the beginning of the element, use  prepend():

3.3. HTML Manipulations with jQuery 19

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 24/57

OpenERP Web Training, Release 1.0

$(".main_content").prepend(’<div style="color: green">Hello world, I am the first one!</div>’

To put some text in an element it is a bad idea to use the  html() method, because that text could contain text that

looks like HTML and the browser could try to interpret it as real HTML. To avoid that the text must be escaped, this

can be done automatically by the  text() method:

$(".main_content").text(’The <div> element will appear as-is in the browser.’);

3.3.5 Exercise

Exercise - Usage of jQuery

Modify the code of the start application so, when the user click on the button, it counts from 1 to 10 and prints

each number on a separate line in the area on the left.

Solution:

(function() {

app   =   {};

function   main() {

$("button").click(function() {

 _.each(_.range(1,   11),   function(i) {

$(".main_content").append("<div>"   +   i   +   "</div>");

});

});

};

app.main   =   main;

})();

3.4 HTTP Requests with jQuery

In database-centric applications like OpenERP, JavaScript code needs to communicate with the server. jQuery provides

a function to do so.

We will also use the JSON format. JSON stands for JavaScript Object Notation. It is a lightweight format used

to encode data to be exchanged between different computers. Due to its simplicity, it is very easy to implement

in any programming language and the vast majority of existing programming languages already have one or more

implementations of a JSON reader/writer. Here is a simple example that demonstrates the totality of JSON data types:

{

"string":   "this is a string","number":   42,

"array":   [null,   true,   false],

"dictionary":   {"name":   "a simple dictionary"}

}

To test the HTTP requests in JavaScript, we will use the  app.py Python application that contains some JSON web

services:

20 Chapter 3. JavaScript Libraries

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 25/57

OpenERP Web Training, Release 1.0

@app.route(’/service_plus’, methods=["POST"])

def   service_plus():

data   =   flask.request.json

a   =   data["a"]

b   =   data["b"]

delay   =   data.get("delay",   0)

time.sleep(delay)

return   flask.jsonify(**{

"addition": a   +   b,

})

@app.route(’/service_mult’, methods=["POST"])

def   service_mult():

data   =   flask.request.json

a   =   data["a"]

b   =   data["b"]

delay   =   data.get("delay",   0)

time.sleep(delay)

return   flask.jsonify(**{

"multiplication": a   *   b,

})

It would be out of the scope of this guide to explain how works the Flask Python library which is used to create these

web services in Python. Just remember that this code declare two URLs that accept and return JSON and perform

simple mathematic operations (addition and multiplication). We will code some JavaScript that contact these web

services.

3.4.1 The $.ajax() method

The method used to perform an HTTP request in jQuery is the  $.ajax()  method. The AJAX acronym meaning

Asynchronous JavaScript and XML, a name which loosed its signification nowadays because developers use it to send

other data than XML. Anyway, that term is still used in jQuery’s documentation.

Here is an example which will call the  /service_plus web service:

$.ajax("/service_plus", {

type:   "POST",

dataType:   "json",

data:   JSON.stringify({

"a":   3,

"b":   5,

}),

contentType:   "application/json",

}).then(function(a) {

console.log("3+5=", a.addition);

});

The first argument of  $.ajax() is the URL to send the request.

The second argument is a dictionary that can contains a lot of parameters. Here are the parameters we use:

•   type is the type of HTTP request. Here we use the POST method.

•   dataType: "json" is used to inform jQuery that the server will return JSON, so it can read that JSON

and return

JavaScript objects instead of a simple string. *  data is the data we send to the server. Here we want to send a JSON

string containing a dictionary with two keys ‘a’ and ‘b’. We have to call the method  JSON.stringify, a standard

3.4. HTTP Requests with jQuery 21

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 26/57

OpenERP Web Training, Release 1.0

method in JavaScript to convert objects to JSON strings. *  contentType is the MIME type of the data we send to

the server. The value we use informs the server that we are sending JSON.

The  $.ajax()  returns what is called a  promise. Promises are objects created by jQuery that we will explain later.

For now, just understand that when we call the  then() method on that promise it will bind a function to be called

when we receive the result from the server. That first argument of that function is the JSON object returned by the

/service_plus web service.

The $.ajax() contains a lot more parameters to send any type of HTTP requests. To know more about them, please

read jQuery’s documentation:  http://api.jquery.com/jQuery.ajax/  .

3.4.2 Promises and Deferreds

As a language (and runtime), JavaScript is fundamentally single-threaded. This means any blocking request or com-

putation will block the whole page (and, in older browsers, the software itself even preventing users from switching to

an other tab): a JavaScript environment can be seen as an event-based runloop where application developers have no

control over the runloop itself.

As a result, performing long-running synchronous network requests or other types of complex and expensive accesses

is frowned upon and asynchronous APIs are used instead.

Asynchronous code rarely comes naturally, especially to developers used to synchronous server-side code (in Python,

Java or C#) where the code will just block until the deed is gone. This is compounded by asynchronous program-

ming not being a first-class concept and being implemented using callbacks-based programming, which is the case in

JavaScript.

This part will provide some tools to deal with asynchronous systems, and warn against common issues and pitfalls.

Deferreds are a form of promises. OpenERP Web currently uses jQuery’s deferreds.

The core idea of deferreds is that potentially asynchronous methods will return a Deferred() object instead of an

arbitrary value or (most commonly) nothing. Deferreds can be seen as a promise to a value or error. This object can

then be used to track the end of the asynchronous operation by adding callbacks onto it, either success callbacks or

error callbacks.

A great advantage of deferreds over simply passing callback functions directly to asynchronous methods is the abilityto compose them.

Deferreds’s most important method is   $.Deferred.then(). It is used to attach new callbacks to the deferred

object. The first parameter attaches a success callback, called when the deferred object is successfully resolved and

provided with the resolved value(s) for the asynchronous operation.

$.ajax(...).then(function(a) {

console.log("Asynchronous operation completed, the value is:", a);

});

The second parameter attaches a failure callback, called when the deferred object is rejected and provided with rejec-

tion values (often some sort of error message).

$.ajax(...).then(function(a) {...

},   function() {

console.error("The asynchronous operation failed");

});

Callbacks attached to deferreds are never “lost”: if a callback is attached to an already resolved or rejected deferred,

the callback will be called (or ignored) immediately.

To demonstrate this we can create our own  $.Deferred instance and call the  resolve() method to resolve it:

22 Chapter 3. JavaScript Libraries

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 27/57

OpenERP Web Training, Release 1.0

var   def   =   $.Deferred();

def.resolve();

def.then(function() {

console.log("operation succeeded");

});

// the message "operation succeeded" will appear, even if the deferred was already resolved w

A deferred is also only ever resolved or rejected once, and is either resolved or rejected: a given deferred can not call

a single success callback twice, or call both a success and a failure callbacks. Example:

var   def   =   $.Deferred();

def.then(function() {

console.log("operation succeeded");

});

def.resolve();

def.resolve();

// the message "operation succeeded" will appear only once in the console, because the second

// will not make the deferred call its binded functions a second time

3.4.3 Composing Deferreds

Deferreds truly shine when code needs to compose asynchronous operations in some way or other, as they can be used

as a basis for such composition.

There are two main forms of compositions over deferred: multiplexing and chaining.

Deferred Multiplexing

The most common reason for multiplexing deferred is simply performing 2+ asynchronous operations and wanting to

wait until all of them are done before moving on (and perform more operations).

The jQuery multiplexing function for promises is  $.when(). This function can take any number of promises and

will return a new promise. This returned promise will be resolved when all multiplexed promises are resolved, andwill be rejected as soon as one of the multiplexed promises is rejected.

var   def1   =   $.ajax("/service_plus", {

type:   "POST",

dataType:   "json",

data:   JSON.stringify({

"a":   3,

"b":   5,

}),

contentType:   "application/json",

});

var   def2   =   $.ajax("/service_plus", {

type:   "POST",dataType:   "json",

data:   JSON.stringify({

"a":   6,

"b":   7,

}),

contentType:   "application/json",

});

$.when(def1, def2).then(function(result1, result2) {

3.4. HTTP Requests with jQuery 23

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 28/57

OpenERP Web Training, Release 1.0

console.log("3+5=", result1[0].addition);

console.log("6+7=", result2[0].addition);

});

The arguments given to the function bound to the promise returned by  $.when() are a little complicated. For each

promise passed to  $.when(), the function will receive a parameter. Each parameter is an array containing all the

parameters that would have been passed to a function bound on the original deferred. So, if we want the first parameterof that function we have to use  result1[0].

Deferred Chaining

A second useful composition is starting an asynchronous operation as the result of an other asynchronous operation,

and wanting the result of the second one.

To do so, the function you bind to the deferred using  then() must return another deferred. Example:

var   def1   =   $.ajax("/service_mult", {

type:   "POST",

dataType:   "json",

data:   JSON.stringify({

"a":   3,

"b":   5,

}),

contentType:   "application/json",

}).then(function(result) {

var   def2 $.ajax("/service_mult", {

type:   "POST",

dataType:   "json",

data:   JSON.stringify({

"a":   result.multiplication,

"b":   7,

}),

contentType:   "application/json",

});

return   def2;

});

def1.then(function(result) {

console.log("3*5*7=", result.multiplication);

});

To understand why this works, it is important to know that  then() returns a new promise. That promise represents

the result of the execution of the given function after the deferred was resolved. If the bound function returns a new

promise the result of the call to then() becomes an equivalent of that new promise. So, in the code above def1 and

def2  are promises which will be resolved at the same time and with the same arguments (except if the first call to

ajax() fails, in that case the second call to  ajax() will never happen and  def2 will be marked as rejected).

Best Practices When Using Asynchronous Code

In real-world situations asynchronous calls can result in fairly complex code: big programs can call hundreds of 

functions. Even if only a small number of those functions perform asynchronous operations they will affect the whole

program. This is why jQuery’s deferreds are very useful: they provide a generic structure for asynchronous code

execution and, thanks to deferreds composition, it becomes possible to simplify any situation to a single deferred.

A simple practice to remember when using deferreds is to always return a deferred in all functions that perform

asynchronous operations. This is due to the fact that any other function calling that function could need to know when

24 Chapter 3. JavaScript Libraries

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 29/57

OpenERP Web Training, Release 1.0

it has finished.

function   func1() {

var   def1   =   $.ajax(...);   // A first call to the server.

var   def2   =   $.ajax(...);   // A second call.

var   def3   =   $.when(def1, def2);   // We multiplex all that.

var   def4   =   def3.then(...);   // Some more complexity: we chain it.

// Now we don’t forget to return a deferred that represents the complete operation.

return   def4;

};

function   func2() {

var   def   =   func1();   // We want to call func1().

// Now if I need to know when func1() has finished all its operations I have a deferred t

var   def2   =   def.then(...);

// And finally we don’t forget to return a deferred because func2() is, by transitivity,

// that performs an asynchronous call. So it should return a deferred too.

return   def2;};

3.4.4 Exercises

Exercise - Usage of Deferreds

Create a function that takes 4 parameters:  a, b, c and  d. It must return a deferred resolved with the result of the

mathematical operation (a + b)   *   ( c + d ). Do not use any of the mathematical operators of JavaScript,

you must use the  /service_plus  and  /service_mult  web services and combine deferreds properly to

return a single deferred with the result of the whole operation.

Solution:

(function() {

app   =   {};

function   main() {

$("button").click(function() {

plusmultplus(1,   2,   3,   4).then(function(result) {

console.log("(1+2)*(3+4)=", result.multiplication);

});

});

};

app.main   =   main;

function   plusmultplus(a, b, c, d) {

var   def1   =   $.ajax("/service_plus", {

type:   "POST",

dataType:   "json",

data:   JSON.stringify({

"a":   a,

"b":   b,

}),

3.4. HTTP Requests with jQuery 25

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 30/57

OpenERP Web Training, Release 1.0

contentType:   "application/json",

});

var   def2   =   $.ajax("/service_plus", {

type:   "POST",

dataType:   "json",

data:   JSON.stringify({

"a":   c,

"b":   d,

}),

contentType:   "application/json",

});

return   $.when(def1, def2).then(function(result1, result2) {

return   $.ajax("/service_mult", {

type:   "POST",

dataType:   "json",

data:   JSON.stringify({

"a":   result1[0].addition,

"b":   result2[0].addition,

}),

contentType:   "application/json",});

});

};

})();

26 Chapter 3. JavaScript Libraries

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 31/57

CHAPTER

FOUR

OPENERP WEB FRAMEWORK

In the first part of this guide, we explained the JavaScript language and some libraries commonly used with it (jQuery

and Underscore.js). Still, we lack some serious features to be able to program effectively for a big program like

OpenERP. We don’t really have tools for object oriented programming, no structure for graphical user interfaces

conception, our helpers to request data from the server are too basic, etc...

That’s why the OpenERP web client contains a web development framework to provide structure and the necessary

features for everyday programming. The current part will explain that framework.

4.1 A Simple Module to Test the Web Framework

It’s not really possible to include the multiple JavaScript files that constitute the OpenERP web framework in a simple

HTML file like we did in the previous chapter. So we will create a simple module in OpenERP that contains some

configuration to have a web component that will give us the possibility to test the web framework.

To download the example module, use this bazaar command:

bzr branch lp:~niv-openerp/+junk/oepetstore -r 1

Now you must add that folder to your the addons path when you launch OpenERP ( --addons-path  parame-

ter when you launch the   openerp-server  executable). Then create a new database and install the new moduleoepetstore.

Now let’s see what files exist in that module:

oepetstore

|-- __init__.py

|-- __openerp__.py

|-- petstore_data.xml

|-- petstore.py

|-- petstore.xml

‘-- static

‘-- src

|-- css

| ‘-- petstore.css|-- js

| ‘-- petstore.js

‘-- xml

‘-- petstore.xml

This new module already contains some customization that should be easy to understand if you already coded an

OpenERP module like a new table, some views, menu items, etc... We’ll come back to these elements later because

they will be useful to develop some example web module. Right now let’s concentrate on the essential: the files

dedicated to web development.

27

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 32/57

OpenERP Web Training, Release 1.0

Please note that all files to be used in the web part of an OpenERP module must always be placed in a static folder

inside the module. This is mandatory due to possible security issues. The fact we created the folders  css,  js  and

xml is just a convention.

oepetstore/static/css/petstore.css is our CSS file. It is empty right now but we will add any CSS we

need later.

oepetstore/static/xml/petstore.xml   is an XML file that will contain our QWeb templates. Right nowit is almost empty too. Those templates will be explained later, in the part dedicated to QWeb templates.

oepetstore/static/js/petstore.js is probably the most interesting part. It contains the JavaScript of our

application. Here is what it looks like right now:

openerp.oepetstore   =   function(instance) {

var   _t   =   instance.web._t,

 _lt   =   instance.web._lt;

var   QWeb   =   instance.web.qweb;

instance.oepetstore   =   {};

instance.oepetstore.HomePage  =  instance.web.Widget.extend({

start:   function() {

console.log("pet store home page loaded");

},

});

instance.web.client_actions.add(’petstore.homepage’,  ’instance.oepetstore.HomePage’);

}

The multiple components of that file will explained progressively. Just know that it doesn’t do much things right now

except display a blank page and print a small message in the console.

Like OpenERP’s XML files containing views or data, these files must be indicated in the  __openerp__.py  file.

Here are the lines we added to explain to the web client it has to load these files:

’js’:   [’static/src/js/*.js’],

’css’:   [’static/src/css/*.css’],’qweb’:   [’static/src/xml/*.xml’],

These configuration parameters use wildcards, so we can add new files without altering   __openerp__.py: they

will be loaded by the web client as long as they have the correct extension and are in the correct folder.

Warning:   In OpenERP, all JavaScript files are, by default, concatenated in a single file. Then we apply an

operation called the  minification on that file. The minification will remove all comments, white spaces and line-

breaks in the file. Finally, it is sent to the user’s browser.

That operation may seem complex, but it’s a common procedure in big application like OpenERP with a lot of 

JavaScript files. It allows to load the application a lot faster.

It has the main drawback to make the application almost impossible to debug, which is very bad to develop. The

solution to avoid this side-effect and still be able to debug is to append a small argument to the URL used to load

OpenERP: ?debug. So the URL will look like this:

http://localhost:8069/?debug

When you use that type of URL, the application will not perform all that concatenation-minification process on the

JavaScript files. The application will take more time to load but you will be able to develop with decent debugging

tools.

28 Chapter 4. OpenERP Web Framework

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 33/57

OpenERP Web Training, Release 1.0

4.2 OpenERP JavaScript Module

In the previous chapter, we explained that JavaScript do not have a correct mechanism to namespace the variables

declared in different JavaScript files and we proposed a simple method called the Module pattern.

In OpenERP’s web framework there is an equivalent of that pattern which is integrated with the rest of the framework.

Please note that  an OpenERP web module is a separate concept from an OpenERP addon . An addon is a folderwith a lot of files, a web module is not much more than a namespace for JavaScript.

The oepetstore/static/js/petstore.js already declare such a module:

openerp.oepetstore   =   function(instance) {

instance.oepetstore   =   {};

instance.oepetstore.xxx   =   ...;

}

In OpenERP’s web framework, you declare a JavaScript module by declaring a function that you put in the global

variable  openerp. The attribute you set in that object must have the exact same name than your OpenERP addon

(this addon is named  oepetstore, if I set  openerp.petstore  instead of   openerp.oepetstore that will

not work).That function will be called when the web client decides to load your addon. It is given a parameter named instance,

which represents the current OpenERP web client instance and contains all the data related to the current session as

well as the variables of all web modules.

The convention is to create a new namespace inside the  instance object which has the same name than you addon.

That’s why we set an empty dictionary in instance.oepetstore. That dictionary is the namespace we will use

to declare all classes and variables used inside our module.

4.3 Classes

JavaScript doesn’t have a class mechanism like most object-oriented programming languages. To be more exact, itprovides language elements to make object-oriented programming but you have to define by yourself how you choose

to do it. OpenERP’s web framework provide tools to simplify this and let programmers code in a similar way they

would program in other languages like Java. That class system is heavily inspired by John Resig’s  Simple JavaScript

Inheritance.

To define a new class, you need to inherit from the  instance.web.Class class. Here is the syntax to do so:

instance.oepetstore.MyClass  =  instance.web.Class.extend({

say_hello:   function() {

console.log("hello");

},

});

As you can see, you have to call the   instance.web.Class.extend()  method and give it a dictionary. That

dictionary will contain the methods and class attributes of our new class. Here we simply put a method named

say_hello(). This class can be instantiated and used like this:

var   my_object   =   new  instance.oepetstore.MyClass();

my_object.say_hello();

// print "hello" in the console

You can access the attributes of a class inside a method using  this:

4.2. OpenERP JavaScript Module 29

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 34/57

OpenERP Web Training, Release 1.0

instance.oepetstore.MyClass  =  instance.web.Class.extend({

say_hello:   function() {

console.log("hello",   this.name);

},

});

var   my_object   =   new  instance.oepetstore.MyClass();

my_object.name   =   "Nicolas";

my_object.say_hello();

// print "hello Nicolas" in the console

Classes can have a constructor, it is just a method named  init(). You can pass parameters to the constructor like in

most language:

instance.oepetstore.MyClass  =  instance.web.Class.extend({

init:   function(name) {

this.name   =   name;

},

say_hello:   function() {

console.log("hello",   this.name);

},

});

var   my_object   =   new  instance.oepetstore.MyClass("Nicolas");

my_object.say_hello();

// print "hello Nicolas" in the console

Classes can be inherited. To do so, use the   extend()   directly on your class just like you extended the

instance.web.Class class:

instance.oepetstore.MySpanishClass  =   instance.oepetstore.MyClass.extend({

say_hello:   function() {

console.log("hola",   this.name);

},

});

var   my_object   =   new   instance.oepetstore.MySpanishClass("Nicolas");

my_object.say_hello();

// print "hola Nicolas" in the console

When overriding a method using inheritance, you can use   this._super()   to call the original method.

this._super() is not a normal method of your class, you can consider it’s magic. Example:

instance.oepetstore.MySpanishClass  =   instance.oepetstore.MyClass.extend({

say_hello:   function() {

this._super();

console.log("translation in Spanish: hola",   this.name);

},

});

var   my_object   =   new   instance.oepetstore.MySpanishClass("Nicolas");

my_object.say_hello();

// print "hello Nicolas \n translation in Spanish: hola Nicolas" in the console

30 Chapter 4. OpenERP Web Framework

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 35/57

OpenERP Web Training, Release 1.0

4.4 Widgets Basics

In previous chapter we discovered jQuery and its DOM manipulation tools. It’s useful, but it’s not sufficient to structure

a real application. Graphical user interface libraries like Qt, GTK or Windows Forms have classes to represent visual

components. In OpenERP, we have the Widget class. A widget is a generic component dedicated to display content

to the user.

4.4.1 The Client Action

The start module you installed already contains a small widget, it’s also what we call a  client action. See the

code:

instance.oepetstore.HomePage  =  instance.web.Widget.extend({

start:   function() {

console.log("pet store home page loaded");

},

});

instance.web.client_actions.add(’petstore.homepage’,  ’instance.oepetstore.HomePage’);

Here we create a simple widget by extending the instance.web.Widget class. This one defines a method named

start() that doesn’t do anything really interesting right now. We’ll come back on that method later.

The last line registers our basic widget as a client action. To understand the usefulness of this, take a look at the

OpenERP action definition located at the beginning of the   oepetstore/petstore.xml  OpenERP XML data

file:

<record   id="action_home_page" model="ir.actions.client">

<field   name="name">Inbox</field>

<field   name="tag">petstore.homepage</field>

</record>

<menuitem   id="home_page_petstore_menu" name="Home Page" parent="petstore_menu"

action="action_home_page"/>

If you are an OpenERP addon programmer, should already know at least one type of action: the action act_window.

That type of action displays an OpenERP view like a form view, a list view or any other type of standard OpenERP

view. Here we declare a different type of OpenERP action: a client action.

Client actions are dedicated to be used with a web module part. That action simply tells OpenERP’s web client “open

the client action identified by the key petstore.homepage”. Let’s come back to the JavaScript code that declared

our client action:

instance.web.client_actions.add(’petstore.homepage’,  ’instance.oepetstore.HomePage’);

Calling the   instance.web.client_actions.add()   method like this simply tells the web

client “If someone asks you to open a client action with key   petstore.homepage, instantiate the

instance.oepetstore.HomePage class and show it to the user”.

Due to this declaration, if you go in the menu Pet Store > Pet Store > Home Page you will see an empty

page, but the JavaScript console will print “pet store home page loaded”. This means the widget was correctly loaded

and we can start to use it to test OpenERP’s widgets.

4.4. Widgets Basics 31

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 36/57

OpenERP Web Training, Release 1.0

4.4.2 Display Content

Widgets have a lot of methods and features, but let’s start with the basics: display some data inside the widget and

how to instantiate a widget and display it.

The  HomePage  widget already has a  start()  method. That method is automatically called after the widget has

been instantiated and it has received the order to display its content. We will use it to display some content to the user.

To do so, we will also use the  $el  attribute that all widgets contain. That attribute is a jQuery object with a reference

to the HTML element that represent the root of our widget. A widget can contain multiple HTML elements, but they

must be contained inside one single element. By default, all widgets have an empty root element which is a  <div>

HTML element.

A <div> element in HTML is usually invisible for the user if it does not have any content. That explains why when

the instance.oepetstore.HomePage widget is displayed you can’t see anything: it simply doesn’t have any

content. To show something, we will use some simple jQuery methods on that object to add some HTML in our root

element:

instance.oepetstore.HomePage  =  instance.web.Widget.extend({

start:   function() {

this.$el.append("<div>Hello dear OpenERP user!</div>");

},});

That message will now appear when you go to the menu  Pet Store→ Pet Store→  Home Page (remember you need to

refresh your web browser, although there is not need to restart OpenERP’s server).

Now you should learn how to instantiate a widget and display its content. To do so, we will create a new widget:

instance.oepetstore.GreetingsWidget  =  instance.web.Widget.extend({

start:   function() {

this.$el.append("<div>We are so happy to see you again in this menu!</div>");

},

});

Now we want to display the  instance.oepetstore.GreetingsWidget inside the home page. To do so we

can use the append() method of  Widget:

instance.oepetstore.HomePage  =  instance.web.Widget.extend({

start:   function() {

this.$el.append("<div>Hello dear OpenERP user!</div>");

var   greeting   =   new   instance.oepetstore.GreetingsWidget(this);

greeting.appendTo(this.$el);

},

});

Here, the   HomePage   instantiate a   GreetingsWidget   (the first argument of the constructor of 

GreetingsWidget  will be explained in the next part). Then it asks the   GreetingsWidget   to insert itself 

inside the DOM, more precisely directly under the HomePage widget.

When the appendTo() method is called, it asks the widget to insert itself and to display its content. It’s during thecall to appentTo() that the  start() method will be called.

To check the consequences of that code, let’s use Chrome’s DOM explorer. But before that we will modify a little bit

our widgets to have some classes on some of our  <div> elements so we can clearly see them in the explorer:

instance.oepetstore.HomePage  =  instance.web.Widget.extend({

start:   function() {

this.$el.addClass("oe_petstore_homepage");

...

},

32 Chapter 4. OpenERP Web Framework

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 37/57

OpenERP Web Training, Release 1.0

});

instance.oepetstore.GreetingsWidget  =  instance.web.Widget.extend({

start:   function() {

this.$el.addClass("oe_petstore_greetings");

...

},

});

The result will be this if you can find the correct DOM part in the DOM explorer:

<div  class="oe_petstore_homepage">

<div>Hello dear OpenERP user!</div>

<div  class="oe_petstore_greetings">

<div>We are so happy to see you again in this menu!</div>

</div>

</div>

Here we can clearly see the two  <div> created implicitly by the  Widget class, because we added some classes on

them. We can also see the two divs containing messages we created using the jQuery methods on  $el. Finally, note

the <div class="oe_petstore_greetings"> element which represents the GreetingsWidget instance

is inside the <div class="oe_petstore_homepage">

 which represents the HomePage

 instance.

4.4.3 Widget Parents and Children

In the previous part, we instantiated a widget using this syntax:

new   instance.oepetstore.GreetingsWidget(this);

The first argument is  this, which in that case was a  HomePage  instance. This serves to indicate the Widget what

other widget is his parent.

As we’ve seen, widgets are usually inserted in the DOM by another widget and   inside  that other widget. This means

most widgets are always a part of another widget. We call the container the  parent , and the contained widget the child .

Due to multiple technical and conceptual reasons, it is necessary for a widget to know who is his parent and who areits children. This is why we have that first parameter in the constructor of all widgets.

The getParent() method can be used to get the parent of a widget:

instance.oepetstore.GreetingsWidget  =  instance.web.Widget.extend({

start:   function() {

console.log(this.getParent().$el );

// will print "div.oe_petstore_homepage" in the console

},

});

The getChildren() method can be used to get a list of its children:

instance.oepetstore.HomePage  =  instance.web.Widget.extend({

start:   function() {var   greeting   =   new   instance.oepetstore.GreetingsWidget(this);

greeting.appendTo(this.$el);

console.log(this.getChildren()[0].$el);

// will print "div.oe_petstore_greetings" in the console

},

});

You should also remember that, when you override the  init() method of a widget you should always put the parent

as first parameter are pass it to  this._super():

4.4. Widgets Basics 33

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 38/57

OpenERP Web Training, Release 1.0

instance.oepetstore.GreetingsWidget  =  instance.web.Widget.extend({

init:   function(parent, name) {

this._super(parent);

this.name   =   name;

},

});

Finally, if a widget does not logically have a parent (ie: because it’s the first widget you instantiate in an application),

you can give null as a parent instead:

new instance.oepetstore.GreetingsWidget(null);

4.4.4 Destroying Widgets

If you can display content to your users, you should also be able to erase it. This can simply be done using the

destroy() method:

greeting.destroy();

When a widget is destroyed it will first call  destroy() on all its children. Then it erases itself from the DOM. The

recursive call to destroy from parents to children is very useful to clean properly complex structures of widgets and

avoid memory leaks that can easily appear in big JavaScript applications.

4.5 The QWeb Template Engine

The previous part of the guide showed how to define widgets that are able to display HTML to the user. The example

GreetingsWidget used a syntax like this:

this.$el.append("<div>Hello dear OpenERP user!</div>");

This technically allow use to display any HTML, even it is very complex and require to be generated by code. Although

generating text using pure JavaScript is not very nice, that would necessitate to copy-paste a lot of HTML lines insideour JavaScript source file, add the  "  character at the beginning and the end of each line, etc...

The problem is exactly the same in most programming languages needing to generate HTML. That’s why they typically

use template engines. Example of template engines are Velocity, JSP (Java), Mako, Jinja (Python), Smarty (PHP), etc...

In OpenERP we use a template engine developed specifically for OpenERP’s web client. Its name is QWeb.

QWeb is an XML-based templating language, similar to  Genshi, Thymeleaf  or Facelets with a few peculiarities:

• It’s implemented fully in JavaScript and rendered in the browser.

• Each template file (XML files) contains multiple templates, where template engine usually have a 1:1 mapping

between template files and templates.

• It has special support in OpenERP Web’s instance.web.Widget, though it can be used outside of Open-

ERP’s web client (and it’s possible to use  instance.web.Widget without relying on QWeb).

The rationale behind using QWeb instead of existing javascript template engines is that its extension mechanism is

very similar to the OpenERP view inheritance mechanism. Like OpenERP views a QWeb template is an XML tree

and therefore XPath or DOM manipulations are easy to performs on it.

34 Chapter 4. OpenERP Web Framework

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 39/57

OpenERP Web Training, Release 1.0

4.5.1 Using QWeb inside a Widget

First let’s define a simple QWeb template in   oepetstore/static/src/xml/petstore.xml  file, the exact

meaning will be explained later:

<?xml version="1.0" encoding="UTF-8"?>

<templates   xml:space="preserve">

<t  t-name="HomePageTemplate">

<div   style="background-color: red;">This is some simple HTML</div>

</t>

</templates>

Now let’s modify the HomePage class. Remember that enigmatic line at the beginning the the JavaScript source file?

var   QWeb   =   instance.web.qweb;

This is a line we recommend to copy-paste in all OpenERP web modules. It is the object giving access to all templates

defined in template files that were loaded by the web client. We can use the template we defined in our XML template

file like this:

instance.oepetstore.HomePage  =  instance.web.Widget.extend({start:   function() {

this.$el.append(QWeb.render("HomePageTemplate"));

},

});

Calling the QWeb.render() method asks to render the template identified by the string passed as first parameter.

Another possibility commonly seen in OpenERP code is to use  Widget‘s integration with QWeb:

instance.oepetstore.HomePage  =  instance.web.Widget.extend({

template:   "HomePageTemplate",

start:   function() {

...

},

});

When you put a  template class attribute in a widget, the widget knows it has to call  QWeb.render() to render

that template.

Please note there is a difference between those two syntaxes. When you use   Widget‘s QWeb integration the

QWeb.render()  method is called   before   the widget calls   start(). It will also take the root element of the

rendered template and put it as a replacement of the default root element generated by the  Widget class. This will

alter the behavior, so you should remember it.

QWeb Context

Like with all template engines, QWeb templates can contain code able to manipulate data that is given to the template.

To pass data to QWeb, use the second argument to  QWeb.render():

<t  t-name="HomePageTemplate">

<div>Hello   <t   t-esc="name"/></div>

</t>

QWeb.render("HomePageTemplate", {name:   "Nicolas"});

Result:

4.5. The QWeb Template Engine 35

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 40/57

OpenERP Web Training, Release 1.0

<div>Hello Nicolas</div>

When you use Widget‘s integration you can not pass additional data to the template. Instead the template will have

a unique widget variable which is a reference to the current widget:

<t  t-name="HomePageTemplate">

<div>Hello   <t   t-esc="widget.name"/></div>

</t>

instance.oepetstore.HomePage  =  instance.web.Widget.extend({

template:   "HomePageTemplate",

init:   function(parent) {

this._super(parent);

this.name   =   "Nicolas";

},

start:   function() {

},

});

Result:

::  <div>Hello Nicolas</div>

Template Declaration

Now that we know everything about rendering templates we can try to understand QWeb’s syntax.

All QWeb directives use XML attributes beginning with the prefix   t-. To declare new templates, we add a   <t

t-name="..."> element into the XML template file inside the root element <template>:

<templates>

<t t-name="HomePageTemplate">

<div>This is some simple HTML</div>

</t>

</templates>

t-name simply declares a template that can be called using  QWeb.render().

Escaping

To put some text in the HTML, use t-esc:

<t t-name="HomePageTemplate">

<div>Hello <t t-esc="name"/></div>

</t>

This will output the variable  name and escape its content in case it contains some characters that looks like HTML.

Please note the attribute t-esc can contain any type of JavaScript expression:<t t-name="HomePageTemplate">

<div><t t-esc="3+5"/></div>

</t>

Will render:

<div>8</div>

36 Chapter 4. OpenERP Web Framework

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 41/57

OpenERP Web Training, Release 1.0

Outputting HTML

If you know you have some HTML contained in a variable, use  t-raw instead of  t-esc:

<t t-name="HomePageTemplate">

<div><t t-raw="some_html"/></div>

</t>

If

The basic alternative block of QWeb is  t-if:

<t t-name="HomePageTemplate">

<div>

<t t-if="true == true">

true is true

</t>

<t t-if="true == false">

true is not true

</t>

</div>

</t>

Although QWeb does not contains any structure for else.

Foreach

To iterate on a list, use  t-foreach and  t-as:

<t t-name="HomePageTemplate">

<div>

<t t-foreach="names" t-as="name">

<div>

Hello <t t-esc="name"/></div>

</t>

</div>

</t>

Setting the Value of an XML Attribute

QWeb has a special syntax to set the value of an attribute. You must use t-att-xxx and replace xxx with the name

of the attribute:

<t t-name="HomePageTemplate">

<div>

Input your name:<input type="text" t-att-value="defaultName"/>

</div>

</t>

To Learn More About QWeb

This guide does not pretend to be a reference for QWeb, please see the documentation for more information:

https://doc.openerp.com/trunk/web/qweb/  .

4.5. The QWeb Template Engine 37

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 42/57

OpenERP Web Training, Release 1.0

Exercise

Exercise - Usage of QWeb in Widgets

Create a widget whose constructor contains two parameters aside from   parent:   product_names   and

color.   product_names   is a list of strings, each one being a name of product.   color   is a string con-

taining a color in CSS color format (ie:   #000000  for black). That widget should display the given product

names one under the other, each one in a separate box with a background color with the value of  color and a

border. You must use QWeb to render the HTML. This exercise will necessitate some CSS that you should put

in oepetstore/static/src/css/petstore.css. Display that widget in the HomePage widget with

a list of five products and green as the background color for boxes.

Solution:

openerp.oepetstore   =   function(instance) {

var   _t   =   instance.web._t,

 _lt   =   instance.web._lt;

var   QWeb   =   instance.web.qweb;

instance.oepetstore   =   {};

instance.oepetstore.HomePage  =  instance.web.Widget.extend({

start:   function() {

var   products   =   new   instance.oepetstore.ProductsWidget(this, ["cpu",   "mouse",   "keyb

products.appendTo(this.$el);

},

});

instance.oepetstore.ProductsWidget  =  instance.web.Widget.extend({

template:   "ProductsWidget",

init:   function(parent, products, color) {

this._super(parent);

this.products   =   products;

this.color   =   color;

},

});

instance.web.client_actions.add(’petstore.homepage’,  ’instance.oepetstore.HomePage’);

}

<?xml version="1.0" encoding="UTF-8"?>

<templates   xml:space="preserve">

<t   t-name="ProductsWidget">

<div>

<t   t-foreach="widget.products" t-as="product">

<span   class="oe_products_item" t-att-style="’background-color: ’ + widget.col

</t>

</div>

</t>

</templates>

.oe_products_item   {

display:   inline- block;

 padding:   3px;

 margin:   5px;

 border:   1px   solid   black;

38 Chapter 4. OpenERP Web Framework

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 43/57

OpenERP Web Training, Release 1.0

 border-radius:   3px;

}

4.6 Widget Events and Properties

Widgets still have more helper to learn. One of the more complex (and useful) one is the event system. Events are also

closely related to the widget properties.

4.6.1 Events

Widgets are able to fire events in a similar way most components in existing graphical user interfaces libraries (Qt,

GTK, Swing,...) handle them. Example:

instance.oepetstore.ConfirmWidget  =  instance.web.Widget.extend({

start:   function() {

var   self   =   this;

this.$el.append("<div>Are you sure you want to perform this action?</div>"   +

"<button class=’ok_button’>Ok</button>"   +

"<button class=’cancel_button’>Cancel</button>");

this.$el.find("button.ok_button").click(function() {

self.trigger("user_choose",   true);

});

this.$el.find("button.cancel_button").click(function() {

self.trigger("user_choose",   false);

});

},

});

instance.oepetstore.HomePage  =  instance.web.Widget.extend({

start:   function() {

var   widget   =   new  instance.oepetstore.ConfirmWidget(this);

widget.on("user_choose",   this,   this.user_choose);

widget.appendTo(this.$el);

},

user_choose:   function(confirm) {

if   (confirm) {

4.6. Widget Events and Properties 39

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 44/57

OpenERP Web Training, Release 1.0

console.log("The user agreed to continue");

}   else   {

console.log("The user refused to continue");

}

},

});

First, we will explain what this example is supposed to do. We create a generic widget to ask the user if he really

wants to do an action that could have important consequences (a type widget heavily used in Windows). To do so, we

put two buttons in the widget. Then we bind jQuery events to know when the user click these buttons.

Note:   It could be hard to understand this particular line:

var   self   =   this;

Remember, in JavaScript the variable this is a variable that is passed implicitly to all functions. It allows us to know

which is the object if function is used like a method. Each declared function has its own  this. So, when we declare

a function inside a function, that new function will have its own  this that could be different from the  this of the

parent function. If we want to remember the original object the simplest method is to store a reference in a variable.

By convention in OpenERP we very often name that variable  self because it’s the equivalent of  this in Python.

Since our widget is supposed to be generic, it should not perform any precise action by itself. So, we simply make it

trigger and event named  user_choose by using the  trigger() method.

Widget.trigger(event_name [, ...])   takes as first argument the name of the event to trigger. Then it

can takes any number of additional arguments. These arguments will be passed to all the event listeners.

Then we modify the HomePage widget to instantiate a  ConfirmWidget and listen to its user_choose event by

calling the on() method.

Widget.on(event_name, object, func) allows to bind a function to be called when the event identified

by event_name is  triggered. The  func argument is the function to call and  object is the object to which that

function is related if it is a method. The binded function will be called with the additional arguments of  trigger()

if it has any. Example:

start:   function() {

var   widget   =   ...

widget.on("my_event",   this,   this.my_event_triggered);

widget.trigger("my_event",   1,   2,   3);

},

my_event_triggered:   function(a, b, c) {

console.log(a, b, c);

// will print "1 2 3"

}

4.6.2 Properties

Properties are very similar to normal object attributes. They allow to set data on an object but with an additional

feature: it triggers events when a property’s value has changed.

start:   function() {

this.widget   =   ...

this.widget.on("change:name",   this,   this.name_changed);

this.widget.set("name",   "Nicolas");

},

name_changed:   function() {

40 Chapter 4. OpenERP Web Framework

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 45/57

OpenERP Web Training, Release 1.0

console.log("The new value of the property ’name’ is",   this.widget.get("name"));

}

Widget.set(name, value) allows to set the value of property. If the value changed (or it didn’t had a value

previously) the object will trigger a  change:xxx where xxx  is the name of the property.

Widget.get(name) allows to retrieve the value of a property.

4.6.3 Exercise

Exercise - Widget Properties and Events

Create a widget   ColorInputWidget   that will display 3   <input type="text">. Each of these

<input>  is dedicated to type a hexadecimal number from 00 to FF. When any of these  <input>   is modi-

fied by the user the widget must query the content of the three  <input>, concatenate their values to have a

complete CSS color code (ie:  #00FF00) and put the result in a property named color. Please note the jQuery

change() event that you can bind on any HTML  <input> element and the  val() method that can query

the current value of that  <input> could be useful to you for this exercise.

Then, modify the   HomePage   widget to instantiate   ColorInputWidget   and display it. The  HomePagewidget should also display an empty rectangle. That rectangle must always, at any moment, have the same

background color than the color in the  color property of the ColorInputWidget instance.

Use QWeb to generate all HTML.

Solution:

openerp.oepetstore   =   function(instance) {

var   _t   =   instance.web._t,

 _lt   =   instance.web._lt;

var   QWeb   =   instance.web.qweb;

instance.oepetstore   =   {};

instance.oepetstore.ColorInputWidget  =  instance.web.Widget.extend({

template:   "ColorInputWidget",

start:   function() {

var   self   =   this;

this.$el.find("input").change(function() {

self.input_changed();

});

self.input_changed();

},

input_changed:   function(confirm) {

var   color   =   "#";

color   +=   this.$el.find(".oe_color_red").val();

color   +=   this.$el.find(".oe_color_green").val();

color   +=   this.$el.find(".oe_color_blue").val();this.set("color", color);

},

});

instance.oepetstore.HomePage  =  instance.web.Widget.extend({

template:   "HomePage",

start:   function() {

this.colorInput   =   new   instance.oepetstore.ColorInputWidget(this);

this.colorInput.on("change:color",   this,   this.color_changed);

4.6. Widget Events and Properties 41

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 46/57

OpenERP Web Training, Release 1.0

this.colorInput.appendTo(this.$el);

},

color_changed:   function() {

this.$el.find(".oe_color_div").css("background-color",   this.colorInput.get("color

},

});

instance.web.client_actions.add(’petstore.homepage’,  ’instance.oepetstore.HomePage’);

}

<?xml version="1.0" encoding="UTF-8"?>

<templates xml:space="preserve">

<t t-name="ColorInputWidget">

<div>

Red: <input type="text" class="oe_color_red" value="00"></input><br />

Green: <input type="text" class="oe_color_green" value="00"></input><br />

Blue: <input type="text" class="oe_color_blue" value="00"></input><br />

</div>

</t>

<t t-name="HomePage"><div>

<div class="oe_color_div"></div>

</div>

</t>

</templates>

.oe_color_div {

width:   100px;

height:   100px;

margin:   10px;

}

Note:   jQuery’s css() method allows to set a css property.

4.7 Widget Helpers

We’ve seen the basics of the Widget class, QWeb and the events/properties system. There are still some more useful

methods proposed by this class.

4.7.1   Widget‘s jQuery Selector

It is very common to need to select a precise element inside a widget. In the previous part of this guide we’ve seen a

lot of uses of the find() method of jQuery objects:

this.$el.find("input.my_input")...

Widget provides a shorter syntax that does the same thing with the  $()  method:

instance.oepetstore.MyWidget  =  instance.web.Widget.extend({

start:   function() {

this.$("input.my_input")...

},

});

42 Chapter 4. OpenERP Web Framework

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 47/57

OpenERP Web Training, Release 1.0

Note:   We strongly advise you against using directly the global jQuery function   $()   like we did in the previouschapter were we explained the jQuery library and jQuery selectors. That type of global selection is sufficient for

simple applications but is not a good idea in real, big web applications. The reason is simple: when you create a new

type of widget you never know how many times it will be instantiated. Since the  $()  global function operates in  the

whole HTML displayed in the browser , if you instantiate a widget 2 times and use that function you will incorrectly

select the content of another instance of your widget. That’s why you must restrict the jQuery selections to HTMLwhich is located inside your widget most of the time.

Applying the same logic, you can also guess it is a very bad idea to try to use HTML ids in any widget. If the widget

is instantiated 2 times you will have 2 different HTML element in the whole application that have the same id. And

that is an error by itself. So you should stick to CSS classes to mark your HTML elements in all cases.

4.7.2 Easier DOM Events Binding

In the previous part, we had to bind a lot of HTML element events like  click() or  change(). Now that we have

the $()  method to simplify code a little, let’s see how it would look like:

instance.oepetstore.MyWidget  =  instance.web.Widget.extend({

start:   function() {

var   self   =   this;

this.$(".my_button").click(function() {

self.button_clicked();

});

},

button_clicked:   function() {

..

},

});

It’s still a bit long to type. That’s why there is an even more simple syntax for that:

instance.oepetstore.MyWidget  =  instance.web.Widget.extend({

events:   {

"click .my_button":   "button_clicked",

},

button_clicked:   function() {

..

}

});

Warning:   It’s important to differentiate the jQuery events that are triggered on DOM elements and events of the

widgets. The event class attribute is a helper to help binding jQuery events, it has nothing to do with the widget

events that can be binded using the on() method.

The event class attribute is a dictionary that allows to define jQuery events with a shorter syntax.

The key is a string with 2 different parts separated with a space. The first part is the name of the event, the second one

is the jQuery selector. So the key  click .my_button will bind the event  click on the elements matching the

selector my_button.

The value is a string with the name of the method to call on the current object.

4.7. Widget Helpers 43

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 48/57

OpenERP Web Training, Release 1.0

4.7.3 Development Guidelines

As explained in the prerequisites to read this guide, you should already know HTML and CSS. But developing web

applications in JavaScript or developing web modules for OpenERP require to be more strict than you will usually be

when simply creating static web pages with CSS to style them. So these guidelines should be followed if you want to

have manageable projects and avoid bugs or common mistakes:

• Identifiers (id attribute) should be avoided. In generic applications and modules,  id  limits the re-usability of 

components and tends to make code more brittle. Just about all the time, they can be replaced with nothing,

with classes or with keeping a reference to a DOM node or a jQuery element around.

Note:   If it is absolutely necessary to have an  id  (because a third-party library requires one and can’t take aDOM element), it should be generated with _.uniqueId().

• Avoid predictable/common CSS class names. Class names such as “content” or “navigation” might match the

desired meaning/semantics, but it is likely an other developer will have the same need, creating a naming conflict

and unintended behavior. Generic class names should be prefixed with e.g. the name of the component they

belong to (creating “informal” namespaces, much as in C or Objective-C).

• Global selectors should be avoided. Because a component may be used several times in a single page (an

example in OpenERP is dashboards), queries should be restricted to a given component’s scope. Unfilteredselections such as  $(selector)  or   document.querySelectorAll(selector) will generally lead

to unintended or incorrect behavior. OpenERP Web’s   Widget   has an attribute providing its DOM root

(Widget.$el), and a shortcut to select nodes directly (Widget.$()).

• More generally, never assume your components own or controls anything beyond its own personal $el.

• HTML templating/rendering should use QWeb unless absolutely trivial.

• All interactive components (components displaying information to the screen or intercepting DOM events) must

inherit from Widget and correctly implement and use its API and life cycle.

4.8 Translations

The process to translate text in Python and JavaScript code is very similar. You could have noticed these lines at the

beginning of the petstore.js file:

var   _t   =   instance.web._t,

 _lt   =   instance.web._lt;

These lines are simply used to import the translation functions in the current JavaScript module. The correct to use

them is this one:

this.$el.text(_t("Hello dear user!"));

In OpenERP, translations files are automatically generated by scanning the source code. All piece of code that calls a

certain function are detected and their content is added to a translation file that will then be sent to the translators. In

Python, the function is _(). In JavaScript the function is  _t() (and also  _lt()).

If the source file as never been scanned and the translation files does not contain any translation for the text given to

 _t() it will return the text as-is. If there is a translation it will return it.

 _lt()  does almost the exact same thing but is a little bit more complicated. It does not return a text but returns a

function that will return the text. It is reserved for very special cases.

var   text_func   =   _lt("Hello dear user!");

this.$el.text(text_func());

44 Chapter 4. OpenERP Web Framework

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 49/57

OpenERP Web Training, Release 1.0

To have more information about OpenERP’s translations, please take a look at the reference documentation:

https://doc.openerp.com/contribute/translations/  .

4.9 Communication with the OpenERP Server

Now you should know everything you need to display any type of graphical user interface with your OpenERP mod-

ules. Still, OpenERP is a database-centric application so it’s still not very useful if you can’t query data from the

database.

As a reminder, in OpenERP you are not supposed to directly query data from the PostgreSQL database, you will

always use the build-in ORM (Object-Relational Mapping) and more precisely the OpenERP  models.

4.9.1 Contacting Models

In the previous chapter we explained how to send HTTP requests to the web server using the  $.ajax() method and

the JSON format. It is useful to know how to make a JavaScript application communicate with its web server using

these tools, but it’s still a little bit low-level to be used in a complex application like OpenERP.

When the web client contacts the OpenERP server it has to pass additional data like the necessary information to

authenticate the current user. There is also some more complexity due to OpenERP models that need a higher-level

communication protocol to be used.

This is why you will not use directly $.ajax() to communicate with the server. The web client framework provides

classes to abstract that protocol.

To demonstrate this, the file  petstore.py already contains a small model with a sample method:

class  message_of_the_day(osv.osv):

 _name   =   "message_of_the_day"

def   my_method(self, cr, uid, context=None):

return   {"hello":   "world"}

 _columns   =   {

’message’: fields.text(string="Message"),

’color’: fields.char(string="Color", size=20),

}

If you know OpenERP models that code should be familiar to you. This model declares a table named

message_of_the_day  with two fields. It also has a method  my_method()  that doesn’t do much except re-

turn a dictionary.

Here is a sample widget that calls  my_method() and displays the result:

instance.oepetstore.HomePage  =  instance.web.Widget.extend({

start:   function() {

var   self   =   this;

var   model   =   new   instance.web.Model("message_of_the_day");

model.call("my_method", [], {context:   new   instance.web.CompoundContext()}).then(funct

self.$el.append("<div>Hello "   +   result["hello"]   +   "</div>");

// will show "Hello world" to the user 

});

},

});

4.9. Communication with the OpenERP Server 45

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 50/57

OpenERP Web Training, Release 1.0

The class used to contact OpenERP models is   instance.web.Model. When you instantiate it, you must

give as first argument to its constructor the name of the model you want to contact in OpenERP. (Here it is

message_of_the_day, the model created for this example, but it could be any other model like res.partner.)

call(name, args, kwargs)   is the method of   Model  used to call any method of an OpenERP server-side

model. Here are its arguments:

•   name is the name of the method to call on the model. Here it is the method named my_method.

•   args   is a list of positional arguments to give to the method. The sample   my_method()  method does not

contain any particular argument we want to give to it, so here is another example:

def   my_method2(self, cr, uid, a, b, c, context=None):   ...

model.call("my_method", [1,   2,   3], ...

// with this a=1, b=2 and c=3

•   kwargs   is a list of named arguments to give to the method. In the example, we have one named argu-

ment which is a bit special:   context. It’s given a value that may seem very strange right now:   new

instance.web.CompoundContext(). The meaning of that argument will be explained later. Right now

you should just know the kwargs argument allows to give arguments to the Python method by name instead of 

position. Example:def   my_method2(self, cr, uid, a, b, c, context=None):   ...

model.call("my_method", [], {a:   1, b:   2, c:   3, ...

// with this a=1, b=2 and c=3

Note:   If you take a look at the my_method()‘s declaration in Python, you can see it has two arguments named  crand uid:

def   my_method(self, cr, uid, context=None):

You could have noticed we do not give theses arguments to the server when we call that method from JavaScript. That

is because theses arguments that have to be declared in all models’ methods are never sent from the OpenERP client.

These arguments are added implicitly by the OpenERP server. The first one is an object called the  cursor  that allowscommunication with the database. The second one is the id of the currently logged in user.

call()  returns a deferred resolved with the value returned by the model’s method as first argument. If you don’t

know what deferreds are, take a look at the previous chapter (the part about HTTP requests in jQuery).

4.9.2 CompoundContext

In the previous part, we avoided to explain the strange  context argument in the call to our model’s method:

model.call("my_method", [], {context:   new  instance.web.CompoundContext()})

In OpenERP, models’ methods should always have an argument named  context:def   my_method(self, cr, uid, context=None):   ...

The context is like a “magic” argument that the web client will always give to the server when calling a method. The

context is a dictionary containing multiple keys. One of the most important key is the language of the user, used by

the server to translate all the messages of the application. Another one is the time zone of the user, used to compute

correctly dates and times if OpenERP is used by people in different countries.

46 Chapter 4. OpenERP Web Framework

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 51/57

OpenERP Web Training, Release 1.0

The argument is necessary in all methods, because if we forget it bad things could happen (like the application not

being translated correctly). That’s why, when you call a model’s method, you should always give it to that argument.

The solution to achieve that is to use the  instance.web.CompoundContext class.

CompoundContext is a class used to pass the user’s context (with language, time zone, etc...) to the server as well

as adding new keys to the context (some models’ methods use arbitrary keys added to the context). It is created by

giving to its constructor any number of dictionaries or other  CompoundContext  instances. It will merge all those

contexts before sending them to the server.

model.call("my_method", [], {context:   new  instance.web.CompoundContext({’new_key’:   ’key_value

def   display_context(self, cr, uid, context=None):

 print   context

//   will   print: {’lang’:   ’en_US’,   ’new_key’:   ’key_value’,   ’tz’:   ’Europe/Brussels’,   ’uid’:  

You can see the dictionary in the argument  context contains some keys that are related to the configuration of the

current user in OpenERP plus the  new_key key that was added when instantiating  CompoundContext.

To resume, you should always add an instance of   instance.web.CompoundContext  in all calls to a model’s

method.

4.9.3 Queries

If you know OpenERP module development, you should already know everything necessary to communicate with

models and make them do what you want. But there is still a small helper that could be useful to you :  query().

query()  is a shortcut for the usual combination of  search and  read methods in OpenERP models. It allows to

search records and get their data with a shorter syntax. Example:

model.query([’name’,   ’login’,   ’user_email’,   ’signature’])

.filter([[’active’,   ’=’,   true], [’company_id’,   ’=’, main_company]])

.limit(15)

.all().then(function   (users) {

// do work with users records

});

query()   takes as argument a list of fields to query in the model. It returns an instance of the

instance.web.Query class.

Query  is a class representing the query you are trying to construct before sending it to the server. It has multiple

methods you can call to customize the query. All these methods will return the current instance of  Query:

•   filter allows to specify an OpenERP domain. As a reminder, a domain in OpenERP is a list of conditions,

each condition is a list it self.

•   limit sets a limit to the number of records returned.

When you have customized you query, you can call the  all() method. It will performs the real query to the server

and return a deferred resolved with the result. The result is the same thing return by the model’s method  read() (a

list of dictionaries containing the asked fields).

To have more information about the   query()   helper, see the reference documentation:

https://doc.openerp.com/trunk/web/rpc/  .

4.10 Exercises

4.10. Exercises 47

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 52/57

OpenERP Web Training, Release 1.0

Exercise - Message of the Day

Create a widget   MessageOfTheDay   that will display the message contained in the last record of the

message_of_the_day. The widget should query the message as soon as it is inserted the the DOM and

display the message to the user. Display that widget on the home page of the OpenERP Pet Store module.

Solution:

openerp.oepetstore   =   function(instance) {

var   _t   =   instance.web._t,

 _lt   =   instance.web._lt;

var   QWeb   =   instance.web.qweb;

instance.oepetstore   =   {};

instance.oepetstore.HomePage  =  instance.web.Widget.extend({

template:   "HomePage",

start:   function() {

var   motd   =   new   instance.oepetstore.MessageOfTheDay(this);

motd.appendTo(this.$el);},

});

instance.web.client_actions.add(’petstore.homepage’,  ’instance.oepetstore.HomePage’);

instance.oepetstore.MessageOfTheDay  =  instance.web.Widget.extend({

template:   "MessageofTheDay",

init:   function() {

this._super.apply(this, arguments);

},

start:   function() {

var   self   =   this;

new   instance.web.Model("message_of_the_day").query(["message"]).first().then(funct

self.$(".oe_mywidget_message_of_the_day").text(result.message);});

},

});

}

<?xml version="1.0" encoding="UTF-8"?>

<templates   xml:space="preserve">

<t   t-name="HomePage">

<div  class="oe_petstore_homepage">

</div>

</t>

<t  t-name="MessageofTheDay"><div  class="oe_petstore_motd">

<p   class="oe_mywidget_message_of_the_day"></p>

</div>

</t>

</templates>

.oe_petstore_motd   {

 margin:   5px;

 padding:   5px;

48 Chapter 4. OpenERP Web Framework

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 53/57

OpenERP Web Training, Release 1.0

 border-radius:   3px;

 background-color:   #F0EEEE;

}

Exercise - Pet Toys List

Create a widget PetToysList that will display 5 toys on the home page with their names and their images.

In this OpenERP addon, the pet toys are not stored in a new table like for the message of the day. They are in

the table product.product. If you click on the menu item  Pet Store > Pet Store > Pet Toys

you will be able to see them. Pet toys are identified by the category named   Pet Toys. You could need to

document yourself on the model  product.product to be able to create a domain to select pet toys and not

all the products.

To display the images of the pet toys, you should know that images in OpenERP can be queried from the

database like any other fields, but you will obtain a string containing Base64-encoded binary. There is a little

trick to display images in Base64 format in HTML:

<img   class="oe_kanban_image" src="data:image/png;base64,${replace this by base64}"></image>

The   PetToysList  widget should be displayed on the home page on the right of the   MessageOfTheDay

widget. You will need to make some layout with CSS to achieve this.

Solution:

openerp.oepetstore   =   function(instance) {

var   _t   =   instance.web._t,

 _lt   =   instance.web._lt;

var   QWeb   =   instance.web.qweb;

instance.oepetstore   =   {};

instance.oepetstore.HomePage  =  instance.web.Widget.extend({

template:   "HomePage",

start:   function() {

var   pettoys   =   new  instance.oepetstore.PetToysList(this);pettoys.appendTo(this.$(".oe_petstore_homepage_left"));

var   motd   =   new   instance.oepetstore.MessageOfTheDay(this);

motd.appendTo(this.$(".oe_petstore_homepage_right"));

},

});

instance.web.client_actions.add(’petstore.homepage’,  ’instance.oepetstore.HomePage’);

instance.oepetstore.MessageOfTheDay  =  instance.web.Widget.extend({

template:   "MessageofTheDay",

init:   function() {

this._super.apply(this, arguments);

},

start:   function() {var   self   =   this;

new   instance.web.Model("message_of_the_day").query(["message"]).first().then(funct

self.$(".oe_mywidget_message_of_the_day").text(result.message);

});

},

});

instance.oepetstore.PetToysList  =  instance.web.Widget.extend({

template:   "PetToysList",

4.10. Exercises 49

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 54/57

OpenERP Web Training, Release 1.0

start:   function() {

var   self   =   this;

new   instance.web.Model("product.product").query(["name",   "image"])

.filter([["categ_id.name",   "=",   "Pet Toys"]]).limit(5).all().then(function(re

 _.each(result,   function(item) {

var   $item   =   $(QWeb.render("PetToy", {item:   item}));

self.$el.append($item);

});

});

},

});

}

<?xml version="1.0" encoding="UTF-8"?>

<templates   xml:space="preserve">

<t   t-name="HomePage">

<div  class="oe_petstore_homepage">

<div  class="oe_petstore_homepage_left"></div>

<div   class="oe_petstore_homepage_right"></div></div>

</t>

<t  t-name="MessageofTheDay">

<div  class="oe_petstore_motd">

<p   class="oe_mywidget_message_of_the_day"></p>

</div>

</t>

<t   t-name="PetToysList">

<div  class="oe_petstore_pettoyslist">

</div>

</t>

<t   t-name="PetToy">

<div  class="oe_petstore_pettoy">

<p><t   t-esc="item.name"/></p><p><img   t-att-src="’data:image/jpg;base64,’+item.image"/></p>

</div>

</t>

</templates>

.oe_petstore_homepage   {

display:   table;

}

.oe_petstore_homepage_left   {

display:   table-cell;

width   :   300px;

}

.oe_petstore_homepage_right   {

display:   table-cell;

width   :   300px;

}

.oe_petstore_motd   {

 margin:   5px;

 padding:   5px;

 border-radius:   3px;

50 Chapter 4. OpenERP Web Framework

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 55/57

OpenERP Web Training, Release 1.0

 background-color:   #F0EEEE;

}

.oe_petstore_pettoyslist   {

 padding:   5px;

}

.oe_petstore_pettoy   {

 margin:   5px;

 padding:   5px;

 border-radius:   3px;

 background-color:   #F0EEEE;

}

4.10. Exercises 51

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 56/57

OpenERP Web Training, Release 1.0

52 Chapter 4. OpenERP Web Framework

8/10/2019 Odoo for All Ages

http://slidepdf.com/reader/full/odoo-for-all-ages 57/57