[心缘地方]同学录
首页 | 功能说明 | 站长通知 | 最近更新 | 编码查看转换 | 代码下载 | 常见问题及讨论 | 《深入解析ASP核心技术》 | 王小鸭自动发工资条VBA版
登录系统:用户名: 密码: 如果要讨论问题,请先注册。

[备忘]richfaces的日期控件导致页面载入慢。[已解决]

上一篇:[备忘]Seam中使用Message Driven Bean(MDB)
下一篇:[备忘]JBOSS 配置连接工厂,JBOSS Configuring Connection Factories

添加日期:2009/1/7 14:29:23 快速返回   返回列表 阅读5767次
IE6,页面上有15个<rich:calendar/>控件,显示完毕大概需要5秒,都去掉的话,则马上显示。

查看源代码,在每个日期控件之后,都有一段脚本,里面new Calendar(....)。

将页面另存,将每段new Calendar(...)都挪到html最后,则页面出来很快,

但还是等待状态,一样要等几秒才有响应……

一个破日历这么占CPU,……

https://jira.jboss.org/jira/browse/RF-1717
这个帖子也提到这个问题,我这个已经用的3.2.2版本,还是慢啊……

解决方法见下面。
=========================================================
http://www.codezealot.org/archives/35

Richfaces Calendar Performance

23 May, 2009 in Java by William 

If you are reading this then you probably have come into contact with the richfaces calendar performance problem.

By design, the richfaces calendar will create a calendar every place where you put a rich:calendar tag. This isn’t a problem when you have one or two, but lets say you have a rich:dataTable where one of the columns contains a rich:calendar tag. After about 20 or so rows, the page becomes incredibly slow and almost unusable.

I ran into this exact problem a few months back and just got around to posting the solution.


First take a look at this URL. Following the post fixes the performance issues, however caused other problems.

Problem 1:
If you are using a converter for the date, make sure you only put it on the ‘fake’ calendar inputText. Doing so will allow the controller to accept it as a Date object. Also set the converter to the default pattern that is set in the rich:calendar tag. This will make sure that the converter can convert it correctly.

Problem 2:
In addition to the above converter problem, another problem occured. Make sure to use the s:dateTimeConverter tag. This tag will use the Seam timezone setting, which will be the server timezone unless changed by a user. This is important as I was trying to use the f:dateTimeConverter tag which defaults to GMT. This was causing my dates to be a day off.

Problem 3:
If you are wanting to validate the ‘fake’ calendar value, say it has to be after the current day, via ajax using a a4j:support tag, then you have to make the ‘fake’ calendar’s input box enabled. This poses a problem now because the user can then type in whatever they want. The solution to this issue was to add this.blur(); on the onclick and onkeydown events.

The modified code:

Add
-------------------------------
oncollapse="#{rich:component('calendar')}.customInput.onchange();" 
----------------------------------
to the rich:calendar tag to call the onchange event for the ‘fake’ calendar.

Change
-----------------------
<f:convertDateTime.../> 
--------------------------
to
-----------------------------
 <s:convertDateTime ... pattern="MMM d, yyyy" /> 
-------------------------------
on the ‘fake’ calendar input box, remove the converter from the rich:calendar tag, and make sure the pattern of both the rich:calendar tag and the ‘fake’ calendar input box are the same (the rich:calendar tag uses the above pattern by default).

Add
--------------------------
onkeydown="this.blur();"  
onclick="this.blur(); ....." 
-----------------------------
to the ‘fake’ calendar input box to avoid the issue of the user typing in the input box.

========================================================================

http://www.jroller.com/a4j/entry/richfaces_calendar_component_shared_calendar

RichFaces Calendar component performance. Shared calendar creation. 


One of the popular FAQs risen among the RichFaces community is the rich:calendar performance issue. Calendar component creates and initializes a client side object for every instance on the page according to design. Thus, using the calendar inside some huge tables or just using many instances on some complex form could badly affect the page performance especially using older browsers(IE6).


This entry suggest you the way to use one calendar instance shared between inputs on the page.


UPDATE: Need to mention that the feature described there is based on 3.3.1 snapshots calendar codebase(till CR2 version). And could require additional work in order to work with different versions where the calendar methods code could be changed.
Thanks to our customer feedback


We need to copy one calendar method(doExpand) to our custom JS and perform slight modifications in order to call the calendar from external inputs and return back the value to the correct input. 


Original expansion method takes only an event as a parameter. As we will use some external inputs and also some custom buttons we should add input and button objects to the method parameters. Hence, the definition will be the next


