-----------
更新日期
15:17 2016-02-16 星期二
-----------
* 用到的js库
   我们可以打开 addons/web/views/webclient_template.xml
   看到如下:
        <template id="web.assets_common">
            <script type="text/javascript" src="/web/static/lib/es5-shim/es5-shim.min.js"></script>
            <script type="text/javascript" src="/web/static/lib/underscore/underscore.js"></script>
            <script type="text/javascript" src="/web/static/lib/underscore.string/lib/underscore.string.js"></script>
            <script type="text/javascript" src="/web/static/lib/datejs/globalization/en-US.js"></script>
            <script type="text/javascript" src="/web/static/lib/spinjs/spin.js"></script>

<!-- jQuery stuff -->
            <script type="text/javascript" src="/web/static/lib/jquery/jquery.js"></script>
            <script type="text/javascript" src="/web/static/lib/jquery.blockUI/jquery.blockUI.js"></script>
            <script type="text/javascript" src="/web/static/lib/jquery.hotkeys/jquery.hotkeys.js"></script>
            <script type="text/javascript" src="/web/static/lib/jquery.placeholder/jquery.placeholder.js"></script>
            <script type="text/javascript" src="/web/static/lib/jquery.timeago/jquery.timeago.js"></script>
            <script type="text/javascript" src="/web/static/lib/jquery.form/jquery.form.js"></script>

<script type="text/javascript" src="/web/static/lib/jquery.ba-bbq/jquery.ba-bbq.js"></script>

<script type="text/javascript" src="/web/static/lib/qweb/qweb2.js"></script>
            <script type="text/javascript" src="/web/static/src/js/openerpframework.js"></script>
            <script type="text/javascript" src="/web/static/src/js/tour.js"></script>

<link rel="stylesheet" href="/web/static/lib/fontawesome/css/font-awesome.css"/>
        </template>
        # es5-shim 给傻逼浏览器做兼容性,使得傻逼浏览器可以支持一些 es5 的 api
        # spinjs  ajax异步时,等待出现一个轮的图片
        # datejs 日期处理js
        # jQuery库,这是经典库了 http://t.mb5u.com/jquery/   1.8.3
        # jquery.blockUI 提示窗口
        # jquery.hotkeys 键盘js处理
        # jquery.placeholder 实现文本框显示描述文字
        # jquery.timeago 时间格式
        # jquery.form 表单处理
        # jquery.ba-bbq
        # underscore库,弥补了jQuery没有实现的地方 文档http://www.css88.com/doc/underscore1.6.0/
          所有的功能都封装在名为"_"命名空间内
          常用_.each()来代替javascript中的循环
          _.range()产生一个范围的数列
          function sumTotal(){
            var x=0;
            _.each(_.range(1,101),function(i){
                x+=i;
            });
            console.log("Result",x);
          }
         
*jQuery 简单操作
    #选择器
      @ $("input") 选择特定的HTML元素
      @ $("#content") 选择指定的id的HTML元素
      @ $(".title") 选择明确的css样式类的所有元素
      @ $("span.title") 组合选择元素,可以更精确选择元素
     
    # 事件
      @ $("button").click(function){
            console.log("someone clicked on the button")
        }); // 按钮上的单点事件
       
    # 修改DOM
      @ $(".main_content").html('<div style="color:white">Hello world!' </div>) <!--替换内容-->
      @ $(".main_content").append('<div style="color:white">Hello world again!' </div>) <!--追加内容到后面-->
      @ $(".main_content").prepend('<div style="color:white">Hello world front!' </div>) <!--追加内容到前面-->
      放文本用 text()
      $(".main_content").text('The <div> element will appear as-is in the browser.');
     
    # 异步调用
       服务端:
            @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,
                })
               
        客户端:
            $.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);
            });
           
        说明:
            @ JSON.stringify  把字典转换为json
            @ flask.jsonify 返回json串
           
            在所有的处理异步操作中,总是返回一个deferred
            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 h
                    ave a deferred that represents that.
                    var def2 = def.then(...);
                    // And finally we don't forget to return a deferred because func2() is, b
                    y transitivity, a function
                    // that performs an asynchronous call. So it should return a deferred too.
                    return def2;
                    };           
      
         
*模块定义
    (function() {
        app = {};
        function main() {
            console.log("launch application");
        };
        app.main = main;
    })();   
    采用匿名函数封装
           
       
