`
swachian
  • 浏览: 73510 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

“结点信息编辑”的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
 


 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics