论坛首页 Web前端技术论坛

通过标签属性扩展来实现operamasks-ui的组件定义

浏览 8775 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2013-04-18  

最近在学习OperamasksUI(版本v2.1)的时候,有了一个想法,想把UI的组件生成通过标签属性扩展的方式来实现。比如对于按钮,通过标签扩展,增加left-icon,right-icon,label,width等属性来实现属性的定义,而通过扩展一个fn属性来达到对事件的定义。如:

<a class="ui-button" label="按钮3" id="btn3" fn="{onClick:function(){alert('按钮3的事件通过扩展属性fn实现');}}"
		left-icon="${ctx }/static/icons/down.png" width="100"
		right-icon="${ctx }/static/icons/help.png"></a>

     这样的标签扩展带来的好处有:

(1) 页面中不再需要大量的JSONJS代码,减少了维护的工作量

(2) 降低了开发人员学习om-ui的难度

(3) 能够方便地通过属性的是否输出或EL表达式等方式实现权限的控制。

(4) 标签扩展的代码样式可以方便地通过代码生成器来达到快速生成代码的目的。

由于我自己是做java开发的,所以对于这里的(3)和(4),是很容易来实现的。其实我的终极目的就是要通过代码生成器来达到快速开发的目的。现在的java应用中,一般都会通过自定义TAG的方式来实现快速开发,自定义tag可以做更多的事情,将很复杂的功能封装在一个简单的tag里,但是考虑到自定义tag的可扩展性和可维护性,以及跨语言平台的原因,我还是更愿意尝试通过标签扩展的方式来实现这个目标(当然,我也很想与同行交流一下,自定义tag与标签扩展方式的优劣)。

在对方法的调用上,Operamaks-ui基于和jquery-ui同样的思想,比如对于Dialogopen方法,调用代码如下:

$("#dialog").omDialog("open");

 这与我们通常所说的面向对象编程理念不太一致,面向对象的编程思想中,对于一个Dialogopen方法,应该是这样的调用代码:

dialog.open()

 就是说,在组件生成之后,应该把句柄暴露给开发者,开发者直接通过该句柄即可完成对方法的调用。

基于这样的思路,我先是对最基本的两个组件:按钮和对话框进行了封装和改造,具体的代码如下(operamasks-ui-plugin.js)

$.omPlugin={};
$.extend.apply($.omPlugin,{
	"omButtonbar":{},
	"omButton":{},
});
$.omPlugin["omButtonbar"]={
	attrs:["width"]
};
$.omPlugin["omButton"]={
	attrs:["label","width","disabled"],
	methods:["changeIcons","changeLabel","click","disable","enable"]
};
$.omPlugin["omDialog"]={
		attrs:["autoOpen|b","closeOnEscape|b","modal|b","resizable|b","title","width","zIndex","dialogClass","draggable|b","height","maxHeight","maxWidth","minWidth"],
		methods:["close","isOpen","open"]
};
$.omPlugin["omDialog-Button"]={
		attrs:["text"]
};
/**
 * 对页面中的控件以定义的class进行初始化
 * @param $config 预定义的配置信息
 */