* 例子
    function openerp_picking_widgets(instance){

var module = instance.stock;
    var _t     = instance.web._t;
    var _lt = instance.web._lt;
    var QWeb   = instance.web.qweb;

// This widget makes sure that the scaling is disabled on mobile devices.
    // Widgets that want to display fullscreen on mobile phone need to extend this
    // widget.

module.MobileWidget = instance.web.Widget.extend({
        start: function(){
            if(!$('#oe-mobilewidget-viewport').length){
                $('head').append('<meta id="oe-mobilewidget-viewport" name="viewport" content="initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">');
            }
            return this._super();
        },
        destroy: function(){
            $('#oe-mobilewidget-viewport').remove();
            return this._super();
        },
    });
   
    #var _t     = instance.web._t; 翻译
   
   
* 采用异步 做 (a+b)*(c+d) 这个功能 ,这是例子,主要参考模式思想
    (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,
                }),
                contentType:"application/json",
            });
           
            var def2 = $.ajax("/service_plus",{
                type:"POST",
                dataType:"json",
                data:JSON.stringify({
                    "c":c,
                    "d":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",
                });
            });
        }
       
       
       
    })();
   
    --------------------------------
    @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,
        })
   
* Web Framework 构造图形化的javascript 应用
    # 基础框架js所在位置 addons/web/static/src/js/openerpframework
    #js 在odoo中的运用
        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');
        }
       
        @ odoo运行时会把所有的javascript文件连接为一个文件,然后压缩,若要调试,必须采用debug模式
        @ 上面创建了, oepetstore 模块,且做为属性放在全局变量openerp中;模块名和addon的模块名要一
           致,否则不能运行
        @ 当载入addon这个模块时,该js模块调用,传入instance参数,这个参数代表当前 OpenERP 的 Web 客户端实例,
           包含了所有相关当前会话数据, 以及所有 Web 模块的变量 
        @ instance.oepetstore = {}; 这是命名空间,用来声明我们模块内自己使用的所有类和变量
       
    # js在odoo定义一个新类
        instance.oepetstore.MyClass = instance.web.Class.extend({
            say_hello: function(){
                console.log("hello");
            }
        });
       
        @ 调用instance.web.Class.extend() 传入一个dictionary 参数
        @ 在方法内,用this访问属性值
            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();
           
        @ 类可以有一个构造函数 init()
            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();
           
        @ 重载方法时,使用 this._super()调用原来的方法
            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();
                           
               
* Widgets
    # 第一个部件
    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');
    这句是把这个部件注册为客户端的action
    @ start 这个方法部件初始化会自动调用
    @ 改造一下用上jQuery 的$el
    instance.oepetstore.HomePage = instance.web.Widget.extend({
        start: function() {
            this.$el.append("<div>Hello dear OpenERP user!</div>")
        },
    });   
   
    # 实例化部件
        instance.oepetstore.GreetingsWidget = instance.web.Widget.extend({
            start: function() {
                this.$el.addClass("oe_petstore_greetings");
                this.$el.append("<div>We are so happy to see you again in this menu!</div>");
            },
        });   
       
        instance.oepetstore.HomePage = instance.web.Widget.extend({
            start: function() {
                this.$el.addClass("oe_petstore_homepage");
                this.$el.append("<div>Hello dear OpenERP user!</div>");       
                var greeting = new instance.oepetstore.GreetingsWidget(this);
                greeting.appendTo(this.$el);
            },
        });

显示结果:
        <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>
       
        @ new instance.oepetstore.GreetingsWidget(this); 实例化部件
          this 参数,在这里代表 HomePage实例,部件有父子关系
          instance.oepetstore.GreetingsWidget = instance.web.Widget.extend({
            start: function() {
                console.log(this.getParent().$el );
                // will print "div.oe_petstore_homepage" in the console
            },
          });   
          ----------
         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
            },
            });
        @ 当重载部件的init()时,必须以父部件作为第一参数传入,并调用传入给 this._super()
            instance.oepetstore.GreetingsWidget = instance.web.Widget.extend({
                init: function(parent, name) {
                    this._super(parent);
                    this.name = name;
                },
            });
            当一个部件没有父部件时,实例化传null参数
           
    # 销毁部件
        greeting.destory()
       
    # 事件
        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) {
                    console.log("The user agreed to continue");
                } else {
                    console.log("The user refused to continue");
                }
            },
        });

