“結點信息編輯”的web2.0體驗

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
 


 

 

 

發佈了2 篇原創文章 · 獲贊 0 · 訪問量 926
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章