1. 前提
将局部style放到list.rhtml中,调整label浮动和固定长度,input等宽,再将左侧和右侧的select命名成不同的class,配以不同的margin-left
把form_for改成remote_form_for。这个东西相当好用.对于prototype来说,常态是使用request,简单情形才是在辅助方法里面给出:update
2. form_builder私有化
:builder => TaggedBuilder form_builder私有化,这个主意很棒
#
# tagged_builder.rb
#
# generate like :
# <p>
# <label for="desc">描述</label>
# <%= form.text_field 'desc'%>
#</p>
#
class TaggedBuilder < ActionView::Helpers::FormBuilder
#metapragramming to gernate method for method_name
(field_helpers - %w(check_box radio_button) ).each do |selector|
src = <<-END_SRC
def #{selector}(field, options = {})
options[:class] ||= "biaodan"
label_name=options[:label] || field.to_s
@template.content_tag("p",
@template.content_tag("label", label_name + ":", :for => field.to_s) +
super, :id => "p"+field.to_s, :class => "pb")
end
END_SRC
class_eval src, __FILE__, __LINE__
end
end
3. 把在view中重复出现的代码塞到helpers中。
把radio_button和select+observe_field放入Helpers中,方便在view中调用
引用model中的常量并传递参数,指定remote_function
def num_select(i, form)
id = 'corp_ivr_flow_n'+i.to_s
s = form.select 'n'+i.to_s, @selectingnode,{:prompt => '请选择'}, :class=>"biaodan", :style => "width:100px"
s += observe_field id, :url => { :action => :select_node_changed },
:with => 'n'+i.to_s
end
def fangshou_radio(play_mode, form)
form.radio_button('play_mode', play_mode, :onclick => remote_function(:url => { :action => :change_playmode, :id => @corp_ivr_flow, :play_mode => play_mode }))
end
这样在view中的调用简化成
<%=fangshou_radio(CorpIvrFlow::PLAY_MODE_PA, form)%>
<%=fangshou_radio(CorpIvrFlow::PLAY_MODE_CHOICE, form)%>
<%=num_select(i, form)%>
如果复杂的if可以使用“if的block化写法”
4. controller
controller中的before_filter 和 only参数,redirect时也会被再次执行,需要进一步学习cache
before_filter :get_node , :only => [:edit, :change_playmode]如果觉得不需要有内容回应,可render nothing
render :nothing => true使用 render :update可以在controller中直接返回类似rjs的东西
render :update do |page|
page.call 'showNewNode'
end
showNewNode是基于Extjs窗体的定制,然后调用。
5. 修改Extjs的图像应用链接
Extjs的s.gif是空白图像,默认竟然直接引用自extjs.com
经修改,在javascripts/adapter/prototype/ext-prototype-adapter.js中修改路径,还有在javascripts/adapter/ext/ext-base.js中也能修改,但对tree而言,是第一个起作用
6. 让Extjs.window同rails配合起来
基本思路: window可以获取html脚本中“埋伏”的元素(el),并在合适的时候显示出来。于是,这个埋伏的元素使用rails生成出来,再在需要的时候通过rjs的方式show出来。更进一步,对已show出来的window也能通过调用replace_html来重新产生显示的内容。
A. html
<div id="hello-win" class="x-hidden">
<div class="x-window-header">新建语音节点 -输入基本信息</div>
<div id="hello-tabs" >
<div id="newformpage" style="height:500px;">
<%= render :partial => "newform" -%>
</div>
</div>
</div>_newform.rhtml
通过 :html => { :id => "newform" }指定这个form的id
<% remote_form_for :corp_ivr_flow, :url => {:action => 'new', :id => @corp_ivr_flow}, :builder => TaggedBuilder, :html => { :id => "newform" } do |form| %>
<input type="hidden" name="nodeid" id="nodeid" value='<%=params[:nodeid] ||= 0 %>' />
<%= render :partial => "editnode_p1", :object => form %>
<% end %>
B.js
提交时,调用由rails生成的该form的onsubmit代码
function showNewNode(){
if(!win){
win = new Ext.Window({
el:'hello-win',
layout:'fit',
width:500,
height:200,
closeAction:'hide',
plain: true,
modal: true,
items: new Ext.TabPanel({
el: 'hello-tabs',
autoTabs:true,
activeTab:0,
deferredRender:false,
border:false
}),
buttons: [{
text:'提交',
handler: function(){
form = $('newform');
form.onsubmit();
}
//disabled:true
},{
text: '关闭',
handler: function(){
win.hide();
}
}]
});
}
win.show();
}
C. rails
rails主要处理两种情况:1. 操作成功后,提示用户操作完成并关闭窗口,回到主界面。
2. 操作失败,提示用户失败信息。
C1. 成功时
render :update do |page|
page.call 'win.hide'
# page.replace_html 'newformpage', :partial => "newform"
end
C2. 先给model加上一个验证
validates_presence_of :name, :message => '必须输入节点名称'controller中
render :update do |page|
page.replace_html 'newformpage', :partial => "newform"
end
更新的东西就是_newform,这里需要写入出错信息,于是又要回到rhtml那边,原先的模板里面并没有考虑出错信息。 可以使用error_messages_for 或者error_message_on。希望能把出错信息提示在form元素的最近的边上,于是又打起了helpers的主意,重新修改了私有化的form_builder
class TaggedBuilder < ActionView::Helpers::FormBuilder
#metapragramming to gernate method for method_name
(field_helpers - %w(check_box radio_button) ).each do |selector|
src = <<-END_SRC
def #{selector}(field, options = {})
options[:class] ||= "biaodan"
label_name=options[:label] || field.to_s
@template.content_tag("p",
@template.content_tag("label", label_name + ":", :for => field.to_s) +
super+ @template.error_message_on("corp_ivr_flow", field.to_s), :id => "p"+field.to_s, :class => "pb")
end
END_SRC
class_eval src, __FILE__, __LINE__
end
end
终于明白@template其实起到了<% %>的作用,而1.2.3的rails中,error_message_on不能传入实例变量,新版的似乎已经可以。查看rails代码,代码是仅按字符串再来获取对应的实例变量。
这样的话,无错误时,同原来一样,有错误时也发生了作用,但是排版很乱,于是还是要对view进行调整。
手工输入@corp_ivr_flow.errors.add(:name, 'doit') 查看出错信息。
formError是错误信息的css class,于是先把它设成行内,情况没有好转多少。于是看生成的html代码,发觉问题原来在于对错误的字段,rails会在input元素前面加上<div class="fieldWithErrors">,这样布局就乱掉了。
<p class="pb" id="pname"><label for="name">节点名称:</label><div class="fieldWithErrors"><input class="biaodan" id="corp_ivr_flow_name" label="节点名称" name="corp_ivr_flow[name]" size="10" type="text" /></div><div class="formError">doit</div></p>
修改成如下css
.formError {
display: inline;
}
.fieldWithErrors{
display: inline;
}
好了问题基本解决。
另外,也用到了一个有条件验证:
validates_numericality_of :phone_agent, :message => '必须输入数字,不得为空', :only_integer => true, :if => Proc.new { |c| c.play_mode == CorpIvrFlow::PLAY_MODE_AGENT}
7. ajax 方式提交form产生乱码
ajax提交的序列化后的form乱码 因为js的encodeURIComponent会使字符安全,碰到这种问题的人虽然有,但网上情况看来rails基本解决了这个问题,但我还是遇到了。尝试了下,可以通过URI.decode可以解码,但应该在一个filter里面解决。不过这个不是普遍现象,于是怀疑版本有问题,于是尝试升级到1.2.6(原来是1.2.3)
gem install rails -v 1.2.6
gem clean再修改环境变量到1.2.6,然后发现在1.2.6也一样。
最后,发现是由于一个form中有两个同名的input导致。所以不是prototype封装的问题,就是rails解装的问题。但只要input不同名,那么两边配合就很正常,不会有乱码了。
8. 操作树(Extjs增加节点、删除节点,ruby对树的操作)
ruby侧操作树比较简单,只是不能直接操作parent_id,而用acts_as_nested_set提供的接口move_to_child_of等api。
Extjs侧则出乎意料的麻烦。麻烦在于,增加节点的时候,要区分是加在原先的叶节点
下面还是原先是树节点。如果原先是树节点,那么很简单,add之后了事。如果原先是叶节点,那么先要把这个东西变成树节点,然后才能加新的node。好在还有replaceChild,使得不是太烦。
增加child
function appendChild(childid, name, fatherid, isclickchild) {
var child = new Ext.tree.TreeNode({
text: name,
draggable:false,
id: childid
});
var father = getFather(fatherid);
father.appendChild(child);
father.expand();
var clickon ;
if (isclickchild) {
clickon = child;
} else {
clickon = father;
}
theTree.getSelectionModel().select(clickon);
clickon.expand;
tree_on(clickon, null);
}function getFather (fatherid){
if (fatherid == null || fatherid == "") {
return theTree.getRootNode()
}
thisN = theTree.getNodeById(fatherid)
if(!thisN.isLeaf()){
return thisN;
} else {
ppNode = thisN.parentNode; //得到要添加新节点的节点的父节点
// ppNode.removeChild(thisN); //删除当前节点
var thisNode = new Ext.tree.TreeNode({
text: thisN.text,
draggable:false,
id: thisN.id,
leaf: false
}); //创建一个非叶节点
ppNode.replaceChild(thisNode, thisN)
// ppNode.appendChild(thisNode); //添加非叶节点到父节点
ppNode.expand();
}
return thisNode
}remove
function removeChild(childid, name, fatherid) {
var node = theTree.getNodeById(childid);
node.remove();
if (fatherid !=null ) {
var father = theTree.getNodeById(fatherid);
father.expand();
theTree.getSelectionModel().select(father);
father.expand;
tree_on(father, null);
}
}
更新node只要setText就可以了。
9. 用flash而不是params在action与view间通信
试了下<%= text_field_tag :phoneno, flash[:phoneno], :size => 11 %>
这样比较直接比较简单。
10. 关联对象通过指定外键定义的注意事项
belongs_to :agent, :class_name => 'Agent', :foreign_key => 'phone_agent'
agent就是会成为一个属性的关联对象,外键就是phone_agent。但我开始的时候犯了个错误,把这两个定义成同名的东西了,于是我总是得不到这个foreign_key。回想一下,在coc的情况下,关联的对象和foreign_key也是不同名的。但我们往往需要同时访问这两个东西,如果定义同名的话,foreign_key作为字段会被关联对象这个属性替代(覆盖)。
11. 直接调用底层数据库连接
ActiveRecord::Base.connection.execute sql
#或者CorpIvrFlow.connection.execute sql
本文介绍如何在Ruby on Rails应用中优化表单布局,利用ExtJS增强用户体验,包括自定义表单生成器、使用ExtJS窗口及树组件、处理AJAX提交问题等。
3391

被折叠的 条评论
为什么被折叠?