@ this 隐式传入到所有的函数,每个已声明的函数都有自己的 this 。
          所以,当我们在一个函数内声明了另一个函数,这个新功能将有自己的 this ,
          这和父函数 this 含义不同,采用了 var self = this; 保存
        @ self.trigger("user_choose", true); 定义user_choose为名事件的触发器
          Widget.trigger(event_name [, ...]) 方法的第一个参数是待触发的事件名,
          也接受任何数量的其他参数。这些参数将被传递到所有的事件侦听器
        @ widget.on("user_choose", this, this.user_choose); 监听user_choose 事件
          Widget.on(event_name, object, func) 允许绑定一个事件 event_name 触发时调
          用的函数 func 。 如果 func) 是个方法,则 object 是 func) 函数的引用关联
          对象。 当 func) 被调用时, trigger() 的其他参数会传递给它
         
    # 属性   
       和普通对象属性一样,但多了一个功能,会触发事件
        start: function() {
            this.widget = ...
            this.widget.on("change:name", this, this.name_changed);
            this.widget.set("name", "Nicolas");
        },
        name_changed: function() {
            console.log("The new value of the property 'name' is", this.widget.get("name"));
        }
       
        @ Widget.set(name, value) 方法设置某个属性的值。如果该值改变(或以前没有
          值) , 对象将触发一个事件 change:xxx : xxx 是属性名称。
          Widget.get(name) 读取属性值。
         
    # 部件辅助工具
       选择器  this.$el.find("input.my_input") <=> this.$("input.my_input")   
       要分析jQuery事件和部件事件
       简化jQuery事件写法
        start: function() {
            var self = this;
            this.$(".my_button").click(function() {
                self.button_clicked();
            });
        }
        --------
        events: {
            "click .my_button": "button_clicked",
        },
      
      
    # 翻译
      记得js源码有两行
        var _t = instance.web._t,
            _lt = instance.web._lt;
        这是导入翻译功能
        this.$el.text(_t("Hello dear user!"));
        和对应python代码中 _() 翻译
        _lt()返回一个函数
        var text_func = _lt("Hello dear user!");
        this.$el.text(text_func());

class openerp.Widget()
    是所有可视化组件的基础类
   
    # DOM Root
        Widget() 得到DOM Root
        openerp.Widget.el  Root
        openerp.Widget.$el  jQuery打包
        openerp.Widget.template 生成Root
        openerp.Widget.tagName  生成元素 默认是div
        openerp.Widget.id  生成Root的id属性
        openerp.Widget.className
        openerp.Widget.renderElement() 渲染是生成Root
       
    # 使用widget
        openerp.Widget.init(parent)
        加元素
        openerp.Widget.appendTo(element) 加到后面
        openerp.Widget.prependTo(element) 加到前面
        openerp.Widget.insertAfter(element) 插在元素的后面
        openerp.Widget.insertBefore(element) 插在元素的前面
       
        openerp.Widget.destory()清理
        openerp.Widget.alive(deferred[, reject=false]) 状态操作
        openerp.Widget.isDestroyed() 检测有没有销毁
       
    # 访问DOM内容
        openerp.Widget.$(selector)
        this.$(selector)
        this.$el.find(selector)
       
    # 重设DOM Root
        openerp.Widget.setElement(element)
           element 可以是元素,也可以jQuery对象
          
    # DOM事件处理
        openerp.Widget.events
        如
        events:{
            'click p.oe_some_class a':'some_method',
            'change input':function(e){
               e.stopPropagation()
            }
       
        }
        openerp.Widget.delegateEvents() 代理绑定到DOM上面
        openerp.Widget.undelegateEvents() 解绑
       
    # 子类Widget
        通过extend
        var MyWidget = openerp.Widget.extend({
        // QWeb template to use when rendering the object
        template: "MyQWebTemplate",
        events: {
            // events binding example
            'click .my-button': 'handle_click',
        },

init: function(parent) {
            this._super(parent);
            // insert code to execute before rendering, for object
            // initialization
        },
        start: function() {
            var sup = this._super();
            // post-rendering initialization code, at this point

// allows multiplexing deferred objects
            return $.when(
                // propagate asynchronous signal from parent class
                sup,
                // return own's asynchronous signal
                this.rpc(/* … */))
         }
        });
       
        // Create the instance
        var my_widget = new MyWidget(this);
        // Render and insert into DOM
        my_widget.appendTo(".some-div");
       
        my_widget.destroy();
       
    # 开发指南
        @ 尽量少用id ,万一要用id 要用 _.uniqueId() 生成
            this.id=_.uniqueId()
           
        @ 尽量少用普通css名字 如 content navigator
        @ 尽量少用全局选择器  用 Widget.$el 或 Widget.$()
        @ 所有的组件都要继承 Widget()
        @ 要用QWeb进行HTML模板和渲染
       
       
