设计时组件开发案例
1.Button组件案例(有对应的运行时组件)
所在目录:/UI2/system/components/justep/button
1.1 创建组件目录
在运行时组件目录/UI2/system/ components/justep/button下创建designer目录,该目录放置设计时组件的相关代码和资源,如下图:
1.2 创建组件配置文件
在designer目录下创建button.xml配置文件,这个文件的名称要与运行时组件目录名相同,详细描述如下 :
<!-- 创建根元素,可以同时定义多个element --> <elements> <!-- 定义组件配置,这里的名称通常定义为运行时组件的名称 tag-name 节点的标签名名称 icon 组件显示在工具箱上面的图标, 默认存放在designer/img下 text 组件文本描述信息 binding-component 跟运行时组件绑定 component-type 组件类型,可自定义, 详细可参见设计时组件开发文档 --> <element name="$UI/system/components/justep/button/button" tag-name="a" icon="button.gif" binding-component="$UI/system/components/justep/button/button" component-type="formControl" design-view="web-designer"> <!-- 定义要可视化编辑的属性 --> <properties> <!-- 定义xid属性,所有组件都有这个属性 --> <property name="xid" text="编号" required="true" /> <!-- 定义class样式属性,默认情况下会继承公共配置中的定义的 class公共属性, 继承后公共属性上定义的配置直接体现到当前属性上,比如这里没有editor-ref, 但会继承了公共属性中的editor-ref,该属性就自动默认带有class属性编辑器 --> <property name="class" text="class" editor-ref="classSelector"> <editor-parameter><![CDATA[ { "0base": { label: "基础样式", value: "btn" }, "1color":{ label: "颜色样式", value: "btn-default|btn-primary|btn-success " }, "2size": { label: "尺寸样式", value: "btn-lg|btn-sm|btn-xs", require: false }, "3icon": { label: "图标位置", value: "btn-icon-left|btn-icon-top|btn-icon-right|btn-icon-bottom", require: false } } ]]></editor-parameter> </property> ...... <!-- 包含数据绑定属性 ,$UI/system/components/designerCommon/commonConfig.xml 为为目标文件路径#号后面的//common-properties/group[@name='bind']为标准的xpath表达式 --> <include path="$UI/system/components/designerCommon/commonConfig.xml#//common-properties/group[@name='bind']" /> </properties> <!-- 定义当前组件的事件 --> <events> <event name="onClick" type="event" text="点击事件" /> <!-- 包含公共事件 --> <include path="$UI/system/components/designerCommon/commonConfig.xml#//html-evens/*" /> </events> <!-- 定义模板 --> <templates> <!-- 默认模板,创建当前组件时会生成模板中的节点代码 --> <template name="default"><![CDATA[ <a component="$UI/system/components/justep/button/button" class="btn btn-default" label="button"> <i/><span></span> </a> ]]></template> </templates> </element> </elements>
补充说明:1)classSelector 编辑器传入的参数讲解
编辑器的参数值放到editor-parameter节点中,是一个json格式的数据,json数据的第一层列出了class的的分类,每个分类下面都有一个或多个class,如:
"1color":{ label: "颜色样式", value: "btn-default|btn-primary|btn-success”, require: false }
label表示分组的名称
value 枚举了可选的class列表,多个用 | 号分隔
require 表示该组的class是不是必须要选一个,默认为false
1.3 创建组件js类
Js类主要负责绘制组件的展现,大多数情况下直接继承运行时的组件,在继承的类里加上设计时的相关的一些设置,以及去掉影响设计时设计的一些功能。设计时组件的的代码结构与运行时组件的一样,但在返回组件类的时候有区别,设计时允许返回多个组件类,这样方便多个设计时组件可以写到一个js文件里面,下面是button组件的js类的说明。
在designer目录下创建button.js文件,代码为:
define(function(require) { var $ = require("jquery"); var Util = require("$UI/system/components/justep/common/designer/common"); /*引入设计时w页面 模型操作服务类*/ var xuiService = require("$UI/system/components/designerCommon/js/xuiService"); var xuiDoc = xuiService.getXuiDoc(); /*引入运行时的相关组件类*/ var Button = require("../button"); var ButtonGroup = require("../buttonGroup"); var Radio = require("../radio"); var Checkbox = require("../checkbox"); var Toggle = require("../toggle"); /*引入css样式*/ require('css!./css/button').load(); /*创建类*/ var _Button = Button.extend({ /*重载运行时的init方法,主要做一些设计时需要的额外处理*/ init : function(value, bindingContext) { /*获取到当前组件的dom节点*/ var $domNode = $(this.domNode); /*调用父的init方法*/ this.callParent(value, bindingContext); var cfg = Util.attr2js($domNode, [ 'label', 'icon', 'image' ]); if (cfg) this.set(cfg); var onclick = $domNode.attr('onClick'); if (onclick && 0 < onclick.indexOf('operation')) { // 操作感知 this.on('onClick', onclick); } $domNode.removeAttr('onclick').off('click'); this._d_inited_ = true; /*对dom节点加上各种控制属性,这些控制属性可以参考《设计时组件开发文档》*/ this._getLabelNode().attr('d_selectable', false).attr("d_canRemove", false).attr("d_canAddChild", false); this._getIconNode().attr('d_selectable', false).attr("d_canRemove", false).attr("d_canAddChild", false); this._getImgNode().attr('d_selectable', false).attr("d_canRemove", false).attr("d_canAddChild", false); }, /*在事件编辑里绑定操作后调用的方法,主要是感知操作里的文字和图片*/ bindOperation : function(data) { if (data && data.eventName) this.on(data.eventName, data.value); }, /*重载运行时的属性改变后的处理方法,主要是做一些属性修改后动态更新界面的操作*/ propertyChangedHandler : function(key, oldVal, value) { switch (key) { case "label": this.callParent(key, oldVal, value); if (this._d_inited_) xuiDoc.updateText(this._getLabelNode()); break; case "icon": if (oldVal != value) { } break; default: this.callParent(key, oldVal, value); } } }); /*定义按钮组类*/ var _ButtonGroup = ButtonGroup.extend({ init : function(value, bindingContext) { var $domNode = $(this.domNode); var cfg = Util.attr2js($domNode, [ 'selected' ]); this.callParent(value, bindingContext); if (cfg) this.set(cfg); this.$domNode.off('click'); }, /*右键菜单上的操作,添加按钮组件*/ addButton : function() { xuiDoc.createComponent('$UI/system/components/justep/button/button', this, { paintComponent : true }); } }); /*定义单选按钮类*/ var _Radio = Radio.extend({ init : function(value, bindingContext) { createRadioButton(this, 'radio'); this.callParent(value, bindingContext); this._getInput().off('click'); } }); /*定义多选按钮类*/ var _Checkbox = Checkbox.extend({ init : function(value, bindingContext) { createRadioButton(this, 'checkbox'); this.callParent(value, bindingContext); this._getInput().off('click'); } }); /*返回多个组件类,这里与运行时组件有区别,组件类的key值对应注册文件里面的name属性*/ return { '$UI/system/components/justep/button/button' : _Button, '$UI/system/components/justep/button/buttonGroup' : _ButtonGroup, '$UI/system/components/justep/button/radio' : _Radio, '$UI/system/components/justep/button/checkbox' : _Checkbox }; });
补充说明:以上代码只摘取了实际类中的主体内容,具体的可以直接到组件目录下查看
2.listGroup组件案例(没有对应的运行时组件)
所在目录:/UI2/system/components/bootstrap/listGroup
该组件不存在运行期的组件,只是以html标签的形式展现
2.1 创建组件目录
/UI2/system/components/bootstrap/listGroup/designer
2.2 创建组件配置文件
配置文件与上面案例的格式基本上是一样的,因为这里没有对应运行时组件,所以通过加额外的鉴别条件来识别该组件:discriminate-condition=“hasClass(‘list-group’)”
意思就是当有list-group这个class的节点时,该节点就会被识别成listGroup组件。
<elements> <element name="$UI/system/components/bootstrap/listGroup/listGroup(bootstrap)" tag-name="div" icon="element.gif" text="列表组" discriminate-condition="hasClass('list-group')" resizable="false" component-type="layout-container" design-view="web-designer" > <!-- 右键菜单到工具栏的配置,addItem对应这js类中的方法 --> <toolbar> <item text="添加条目" method="addItem"/> </toolbar> ….. </element> </elements>
2.3创建js类
因为没有对应的运行时组件类,所以直接继承了一个组件基类,如:
define(function(require) { require("$UI/system/components/justep/common/res"); var xuiService = require("$UI/system/components/designerCommon/js/xuiService"); var xuiDoc = xuiService.getXuiDoc(); /*引入组件基类*/ var ViewComponent = require("$UI/system/lib/base/viewComponent"); /*创建组件类*/ var ListGroup = ViewComponent.extend({ /*重载基类的init方法*/ init : function() { var $domNode = $(this.domNode); $(">*", $domNode).attr("d_resizable", "false"); }, /*右键菜单的操作,添加一个子项*/ addItem : function(){ xuiDoc.createComponent("$UI/system/components/bootstrap/listGroup/listGroup(bootstrap)#gruopItem", this, { paintComponent : true }); } }); /*返回组件类*/ return { '$UI/system/components/bootstrap/listGroup/listGroup(bootstrap)' : ListGroup }; });
3.特定功能点案例
3.1 右键菜单弹出编辑页面
以$UI/system/components/justep/data/data 组件的右键菜单“编辑列”为例
执行效果图
代码片断:
var editColumn = function(data, config) { /*打开列编辑页面,页面 会以对话框的形式 打开 */ xuiService.openPage("$UI/system/components/justep/data/designer/editColumn.w", { title : "data列编辑", //对话框的标题 width:800, //对话框的宽度 height:600,//对话框的高度 xml : config.nodeXml //当前节点的xml,改参数会传入到编辑页面中 }, function(result) {//点确定后的回掉方法 //用返回来的节点替换到当前节点的子节点 xuiDoc.replaceChild(data, result.def.join(""), { paintComponent : false, //是否要在设计区立即绘制这些新加的子节点,true 或者 false xpathCondition : "*[local-name()='column']"//目标子节点的查询条件 }); //设置idColumn属性值 xuiDoc.set(data, { idColumn : result.idColumn }); }); };
3.2 批量创建子组件
以$UI/system/components/justep/grid/grid 组件添加列为例
appendColumn: function(config) { var self = this; //获取data的did var dataID = this.get('data'); var data = this.getModel().comp(dataID); if(!data){ alert('请先设置Grid的data属性'); return; } var data_did = data.$domNode.attr('d_id'); // 获取data列信息 var cols = xuiDoc.getEditorDataSource("RuleRelationDatasource.getDatasource",null,data_did); //打开编辑器 xuiService.openPage("$UI/system/components/justep/dataTables/designer/appendColumn.w", { xml : config.nodeXml, cols : cols }, function(result) { var cols = result.cols; if($.isArray(cols)){ var configs = []; //构建要创建的列列表 for(var i=0;i<cols.length;i++){ configs.push({ componentName : "$UI/system/components/justep/grid/grid#column",//注册的列组件名 parentElementId : self.columns_dId,//父的xid templateContent : '<column width="100" name="'+cols[i]+'"/>'//模板内容 }); } //批量创建列 xuiDoc.batchCreateComponent(configs, function() { self.repaintGrid();//重新绘制grid }); } }); }
3.3 节点同步删除
当右键删除一个组件的同时还想同时在删除其他地方的节点时,可以参考以下例子来进行处理。
以$UI/system/components/bootstrap/tabs/tabs(bootstrap) 组件右键删除页签为例,删除页签头时,同时删除页签的内容。
接管TabItem的onRemove 方法,如:
//删除时调用的方法 onRemove : function(event) { if ($("li", this.domNode.parentNode.parentNode).length == 1) { event.cancel = true; } else { //取消默认的删除动作 event.cancel = true; var $domNode = $(this.domNode); //找到内容节点 var content = $domNode.attr("content"); var contentDId = $(">.tab-content>*[xid='" + content + "']", $domNode.parent().parent().parent()).attr("d_id"); //删除内容节点和当前节点 xuiDoc.deleteComponent([ contentDId, $domNode.parent().attr("d_id") ]); } }
结束语:以上是比较典型的案例开发讲解,设计时的组件有些更细的方面这里没有面面俱到,需要更深入了解的话可以去研究更多的组件代码[/fusion_text]