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