* QWeb
    instance.web.Widget 特别支持 QWeb
    <?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>
    前面js源码,有 var QWeb = instance.web.qweb; 这样可以用QWeb功能了
    instance.oepetstore.HomePage = instance.web.Widget.extend({
        start: function() {
            this.$el.append(QWeb.render("HomePageTemplate"));
        },
    });
    @ 渲染了 HomePageTemplate 模板
    @ 也可以采用集成方式
        instance.oepetstore.HomePage = instance.web.Widget.extend({
            template: "HomePageTemplate",
            start: function() {
               
            },
        });
        发生在start方法之前,会用模板的根标签替换部件的默认根标签
   
    # 传递参数
       <t t-name="HomePageTemplate">
        <div>Hello <t t-esc="name"/></div>
    </t>
    ------
    QWeb.render("HomePageTemplate", {name: "Nicolas"});
   
    当采用集成式,要用变量 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() {
        },
    });
   
    # 模板基础 https://doc.odoo.com/trunk/web/qweb/
    <templates>
        <t t-name="HomePageTemplate">
        <div>This is some simple HTML</div>
        </t>
    </templates>
    @ <templates> 模板的根元素
    前缀 t-
        @ t-name 标识模板, QWeb.render() 可以调用指定模板
        @ t-esc 在HTML中放置文本 会转义html标签
        @ t-raw 保持原有内容输出
        @ t-if  条件语句
            <t t-if="true==true">
                true is true
            </t>
       
        @ t-foreach  t-as 循环
            <t t-foreach="names" t-as="name">
                <div>
                    Hello <t t-esc="name" />
                </div>
            </t>
        @ t-att-xx 设置属性值
   
* 部件调用数据
    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),
        }
    -----------
    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.CompoundConte
            xt()}).then(function(result) {
                self.$el.append("<div>Hello " + result["hello"] + "</div>");
            // will show "Hello world" to the user
            });
        },
    });
   
    #连接模型用 instance.web.Model
    #采用model.call()来调用数据 call(name, args, kwargs) 是 Model 的方法
      args 是对象方法的参数列表
      kwargs 命名参数列表,这里只传了context
    # 模型中的方法始终有一个参数 context , context 是一个包含多个key的dictonary
    # CompoundContext 这个类用来传递用户上下文(语言,时区等..) 给服务器的
      其构造函数的参数是任意数量的 dictionary
     