function initUI($config) {
	var $$config = $config || {};
	// 清理浏览器内存,只对IE起效
	$(document).ajaxStart(function() {
		//
	}).ajaxStop(function() {
		//
	});
	
	function $generateId($uiType){
		$uiType = $uiType || "Control";
		var idCounter = $(window).data("_ID_COUNTER") || 10000;
		var $ret = $uiType + "_" + idCounter;
		$(window).data("_ID_COUNTER",++idCounter);
		return $ret;
	}
	/**
	 * 从扩展标签中提取属性的定义
	 */
	function $buildConfig($obj,$uiType){
		// 这里的id一定需要
		var $config = {id:$obj.attr("id")};
		if(!$config.id){
			$config.id=$generateId();
		} else {
			if($.isPlainObject($$config[$config.id])){
				$.each($$config[$config.id],function(k,v){
					$config[k]=v;
				});
			}
		}
		$obj.attr("id",$config.id);
		var $arrAttr = null;
		var $arrFn=null;
		//加载扩展属性
		if($uiType && $.omPlugin[$uiType]){
			$arrAttr = $.omPlugin[$uiType].attrs;
			$arrFn = $.omPlugin[$uiType].fn;
		}
		$.isArray($arrAttr) && $.each($arrAttr,function(i,attr){
			var $attrN=attr;
			var $attrT=null;
			if(attr && attr.length >2 && attr.charAt(attr.length-2)=="|"){
				$attrN=attr.substring(0,attr.length-2);
				$attrT=attr.charAt(attr.length-1);
			}
			var $v = $obj.attr($attrN);
			if($v && $attrT==="b"){
				$v = $v==="true" ? true : $v==="false"?false : undefined;
			}
			if(typeof($v) != "undefined"){
				($config[$attrN]=$v);
			}
		});
		//从扩展属性fn中取出事件的配置
		if($obj.attr("fn")){
			var fn = eval("("+$obj.attr("fn")+")");
			$.extend($config,fn);
		}
		return $config;
	}
	/**
	 * 创建UI对象,并将对象ID作为句柄附加给window对象,以快速调用其方法
	 */
	function $buildUI($obj,$uiType,$config){
		
		var $ui = $obj[$uiType]($config);
		var $dom = $obj[0];
		$dom._ui=$dom.$=$ui;
		var $instance = $ui.data($uiType);
		var $arrMethod = null;
		if($uiType && $.omPlugin[$uiType]){
			$arrMethod = $.omPlugin[$uiType].methods;
		}
		$.isArray($arrMethod) && $.each($arrMethod,function(i,method){
			if($instance && $.isFunction( $instance[method] )){
				$dom[method]=function(){
					//暴露公共函数
					return $instance[method].apply($instance,arguments);
				};
			}
		});
	}

	// buttonbar工具栏及button
	if($.fn.omButtonbar && $.fn.omButton){
		/**
		 * 生成按钮的配置信息
		 */
		function $buildButtonConfig($obj) {
			var $config = $buildConfig($obj,"omButton");
			return $.extend($config,{
				label : $config.label || $obj.attr("label") || null,
				icons : {
					left : $obj.attr("left-icon") || null,
					right : $obj.attr("right-icon") || null
				}
			});
		}
		$(".ui-buttonbar").each(function() {
			var $this = $(this);
			var $config = $buildConfig($this,"omButtonbar");
			var $buttons = [];
			$(".ui-button", $this).each(function() {
				$buttons.push($buildButtonConfig($(this)));
			});
			$config.btns=$buttons;
			$buildUI($this,"omButtonbar",$config);
		});
	
		$(".ui-button").each(function() {
			var $this = $(this);
			if(!$this.parent(".ui-buttonbar").length){
				$buildUI($this,"omButton",$buildButtonConfig($this));
			}
	    });
	}
	// dialog
	if($.fn.omDialog){
		$(".ui-dialog").each(function() {
			var $this = $(this);
			var $config = $buildConfig($this,"omDialog");
			var $buttons = [];
			$(".ui-dialog-button", $this).each(function() {
				var $btnConfig = $buildConfig($(this),"omDialog-Button");
				$buttons.push($btnConfig);
			});
			$config.buttons=$buttons;
			$buildUI($this,"omDialog",$config);
		});
	}
}

 这里完成了对Buttonbar Button Dialog三个组件的封装。对于支持的属性扩展,通过$.omPlugin[组件类型].attrs进行预定义;而对于需要暴露的方法,通过$.omPlugin[组件类型].methods进行预定义。

 

先来看一下对于ButtonbarButton的测试代码(这里是用jsp技术来做的,但没有夹杂太多的java代码,其他语言的开发者应该可以很方便地把它改造成HTML代码或asp代码,如果有不明白的地方,欢迎进行交流。)

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@include file="/WEB-INF/include/taglibs.jsp"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>插件使用示例</title>
<d:script id="jquery,om-ui" />
<d:script path="/static/om-ui/operamasks-ui-plugin.js" />
<d:css id="om-ui" />