----------------------------------------------
function customExpand(e,input,button)
----------------------------------------------

Then we need to change the line where the base input and button objects evaluated. We will just use the input and button objects from the parameters.


----------------------------------------------
var baseInput = input;
var baseButton = button;//need to store base input on every opening this.customInput = baseInput;
----------------------------------------------

Also the original widget calls the expansion method from doSwitch function. And inside the switch method there is a check verifying whether the calendar is already opened or not. Hence, as we are not using the standard switch we need to add the same check to our custom method:


----------------------------------------------
if (this.isVisible) {    this.doCollapse();    ...}
----------------------------------------------

One additional minor note. We need to close the calendar if it's already opened. And such functionality is achieveed by the code above. But if we open a calendar from the input and then try to call the same calendar from another input, the current one should be closed and a new one should be opened. Thats why we store the customInput on every opening. Thus, we will add one more check in the code above:


----------------------------------------------
if (this.isVisible) {    
this.doCollapse();    
if (input==this.customInput)
        return true;
}
----------------------------------------------

Now the calendar is closed after the second call on the same input instance and reopened for a new instance call.



Now lets explore the page which uses this functionality. From the beginning we should add the calendar components to the page and hide its default elements. Also load our JS with the customExpand method and add the method to a calendar object:

看下面这段。
----------------------------------------------
<style>
    .rich-calendar-input,.rich-calendar-button {
        display: none;    }
</style>
<a4j:loadScript src="/scripts/calendarUtils.js"></a4j:loadScript>

<rich:calendar id="calendar"
    onchanged="#{rich:component'calendar')}.customInput.value=event.rich.component.getSelectedDateString();" />
<script>
    #{rich:component('calendar')}.customExpand=customExpand;
</script>
----------------------------------------------

Note: we defined customInput object to be stored on every calendar call. Hence, we fill this input with the calendar date value after every change. getSelectedDateString method returns the date string formatted according to the component date pattern.



The following code implements three different calendars (which use this shared instance):

看下面这段。
----------------------------------------------
可输入的
<h:outputText value="Editable calendar"/>
<h:panelGrid cellpadding="0" cellspacing="0" columns="2">
    <h:inputText value="#{userBean.name}" id="input" />
    <h:graphicImage
        value="resource:///org.richfaces.renderkit.html.iconimages.CalendarIcon"
        onclick="#{rich:component('calendar')}.customExpand(event, #{rich:element('input')},#{rich:element('button')});"
        id="button" />
</h:panelGrid>

只能选择的
<h:outputText value="Read Only with popup"/>
<h:panelGrid cellpadding="0" cellspacing="0" columns="2">
    <h:inputText value="#{userBean.name}" id="input2" readonly="true"
        onclick="#{rich:component('calendar')}.customExpand(event, #{rich:element('input2')},#{rich:element('button2')});" />
    <h:graphicImage
        value="resource:///org.richfaces.renderkit.html.iconimages.CalendarIcon"
        onclick="#{rich:component('calendar')}.customExpand(event, #{rich:element('input2')},#{rich:element('button2')});"
        id="button2" />
</h:panelGrid>

不能使用的
<h:outputText value="Disabled Calendar"/>
<h:panelGrid cellpadding="0" cellspacing="0" columns="2">
    <h:inputText value="#{userBean.name}" id="input3" disabled="true"/>
    <h:graphicImage    
    value="resource:///org.richfaces.renderkit.html.iconimages.DisabledCalendarIcon"
        id="button3" />
</h:panelGrid>
----------------------------------------------

The first one implements the calendar with editable input. The second one implements default RichFaces calendar behavior - read-only field with the calendar popup button. And the last one shows the icon resource for a disabled state definition.



Client side task is solved. Now the calendar can be called from the custom input field (it will be initialized with the date in input if exists) and and it populates the selected date to the input from which it was called. But you should also remember that now you are responsible for server side value processing. Because comparing to rich:calendar component, h:inputText knows nothing about date. Hence, you should add proper validators and converters to the input components.



看下面这段。
Full script code:
----------------------------------------------