* 部件较好的例子           
    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(function(result) {
                    self.$(".oe_mywidget_message_of_the_day").text(result.message);
                });
            },
        });
        instance.oepetstore.PetToysList = instance.web.Widget.extend({
            template: "PetToysList",
            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(result) {
                    _.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;
        background-color: #F0EEEE;
    }
    .oe_petstore_pettoyslist {
        padding: 5px;
    }
    .oe_petstore_pettoy {
        margin: 5px;
        padding: 5px;
        border-radius: 3px;
        background-color: #F0EEEE;
    }

====================

* RPC
    采用异步的方式来调用
    #高级API
      访问对象方法用 openerp.Module
      映射服务端对象用 call() 和 query()
        var Users = new openerp.Model('res.users');

Users.call('change_password', ['oldpassword', 'newpassword'],
                              {context: some_context}).then(function (result) {
                // do something with change_password result
            });
      query()是 search+read 在后端操作
        Users.query(['name', 'login', 'user_email', 'signature'])
             .filter([['active', '=', true], ['company_id', '=', main_company]])
             .limit(15)
             .all().then(function (users) {
            // do work with users records
        });
                 
      openerp.Model.call(method[, args][, kwargs])
        method 是rpc的方法名
        args 传入方法的参数
        kwargs 关键词参数
       
      openerp.Model.query(fields)
        fields 字段列表
      first() 取第一个记录
       
     class openerp.web.Query(fields)类下方法
        openerp.web.Query.all() 得到上面query() 集的所有
        openerp.web.Query.first() 要第一条,没有就是null
        openerp.web.Query.count() 得到的记录总数
        openerp.web.Query.group_by(grouping...) 分组来列表
        openerp.web.Query.context(ctx) 添加上下文
        openerp.web.Query.filter(domain) 条件过滤domain表达式
        opeenrp.web.Query.offset(offset) 设定起点
        openerp.web.Query.limit(limit) 设定要返回的数量
        openerp.web.Query.order_by(fields…)  记录排序
       
    #聚合
        some_query.group_by(['field1', 'field2']).then(function (groups) {
            // do things with the fetched groups
        })
       
        openerp.web.QueryGroup.get(key)
        得到key的值,key可以为:
            @ grouped_on
            @ value
            @ length
            @ aggregates
           
        openerp.web.QueryGroup.query([fields...]) 等价于
        openerp.web.Model.query()
       
        openerp.web.QueryGroup.subgroups()
       
    #低级API RPC访问python
        opeenrp.session()
        如:
        openerp.session.rpc('/web/dataset/resequence', {
            model: some_model,
            ids: array_of_ids,
            offset: 42
        }).then(function (result) {
            // resequence didn't error out
        }, function () {
            // an error occured during during call
        });
========================
* Web 客户端
    写测试用例
    # 断言
        ok(state[, message])
        strictEqual(actual, expected[, message]) 相当于 ok(actual === expected, message))
        notStrictEqual(actual, expected[, message]) 相当于 ok(actual !== expected, message))
        deepEqual(actual, expected[, message])
        notDeepEqual(actual, expected[, message])
        throws(block[, expected][, message]) 抛出异常
        equal(actual, expected[, message]) 宽松相等
        notEqual(actual, expected[, message])
       
        示例:
        {
            'name': "Demonstration of web/javascript tests",
            'category': 'Hidden',
            'depends': ['web'],
            'js': ['static/src/js/demo.js'],
            'test': ['static/test/demo.js'],
        }
        // src/js/demo.js
        openerp.web_tests_demo = function (instance) {
            instance.web_tests_demo = {
                value_true: true,
                SomeType: instance.web.Class.extend({
                    init: function (value) {
                        this.value = value;
                    }
                })
            };
        };

// test/demo.js
        test('module content', function (instance) {
            ok(instance.web_tests_demo.value_true, "should have a true value");
            var type_instance = new instance.web_tests_demo.SomeType(42);
            strictEqual(type_instance.value, 42, "should have provided value");
        });
       
* 显示图片
  <img class="oe_kanban_image" src ="data:image/png; base64,${replace this by base64}" />
 
* Web Components (Action manager)
    # 看一列子:
    <record model="ir.actions.act_window" id="message_of_the_day_action">
        <field name="name">Message of the day</field>
        <field name="res_model">message_of_the_day</field>
        <field name="view_type">form</field>
        <field name="view_mode">tree,form</field>
    </record>
    <menuitem id="message_day" name="Message of the day" parent="petstore_menu"
    action="message_of_the_day_action"/>
   
    对应下的js 处理,没人xml快捷,但用js更灵活
    instance.oepetstore.PetToysList = instance.web.Widget.extend({
        template: "PetToysList",
        start: function() {
            var self = this;
            new instance.web.Model("product.product").query(["name", "image"])
            .filter([["categ_id.name", "=", "Pet Toys"]]).limit(5).all().the
            n(function(result) {
                _.each(result, function(item) {
                    var $item = $(QWeb.render("PetToy", {item: item}));
                    self.$el.append($item);
                    $item.click(function() {
                        self.item_clicked(item);
                    });
                });
            });
        },
        item_clicked: function(item) {
            this.do_action({
            type: 'ir.actions.act_window',
            res_model: "product.product",
            res_id: item.id,
            views: [[false, 'form']],
            target: 'current',
            context: {},
            });
        },
        });
       
    #上面是窗体Action 下面看焉 client Action
        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');
   
        @ instance.web.client_actions 是一个 Registry类的实例
          要打开关键字是petstore.home的action ,就实例化instance.oepetstore.HomePage
        @对应的菜单
            <record id="action_home_page" model="ir.actions.client">
              <field name="tag">petstore.homepage</field>
            </record>       
         <menuitem id="home_page_petstore_menu" name="Home Page" parent="petstore_menu"
             action="action_home_page"/>

(19)odoo中的javascript的更多相关文章

  1. Odoo中的Javascript单元测试

    前端页面利用QUnit进行单元测试,本文参考官方文档:https://www.odoo.com/documentation/8.0/reference/javascript.html 访问/web/t ...

  2. 在Swift中使用JavaScript的方法和技巧

    本文作者Nate Cook是一位独立的Web及移动应用开发者,是继Mattt大神之后NSHipster的主要维护者,也是非常知名活跃的Swift博主,并且还是支持自动生成Swift在线文档的Swift ...

  3. js-js实现,在HTML中使用JavaScript,基本概念

    Js实现: 1.JavaScript实现的组成: 核心(ECMAScript):由ECMA-262定义,提供核心语言功能 文档对象模型(DOM)提供访问和操作网页内容的方法以及接口 浏览器对象模型(B ...

  4. 第十一章:WEB浏览器中的javascript

    客户端javascript涵盖在本系列的第二部分第10章,主要讲解javascript是如何在web浏览器中实现的,这些章节介绍了大量的脚本宿主对象,这些对象可以表示浏览器窗口.文档树的内容.这些章节 ...

  5. 在WebBrowser中执行javascript脚本的几种方法整理(execScript/InvokeScript/NavigateScript) 附完整源码

    [实例简介] 涵盖了几种常用的 webBrowser执行javascript的方法,详见示例截图以及代码 [实例截图] [核心代码] execScript方式: 1 2 3 4 5 6 7 8 9 1 ...

  6. 在C#中模拟Javascript的setTimeout方法

    在C#中模拟Javascript的setTimeout方法 背景 每种语言都有自己的定时器(Timer),很多人熟悉Javascript中的setInterval和setTimeout,在Javasc ...

  7. HTML中的javascript交互

    在Android开发中,越来越多的商业项目使用了Android原生控件与WebView进行混合开发,当然不仅仅就是显示一个WebView那么简单,有时候还需要本地Java代码与HTML中的javasc ...

  8. JavaScript权威指南--WEB浏览器中的javascript

    知识要点 1.客户端javascript window对象是所有客户端javascript特性和API的主要接入点.它表示web浏览器的一个窗口或窗体,并且可以用window表示来引用它.window ...

  9. 【转载】如何在 C#中访问 JavaScript函数?

    如何在 C#中访问 JavaScript函数? 时间:13-10-17 栏目:Unity3D教程 作者:zqcyou 评论:0     如何在 C#中访问 JavaScript函数?答案如下:c#代码 ...

随机推荐

  1. Android-图片按钮汇总

    <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=&quo ...

  2. Shared Preferences 数据存储

    SharedPreferences类,它是一个轻量级的存储类,特别适合用于保存软件配置参数. 其背后是用xml文件存放数据,文件存放在/data/data/<package name>/s ...

  3. about_Execution_Policies

    https://technet.microsoft.com/en-us/library/hh847748.aspx?f=255&MSPPError=-2147217396 在powershel ...

  4. FZU 2219 StarCraft(星际争霸)

    Description 题目描述 ZB loves playing StarCraft and he likes Zerg most! One day, when ZB was playing SC2 ...

  5. Verify an App Store Transaction Receipt 【苹果服务端 验证一个应用程序商店交易收据有效性】

    转自:http://blog.csdn.net/saindy5828/article/details/6414014 1. 从Transaction 的TransactionReceipt属性中得到接 ...

  6. iOS案例:读取指定txt文件,并把文件中的内容输出出来

    用到的是NSString中的initWithContentsOfFile: encoding方法 // // main.m // 读取指定文件并输出内容 // // Created by Apple ...

  7. Codeforces Round #158 (Div. 2) C. Balls and Boxes 模拟

    C. Balls and Boxes time limit per test 1 second memory limit per test 256 megabytes input standard i ...

  8. CSS笔记(十二)CSS3之2D和3D转换

    参考:http://www.w3school.com.cn/css3/css3_2dtransform.asp 2D Transform 方法 函数 描述 matrix(n,n,n,n,n,n) 定义 ...

  9. 不要温柔地走入AMD

    1.无依赖情况 <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...

  10. C#正则表达式编程(四):正则表达式

    正则表达式提供了功能强大.灵活而又高效的方法来处理文本.正则表达式的全面模式匹配表示法使您可以快速分析大量文本以找到特定的字符模式:提取.编辑.替换或删除文本子字符串:或将提取的字符串添加到集合以生成 ...