<script type="text/javascript">
	function click(){
		alert("通过全局的事件及fn扩展属性实现点击事件.");
	}
	function changeLabel() {
		btn3.changeIcons({
			right : "${ctx}/static/icons/down.png"
		})
	}
	function disable() {
		btn3.disable();
	}
	function enable() {
		btn3.enable();
	}
	$(document).ready(function() {
		initUI({
			"btn4":{
				label:"按钮4(标签通过预定义的配置实现)",
				width:320,
				onClick:function(e){alert("通过initUI的初始化配置实现点击事件.");}
			}
		});
	});
</script>
<style type="text/css">
html,body {
	width: 100%;
	height: 100%;
	margin: 0;
	padding: 0;
}
</style>
</head>
<body>
	<div class="ui-buttonbar">
		<a class="ui-button" label="按钮1"
			left-icon="${ctx }/static/icons/down.png"
			right-icon="${ctx }/static/icons/help.png" fn="{onClick:click}"></a>
		<a class="ui-button" label="按钮2"
			left-icon="${ctx }/static/icons/down.png"
			right-icon="${ctx }/static/icons/help.png" fn="{onClick:click}"></a>
	</div>
	<a class="ui-button" label="按钮3" id="btn3" fn="{onClick:function(){alert('按钮3的事件通过扩展属性fn实现');}}"
		left-icon="${ctx }/static/icons/down.png" width="100"
		right-icon="${ctx }/static/icons/help.png"></a>
		

	<input type="button" value="更改btn3的标签" onclick="changeLabel();">
	<input type="button" value="禁用" onclick="disable();">
	<input type="button" value="启用" onclick="enable();">
	<!--  -->
	<br/>
	<button class="ui-button" id="btn4"
		left-icon="${ctx }/static/icons/down.png"
		right-icon="${ctx }/static/icons/help.png"></button>
</body>
</html>

 可以看到这里的页面中并没有太多的JS代码,都是一些简单的JS函数。对于没有jquery基础,或者json应用不熟练的开发人员,也能很容易看懂。而且对按钮3的启用和禁用使用的代码为:

btn3.enable();
btn3.disable();

 

这里的btn3就是标签中的ID属性。

生成的页面效果如下图,对于事件和方法和调用都通过了测试:



 

再来看一下对于dialog的测试:

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@include file="/WEB-INF/include/taglibs.jsp"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>插件使用示例</title>
<d:script id="jquery,om-ui" />
<d:script path="/static/om-ui/operamasks-ui-plugin.js" />
<d:css id="om-ui" />

<script type="text/javascript">
	$(document).ready(function() {
		initUI();
	});
</script>
<style type="text/css">
html,body {
	width: 100%;
	height: 100%;
	margin: 0;
	padding: 0;
}

</style>
</head>
<body>
<a class="ui-button" label="打开第二个对话框" fn="{onClick:function(){dialog2.open()}}"></a>
	<div id='dialog1' class="ui-dialog" draggable="false" modal="false" title="第一个对话框" resizable="false">
		<span class="ui-dialog-button" text=" 是否打开 " fn='{click:function(){alert("第一个是否打开:" + dialog1.isOpen());}}'></span>
		<span class="ui-dialog-button" text=" 关闭 " fn='{click:function(){dialog1.close();}}'></span>
		第一个对话框的内容
	</div>
	
	
	<div id='dialog2' class="ui-dialog" autoOpen=false draggable="false" modal="true" title="第二个对话框" resizable="false">
		<span class="ui-dialog-button" text=" 关闭 " fn='{click:function(){dialog2.close();}}'></span>
		第二个对话框的内容(默认是不可见的)
		<a class="ui-button" label="检查是否打开" fn="{onClick:function(){alert('第二个对话框是否打开:' +dialog2.isOpen())}}"></a>
	</div>
</body>
</html>

 页面打开之后的效果如下:



 

点击左上角的【打开第二个对话框】按钮,将会弹出第二个对话框,点击对话框中的【检查是否打开】按钮,会弹出对应的提示信息,如图:



 

 