function customExpand(e,input,button) {
    if (this.isVisible) {
        this.doCollapse();
        if (input==this.customInput)
            return true;
    }
   if(!this.isRendered){
    this.isRendered=true;
     this.render()
   }
    this.skipEventOnCollapse = false;
    if (e && e.type=='click') this.skipEventOnCollapse = true;
    if (!this.params.popup || this.isVisible) return;

        var element = $(this.id);
        if (this.invokeEvent("expand", element, e))    {
            var iframe=null;
           if (Richfaces.browser.isIE6) iframe = $(this.IFRAME_ID);
            var base = $(this.POPUP_ID)
            var baseInput = input;
            //custom input
            this.customInput = baseInput;
            var baseButton = button;
            if (baseInput && baseInput.value!=undefined)        {
                this.selectDate(baseInput.value, false, {event:e, element:element});
            }
            //rect calculation
            var offsetBase = Position.cumulativeOffset(baseButton);
            if (this.params.showInput)
        {
            var offsetBase1 = Position.cumulativeOffset(baseInput);
                offsetBase = [offsetBase[0]<offsetBase1[0] ? offsetBase[0] : offsetBase1[0],offsetBase[1]<offsetBase1[1] ? offsetBase[1] : offsetBase1[1>;
            var offsetDimInput = Richfaces.Calendar.getOffsetDimensions(baseInput);
        }
            var offsetDimBase = Richfaces.Calendar.getOffsetDimensions(base);
            var offsetDimButton = Richfaces.Calendar.getOffsetDimensions(baseButton);
            var offsetTemp = (window.opera ? [0,0] : Position.realOffset(baseButton));
            var o = {left: offsetBase[0]-offsetTemp[0],
                 top: offsetBase[1]-offsetTemp[1],
                 width: offsetDimBase.width,
                 height: (offsetDimInput && offsetDimInput.height>offsetDimButton.height ? offsetDimInput.height :offsetDimButton.height)};
        Richfaces.Calendar.setElementPosition(element, o, this.params.jointPoint,this.params.direction, this.popupOffset);
            if (iframe)        {
                iframe.style.left = element.style.left;
                iframe.style.top = element.style.top;
                var edim = Richfaces.Calendar.getOffsetDimensions(element);
                iframe.style.width = edim.width+'px';
                iframe.style.height = edim.height+'px';
                Element.show(iframe);
            }
            Element.show(element);
            this.isVisible = true;
            Event.observe(window.document, "click", this.eventOnCollapse, false);
       }
}
----------------------------------------------


=================================
按上面文章的做法,则所有Calendar的格式只能一样,因为Calendar没有提供setDatePattern的方法,它的datePattern是new的时候就固定的。

如果想页面中Calendar的格式可以不同,需要做一些修改。

(1)追加一个全局变量,保存当前的datePattern
var curDatePattern = "yyyy/MM/dd";

(2)修改calendar控件,getSelectedDateString()方法追加curDatePattern。
<rich:calendar id="calendar"    
onchanged="#{rich:component'calendar')}.customInput.value = event.rich.component.getSelectedDateString(curDatePattern);" />

就是变化的时候,将选择的日期按指定的格式format后,赋值给假的日期框。

(3)假的日期框旁边的图片,修改onclick事件,追加
<h:graphicImage....
onclick="curDatePattern='yyyy/M/d';#{rich:component('calendar')}.....

如果多个控件的格式可能不同的话,那么所有的都要加这句话。
如果都相同的话,都不加就行了。直接指定curDatePattern 变量就行了。

(4)在function customExpand方法中,修改this.selectDate那句话,修改为:


var inputDateForCalendarStr="";
if(baseInput.value.length>0){
    //convert datePattern from one instance's pattern to Calendar Component pattern
    var inputDate = Richfaces.Calendar.parseDate(baseInput.value,curDatePattern,false,false);
    var inputDateForCalendarStr = Richfaces.Calendar.formatDate(inputDate,this.params.datePattern,false,false);
}
this.selectDate(inputDateForCalendarStr,false,..



四步就OK了。

第一次点击小图片,选择日期后,会按curDatePattern格式化日期。
再次点击它时,会按Calendar控件的datePattern去解析日期,
如果两个pattern不一致,那么再次点击时,那个日期不会处于选中状态。
所以在customExpand方法里,先按curDatePattern解析Date,
然后再按Calendar控件的datePattern进行format,然后传给selectDate方法,
就OK了。


 

评论 COMMENTS
没有评论 No Comments.

添加评论 Add new comment.
昵称 Name:
评论内容 Comment:
验证码(不区分大小写)
Validation Code:
(not case sensitive)
看不清?点这里换一张!(Change it here!)
 
评论由管理员查看后才能显示。the comment will be showed after it is checked by admin.
CopyRight © 心缘地方 2005-2999. All Rights Reserved