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