目前已经实现的只有这两个组件,后续将会不断增加对其他常用组件的实现,希望能与感兴趣的同学一起交流这样的实现方式,也十分期待能有志同道合的朋友能一起进行后续的开发。目前在组件的事件实现方面,是通过扩展了一个fn属性,并在fn属性中传递json格式的事件定义实现的,自己感觉这种方式并不是很好,但是还没更好的方法;也希望各位朋友能提一些宝贵的意见,对事件的处理方面进行一些优化。

  • 大小: 9.3 KB
  • 大小: 17.5 KB
  • 大小: 25.5 KB
   发表时间:2013-04-20  
有这么个想法挺惊喜的,但是在论坛里面发布出来这么长时间,居然一个回复也没有,真心失望啊
0 请登录后投票
   发表时间:2013-04-22  
dinguangx 写道
有这么个想法挺惊喜的,但是在论坛里面发布出来这么长时间,居然一个回复也没有,真心失望啊


首先,你的这个想法非常的好,期待整得更好。
不知道以前你有用过JSF否,以前想整成JSF的一套控件出来,与你使用EL表达式,或者标签等,有异曲同工之处。
期待你的积极贡献
0 请登录后投票
   发表时间:2013-04-23  
haiyupeter 写道
dinguangx 写道
有这么个想法挺惊喜的,但是在论坛里面发布出来这么长时间,居然一个回复也没有,真心失望啊


首先,你的这个想法非常的好,期待整得更好。
不知道以前你有用过JSF否,以前想整成JSF的一套控件出来,与你使用EL表达式,或者标签等,有异曲同工之处。
期待你的积极贡献


peter大神出现,十分激动啊。

本来就是想通过jsp标签的方式来实现的,但是这样的话,与java语言绑定,不适合其他语言应用,也不利于扩展,就改为使用扩展属性的方式来做了。至于JSF,现在已经成了小众框架了,没有做过太深入研究。

现在对V2.1的组件,已经改造了大部分,尤其是对于grid的改造,自己还是比较满意的,对于grid的colmodel不再通过json定义,也不需要自己定义colspan和rowspan,只需要通过<span>标签定义好标题与标题的嵌套关系就行了。另外,还打算为grid增加buttorbar。

只是现在还没有对这些改造做过详细的测试,所以很希望能有朋友一起合作开发。

对于组件事件的定义,现在是通过fn属性的JSON实现的,感觉不是太理想,希望能得到peter的帮助,想一个更好的实现方式。
0 请登录后投票
   发表时间:2013-04-23  
和easyUI好像。。。
0 请登录后投票
   发表时间:2013-04-23  
caomeiliang 写道
和easyUI好像。。。

easyUI不开源,GPL协议不适合商用,所以还是支持国产
0 请登录后投票
   发表时间:2013-04-24  
haiyupeter 写道
dinguangx 写道
有这么个想法挺惊喜的,但是在论坛里面发布出来这么长时间,居然一个回复也没有,真心失望啊


首先,你的这个想法非常的好,期待整得更好。
不知道以前你有用过JSF否,以前想整成JSF的一套控件出来,与你使用EL表达式,或者标签等,有异曲同工之处。
期待你的积极贡献




operamasks-ui的论坛没法登录了,登录后进入论坛显示未登录
0 请登录后投票
   发表时间:2013-04-24  
caomeiliang 写道

operamasks-ui的论坛没法登录了,登录后进入论坛显示未登录


我也遇到了这个问题,本来是想把这个帖子发在operamasks-ui的论坛的,但是一直提示需要登录,所以就把帖子发在这里了。

希望管理员能赶快把登录的问题能解决掉
0 请登录后投票
   发表时间:2013-04-25  
dinguangx 写道
caomeiliang 写道

operamasks-ui的论坛没法登录了,登录后进入论坛显示未登录


我也遇到了这个问题,本来是想把这个帖子发在operamasks-ui的论坛的,但是一直提示需要登录,所以就把帖子发在这里了。

希望管理员能赶快把登录的问题能解决掉

好,正在找管理员帮忙
0 请登录后投票
   发表时间:2013-04-26   最后修改:2013-04-26
这个使用属性的UI与DWZ也一样啊,最早我也觉得easyui不错,只是不开源。DWZ控件很少,OM-UI开源扩展也方便,支持啊。
另外,通过class来找元素,好象性能要并差一点,不知有一篇文章对UI进行测试,有这种情况。
0 请登录后投票
论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics