`
swachian
  • 浏览: 75176 次
  • 性别: 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
 


 

 

 

分享到:
评论

相关推荐

    asp.net 2.0中TREEVIEW中动态增加结点

    ### ASP.NET 2.0中TREEVIEW中动态增加结点 在ASP.NET 2.0中,使用TREEVIEW控件动态地从数据库中获取数据并填充到树形结构中是一项非常实用的功能。本文将详细介绍如何实现这一功能,并通过一个具体的例子来说明其...

    若干城市的信息存入一个带头结点的单链表,结点中的城市信息包括城市名、城市的位置坐标

    1.若干城市的信息存入一个带头结点的单链表,结点中的城市信息包括城市名、城市的位置坐标。要求: (1)给定一个城市名,返回其位置坐标。 (2)给定一个位置坐标P和一个距离D,返回所有与P的距离小于等于D的城市。

    lvm2.0 开源虚拟机(内含详细文档,源代码)

    lvm2.0 为第一版本的升级版。里面还有obj文件说明,.lef文件说明,详细源码(运行时,汇编器,链接器,调试器) 最近将对此做成一个开源项目,向着云计算、云存储方向发展。将其做成智能计算结点的核心部分! 欢迎...

    asp.net 2.0中TREEVIEW中动态增加结点.rar

    这个压缩包文件“asp.net 2.0中TREEVIEW中动态增加结点.rar”显然是关于如何在运行时动态地向TreeView添加节点的教程。在ASP.NET中,这种功能非常有用,因为它允许开发者根据用户交互或后台数据变化来更新控件。 ...

    单循环链表(带头结点和不带头结点)

    本篇文章将深入探讨单循环链表的概念、特点以及如何实现带头结点和不带头结点的两种情况。 1. **单循环链表的基本概念** - **链表**:链表是一种线性数据结构,其元素(节点)在内存中不连续存放,每个节点包含...

    RicheditV2.0

    GroupBox_ItemText.caption =结点文本 BtnSameNode.caption =新结点 BtnSubNode.caption =新子结点 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% *检查框标题定义窗体* FCheckBoxCap.caption =检查框标题...

    mysql master/slave介绍及配置

    Master-Slave的数据库机构解决了很多问题,特别是read/write比较高的web2.0应用: 1、写操作全部在Master结点执行,并由Slave数据库结点定时(默认60s)读取Master的bin-log 2、将众多的用户读请求分散到更多的...

    二叉树上结点的路径

    二叉树上结点的路径 二叉树是一种重要的数据结构,它在计算机科学和信息技术领域中具有广泛的应用。本文将对二叉树上结点的路径进行详细的介绍和分析。 一、什么是二叉树? 二叉树是一种特殊的树形结构,它的每个...

    输出二叉树中某结点的双亲结点

    采用先序法建立一棵二叉树,设计输出某结点数据为x的双亲结点的数据的程序,二叉树的数据域类型为字符型, 扩展二叉树的叶子结点用‘#’表示,要求可以求一棵二叉树中多个结点的双亲。

    建立带有头结点的链表

    头结点通常包含链表的长度信息,即链表中的结点个数。在我们的示例代码中,头结点的数据域用于记录链表的长度。 链表的实现 ------------ 我们的示例代码实现了一个带有头结点的链表。首先,我们定义了链表结点的...

    判别结点u是否为结点v的子孙

    判别结点u是否为结点v的子孙

    编写算法交换二叉树中所有结点的左右子树.doc

    然后,输入第一个结点信息,并判断是否为结束符“#”。如果不是虚结点,则为其申请空间,并将其加入队列 Q 中。如果是虚结点,则不需要申请空间。最后,将队列 Q 的第一个元素作为根结点返回。 二、先序遍历输出...

    C语言写的带有头结点的链表

    本文将详细讨论“带有头结点的链表”这一主题,包括它的实现、翻转和销毁过程,以及如何在实际代码中进行测试。 首先,让我们理解什么是头结点。在链表中,头结点是一个特殊的节点,它不存储任何数据,但作为链表的...

    二叉树叶子结点个数计算.doc

    二叉树叶子结点个数计算 本文档讨论了二叉树叶子结点个数计算的问题,涵盖了二叉树的存储结构、递归算法设计、实现提示、源程序等方面。 一、存储结构 在计算二叉树叶子结点个数时,我们需要首先设计二叉树的存储...

    只有尾结点的链表表示的队列算法

    ### 只有尾结点的链表表示的队列算法 #### 1. 定义结点和队列结构 本篇文章将介绍一种利用链表实现队列的方法,该方法的特点在于只维护一个指向链表尾部(即队列尾部)的指针。这种方法在某些场景下可以有效地减少...

    数据结构算法习题答案带头结点的循环链表表示队列,并且只设一个指针指向队尾元素结点(注意不设头指针).docx

    根据提供的文档内容,我们可以总结出以下关于使用带头结点的循环链表表示队列的数据结构算法知识点: ### 1. 循环链表定义及队列表示 #### 结点类型定义 ```c typedef int Datatype; // 定义数据类型 typedef ...

    C实现删除链表中指定结点

    = i`来实现的,如果`k`不等于用户输入的`i`(即未到达指定位置),则表示链表中不存在该结点,此时函数会提前结束并返回错误信息。 此外,`dellist()`函数还展示了良好的内存管理习惯,即在删除结点后立即调用`free...

Global site tag (gtag.js) - Google Analytics