手把手带你学会Odoo OWL组件开发(2):OWL的使用
本期摘要
上一篇我们对odoo owl做了一个简单介绍,从本篇开始我们将手把手教你如何通过owl开发odoo项目,今天我们通过一个简单的“hello world”示例来看下owl 在odoo中如何使用。
感兴趣的小伙伴可以复习一下上一篇内容:手把手带你学习Odoo OWL组件开发(1):认识 OWL
作者
沈童 | 前端开发工程师
默默无闻,走向人生巅峰,激流勇进,退居幕后黑手
01
odoo中使用owl
//Step 1
首先创建一个father.js文件,然后创建一个templates.xml文件,并且清单文件__manifest__.py中引用templates.xml文件
demo.js示例:
odoo.define('owl_demo_view', function (require) { //必须引入部分 const { Component, useState ,hooks} = owl; const { xml } = owl.tags; const { useRef, onMounted, onWillStart} = hooks; const {ComponentWrapper} = require("web.OwlCompatibility"); //qweb,单页面渲染引入部分 var core = require('web.core'); var AbstractAction = require('web.AbstractAction'); //创建元素并展示 class Parent extends Component { //static template = 'owl_demo' //方案一 xml綁定 //方案二 xml,编写 static template = xml`
"owl-father">
"state.title">
`
setup() {
this.state = useState({
title:'Hello World' });
}
} //实例化qweb页面 var test_owl = AbstractAction.extend({
start: function () { self = this
$(document).ready(function () { for(const element of self.el.querySelectorAll(".o_content")){ //创建实例并挂载到对应元素中 (new ComponentWrapper(self, Parent)).mount(element);
}
}); //第二种实例化挂载方式 const app = new Parent();
console.log(app,'owl实例') //挂载在页面的那个位置 ,这里的document.body是可以换的 for(const element of self.el.querySelectorAll(".o_content")){ //创建实例并挂载到对应元素中 app.mount(element);
}
},
})
core.action_registry.add('owl_demo_view', test_owl); return test_owl
})
templates.xml示例:
xml version="1.0" encoding="utf-8"?> <odoo> <template id="aa" name="FrStorageExtendReportKanBanExtend" inherit_id="web.assets_backend"> <xpath expr="script[last()]" position="after"> <script type="text/javascript" src="/xc_quotation/static/src/js/demo.js"/> xpath> template> odoo>
__manifest__.py示例:
'data': [ 'views/templates.xml',
], 'qweb': [ 'static/src/xml/demo.xml',
],
//Step 2:创建一个模板xml文件
我这里创建的是demo.xml。这里的xml文件就是你想要渲染的东西, 其中t-name是对应js中渲染实例需要的模板名称,是唯一的,不然浏览器会不知道渲染那个模板,同时这里模板通过demo.js关联后就能拿到类中的数据。
?xml version="1.0" encoding="UTF-8"?> "preserve">
"owl_demo" owl="1">
"owl-father">
"state.title">
当然你也可以不需要创建模板文件,在执行js中编写template也是可以的,这里和vue中的jsx,都是一样的,在demo.js中有讲解。
02
重点
demo.js中单页需要使用到core,和AbstractAction所以必须引入 var core = require('web.core'); 和var AbstractAction = require('web.AbstractAction');
在odoo中你想创建一个单页,是必须通过odoo已有的模块来完成的,必须将类通过命名的方式,注册到odoo中,不然是没办法渲染,并拿到页面的,
这样注册后,odoo中的菜单, menuitem 和action跳转到这个挂载的页面。
这里的 name=“tag”标签中的owl_demo_view,是需要和core.action_registry.add('owl_demo_view', test_owl);这个名字是一样的,odoo才能获取到对应的页面,然后你点击菜单的时候就会渲染
效果如图
这里可以看到t-sec="state"是直接可以获取js中的数据的,通过关联实例 template "owl_demo_father",
这里是通过声明式的渲染方式,将hello world渲染到 t 标签中。
03
什么是声明式渲染
我的理解就是,声明一个变量,并且能直接获取并渲染到页面模板中。
比较官方的说法是:声明式:只告诉程序想要什么结果,如何达成由程序完成,开发者不用关心。
声明式渲染实现的是,DOM 随状态(数据)更新而更新。
而对应的就是有命令式: 一步一步告诉程序如何去做,能否达成结果取决于开发者的设计。
jq的直接操作dom的方式就是命令式的,一步步的告诉浏览器该如何将数据渲染上去
////为什么前端主流框架都是声明式的渲染方式?
我们首先需要声明一个变量,声明变量并渲染
1.声明一个app的常量,并通过新建一个类
const app = new Parent();
2.挂载,并实例化这个类
app.mount(document.body); console.log(app,'owl实例')
//为什么前端主流框架都是声明式的渲染方式?
在VUE和React中都是声明式的渲染方式,为什么呢,因为这样,编写的代码会更少,逻辑和渲染会分离开来,更容易读懂,和拓展。
下面我打印了app这个实例,一起来看看实例中有哪些东西吧,简单的实例当然就是你new class的集合里面的数据,方法, 在这些的同时,owl也封装了一些odoo自有的方法到owl中,这些会在后期与大家讲解。
这里可以打印这个app 可以看到里面的实例化数据, 如果我在这个时候去操作修改声明的变量也是可以的.
app.data.Name='声明变量修改后'
可以看到这里的这个展示的也会同步修改.
当然不建议这样修改,建议通过方法去修改变量,这也是大家常做的方式。
04
弹窗案例(owl)
因为owl是odoo贴合自己本身开发的,所以owl也是使用的qweb的语法,这里有个小的弹窗案例.
1.首先创一个owl的弹窗主体内容的xml(我这里交owl_components.xml,为后面的多组件做准备)
xml version="1.0" encoding="utf-8" ?> <templates xml:space="preserve">
<div> <div class="owl-popup-mask">div> <div class="owl-confirm-xl" id="owlConfirm"> <div class="owl-confirm-title"> <p t-esc="state.title" class="owl-confirm-text">p> div> <div class="owl-confirm-btn"> <div t-on-click="confimrBtn" class="btn btn-primary">确定div> <div t-on-click="closeConfim" class="btn btn-white ml20">取消div> div> div> div>
<div calss="owl-radios"> 測試 div>
templates>
2.创建对应xml组件的owl_components.js文件同样需要引入到清单文件__manifest__.py和template.xml中
owl_components.js示例:
odoo.define('owl_components', function (require) { "use strict"; const { Component, useState ,hooks} = owl; const { xml } = owl.tags; const { useRef, onMounted, onWillStart} = hooks; const {ComponentWrapper} = require("web.OwlCompatibility"); class OwlConfirm extends Component{ static template= "owl_confirm";
setup() { this.state = useState({ title: "測試", row:this.__owl__.parent.parentWidget.recordData
}); this.state.title = 'xxxxxxxx内容部分' }
confimrBtn(url){ console.log(this.state.row,'确定'); let self = this; let paramsData = { id:(this.state.row.id).toString()
} ; console.log(this,'aaaaasss') console.log(this.env,'ssss') this.rpc({ route: 'xxxxx/xxxxx', params: paramsData
}).then(res=>{ console.log(res,'sss') this.env.services.notification.notify({ title:'系统提示', message: res['msg'], type: 'success',
}); // self.destroy(); })
} //关闭 closeConfim(){ this.destroy();
}
} class OwlRadios extends Component{ static template= "owl_radios";
} return {
OwlConfirm,
OwlRadios
}
});
这里直接return两个类,然后再你需要使用的页面,对应位置实例化挂载这个类
//需要引入的两个模块 const {ComponentWrapper} = require("web.OwlCompatibility");//实例化模块 const {OwlConfirm }= require('owl_components');//组件模块 然后我这里是点击后渲染 //修改数据 editRows: function (){
(new ComponentWrapper(this, OwlConfirm)).mount(document.body);
},
css样式
/*弹窗*/ .owl-confirm-xl{ width: 400px; height: 200px; padding: 20px; position: fixed; top: 50%; left:50% ; transform: translateX(-50%); background-color: #fff; z-index: 999; border-radius: 5px; display: flex; flex-direction: column; justify-content: space-between;
} .owl-confirm-title{ flex: 1;
} .owl-confirm-btn{ height: 30px; display: flex; justify-content: flex-end;
} .owl-confirm-text{ font-size: 22px; text-align: center;
} .owl-popup-mask{ display: block; position: fixed; left: 0; top: 0; width: 100%; height: 100%; opacity: 0.5; background: #000000; z-index: 998;
}
效果如图
今天的分享就到这里啦!