象博客,以及google的iGoogle,都可以个性化页面布局---对页面模块增删改,以及调整位置。这种效果是如何实现的呢?在我的项目中也面临到
这个需求。我最初的解决方法是创建几个典型的rhtml布局组合。但这远远不够,而且不灵活,在形式上也不统一。于是我再次修改了设计,之后似乎可以基本
解决这个问题了。希望感兴趣的朋友一起来讨论。
这个设计其实很简单,就是“引擎+配置“--主体页面只定义一个rthml,可以把它看做页面引擎,然后用一个配置文件指定了页面所应具有的模块和数据。
页面模块就象一个个装有数据的盒子,通过“页面引擎 +
配置文件”把这些盒子组合起来,象搭积木一样。页面引擎是各用户共用的,配置文件是各用户独有的,这样一装配起来,就形成了用户的个性化页面。
剩下的重点就是怎么定义配置文件。首先是要划清配置文件的责任界线----它只负责定义盒子里的数据,还有盒子的嵌套关系,而大小和位置等布局方面则全部交给CSS去负责。
上面是初步想法,下面看看具体实现,代码仅供参考
以下是某个用户的配置文件(我没用XML,而是用YAML)。board_1是指ID=1这个栏目所用的配置定义。topContent是一个
<div>的id值,我把每个栏目的页面分成
topContent顶、sideContent边(左或右由CSS决定)、primaryContent
主要、bottomContent底。topic是指添加一个模块(盒子),显示一个主题内容。和topic类似是还有显示图像的image、显示主题列
表的topics、显示分类列表的categories等等等等。它们各有不同的属性值,比如topic模块,它需要定指它的主题id,以及它所用的
view(topics/_show_hot.rhtml or topics/_show.rhtml等等)。
(虽然这个配置文件抽象得还不够,但这样子已经可以解决我的需要了,那就暂时这样先了。)
template.yml
- board_2:
-
- board_1:
- - topContent:
- - topic:
- topic_id: 7
- view: topics/show_hot
- - sideContent:
- - image:
- url: /images/news.jpg
- - categories:
- board: 5
- view: tree
- - topics:
- board: 5
- per_page: 4
- view: index_simple
- - topics:
- board: 6
- - primaryContent:
- - topics:
- board: 4
- view: index
- - topic:
- topic_id: 7
- view: util/box
-
- board_3:
然后在controller里把配置文件读入,再转化成模型类。我把各个界面模块看做一个个盒子Box
这是它的顶级Box
ruby 代码
- class Box
- attr_accessor :html_id, :view, :boxes
- def initialize
- @boxes=[]
- end
- end
这是topic模块的
ruby 代码
- class TopicBox < Box
- attr_accessor :topic_id
- end
这是Image模块的
ruby 代码
- class ImageBox < Box
- attr_accessor :url
- end
.....等 等, 其他的Box子类大同小异
然后在一个controller里把这些配置信息转成Box模型类
ruby 代码
- templates = YAML::load(File.read("public/uploads/#{user_id}/config/template.yml"))
- template = templates.find{|o| o[0]=="board_#{@board.id}" }
- args = template[1]
-
- @boxes = []
- args.each do |arg1_hash|
- arg1_hash.each do |key1, value1|
- board_box = BoardBox.new
- board_box.html_id = key1
- @boxes << board_box
- value1.each do |arg2_hash|
- arg2_hash.each do |key2, value2|
- case key2
- when 'topics'
- box = TopicsBox.new
- box.board_id = value2['board']
- box.per_page = value2['per_page']||2
- box.view = value2['view']||'index_simple'
- board_box.boxes << box
- when 'categories'
- box = CategoriesBox.new
- box.board_id = value2['board']
- box.view = value2['view']||'list'
- board_box.boxes << box
- when 'image'
- box = ImageBox.new
- box.url = value2['url']
- board_box.boxes << box
- when 'topic'
- box = TopicBox.new
- box.topic_id = value2['topic_id']
- box.view = value2['view']||'util/box'
- board_box.boxes << box
- end
- end
- end
- end
- end
最后是它页面引擎(逻辑代码和页面代码混在一起,比较丑陋)
ruby 代码
- <% @boxes.each do |box1| %>
"<%=box1.html_id%>">
/span>- <% box1.boxes.each do |box2|
- p1 box2.class.to_s
-
- case box2.class.to_s
- when 'TopicsBox'
- board_id = box2.board_id
- if board_id
- board = Board.find(board_id)
- topics = Topic.by_board_id(board_id, :per_page => box2.per_page)
- %>
- <%= render(:partial => "topics/#{box2.view}", :locals => {:title =>board.title, :topics => topics })%>
- <%end
- when 'CategoriesBox'
- board_id = box2.board_id
- if board_id
- board = Board.find(board_id)
- categories = board.categories
- %>
- <%= render :partial => "categories/#{box2.view}", :locals => {:board =>board, :categories => categories } %>
- <%end%>
- <%when 'ImageBox'%>
- <%= box_tag :header=>false%>
- <%=image_tag(box2.url, :border => 0) %>
- <%= end_box_tag %>
- <%when 'TopicBox'
- topic = Topic.find(box2.topic_id) %>
- <%=render(:partial => "#{box2.view}", :locals => {:title =>topic.title, :content => topic.content, :topic => topic })%>
- <%end
- end%>
这样,以后想在页面上增加删除什么模块,修改配置文件就行了。当然给用户用,还必须得用AJAX来写个GUI界面,总不能让用户手工去改配置文件吧。