用JavaScript描述富web程序
在富web程序里,JavaScript不再是用来为网页增色的玩物,而成为真正的重点。当一个页面上有了越来越多的数据逻辑,我们如何设计才能虽多、虽杂,却不乱?
比如:要设计一个任务模块,每一个任务都可以有子任务(理论上无限子),当前任务都有一些基本描述。 如下图:
不错,这是一棵任务树。那么我们怎么设计呢?有语言:oo的重点在于抽象出不动点。那么上图什么才是不动点?
首先,相对于数据库而言,数据结构是不动的。由此我们可以抽象出Task对象,它基本对应了数据库的信息。
再者,因为数据结构是不动的,html结构数据也是不动的,我们可以抽象出TaskDom对象,他对应task对象的html描述。(动的只是css布局而已,html结构是不动的)。
Task对象和TaskDom对象互相关联。如下:
var sm = {}; sm.TaskId = 0; sm.Task = function(parent){ //唯一标识符 this.id = sm.TaskId++; //业务属性 this.name = "nothing"; this.userby = "nobody"; this.desc = "nothing"; //节点关系 this.parent = parent||null; this.children = []; //dom属性 this.dom = new sm.TaskDom(this); }; sm.Task.prototype = { add : function(){ var newT = new sm.Task(this); this.children.push(newT); } ,remove : function(){ if(!this.parent)return false; var pChildren = this.parent.children; for(var i=0;i<pChildren.length;i++){ var child = pChildren[i]; if(this.id == child.id){ pChildren.splice(i,1); return true; } } return false; } ,getJson : function(pdata){ this.dom.fillValues(); var data = {"name":this.name,"userby":this.userby,"desc":this.desc,children:[]}; pdata && pdata.children.push(data); for(var i=0;i<this.children.length;i++){ this.children[i].getJson(data); } return data; } };
sm.TaskDomDesc = $("#task_dom").val(); sm.TaskDom = function(task){ this.task = task; this.dom = $(sm.TaskDomDesc); this.dom.attr("id","task-"+this.task.id) this.init(); }; sm.TaskDom.prototype ={ init : function(){ this.appendTo(); this.regEvent(); } ,regEvent : function(){ var self = this; //child event self.dom.find(".control-child").first().bind("click",function(){ self.task.add(); }); //delete event self.dom.find(".control-delete").first().bind("click",function(){ self.task.remove(); self.dom.remove(); }); //up event self.dom.find(".control-up").first().bind("click",function(){ $(self.task.children).each(function(){ this.dom.dom.slideToggle(); }); }); //task-control显示隐藏 self.dom.find(".task-box").first().bind("mouseover",function(){ self.dom.find(".task-control").first().show(); }).bind("mouseout",function(){ self.dom.find(".task-control").first().hide(); }); //高亮 this.dom.find(".task-box").first().bind("mouseover",function(){ self.dom.addClass("task-highlight"); }).bind("mouseout",function(){ self.dom.removeClass("task-highlight"); }); } ,appendTo : function(){ this.dom.appendTo(this.task.parent ? this.task.parent.dom.dom : $("#tasks")); } ,fillValues : function(){ var task = this.task ,dom = this.dom; task.name = dom.find(".task-name").first().val(); task.userby = dom.find(".task-user-by").first().val(); task.desc = dom.find(".task-desc").first().val(); } };
TaskDom中的Html信息我们隐藏在一个display=none的textarea中:
<textarea style="display:none;" id="task_dom"> <div class="task"> <div class="task-box"> <div class="task-head"> <span>task name:</span><input type="text" class="task-name"/> <span class="task-control"> <a class="control-child">child</a>|<a class="control-delete">delete</a>|<a class="control-up">slide</a> </span> </div> <div class="task-body"> <ul> <li><span>user by:</span><input type="text" class="task-user-by"></li> <li><span>desc:</span><input type="text" / class="task-desc"></li> </ul> </div> </div> </div> </textarea>
Task代表任务书中的每个任务节点,TaskDom用来描述Task对象的Html存在。 于是针对上面的任务树,我们现在只关心这2个对象,而隐藏了其他所有的细节。我们只需要实例化出任务树的根节点var root = new sm.Task();其他节点均从此节点派生。 最后可以调用root.getJson()得到所有任务的相关信息。
demo下载(因为用到了JSON对象,请使用chrome,ff等浏览器)