0

我对如何解决这个问题没有任何想法,这就是发生的事情:当我单击一个应该更新我的视图(通过 ajax)的按钮时,服务器端代码被正确执行,但视图没有更新。就在我发出第二个 ajax 请求时,它会使用最后一个 ajax 请求的值进行更新。我页面中的每个 ajax 操作都会发生这种情况,这意味着视图上的所有内容始终显示旧数据。几岁?1 请求老,总是。

深入研究这个问题,我发现了一些可能的原因,为此,在接下来的几行中,我将更详细地解释我想要做什么。

我有一个事件列表,每个事件都在一个日期发生。我的页面显示 12 个日历,每个月一个,这些日历的目的是显示该月每一天发生的事件。除了日历,我还有一个面板可以更改年份和一个对话框。每次用户单击日历中的日期时,都会打开该对话框。通过对话可以添加新事件、删除点击事件和编辑点击事件。

每个日历都是我的自定义组件“日历”的一个实例,它总是接收一个月、一年和一个事件列表作为参数。事件列表通过我的 managedBean 中的方法“getEvents(int month)”传递。

我的ajax调用是:

  • NextYear():在我的 managedBean 中增加一年,重新计算事件。完成后,更新我的 12 个日历组件。
  • PreviousYear():类似于 nextYear。
  • updateEvent():更新或创建(如果不存在)被点击的事件。重新计算事件列表。完成后更新视图中的日历。
  • cancelEvent():取消事件,将其从事件列表中移除。完成后更新视图。

正如我之前所说,一切都正确完成,但更新。由 ajax 请求触发的更新只是在下一个 ajax 请求时完成。

为了调查发生了什么,我将 printlns 放在方法“getEvents(int month)”中,以了解它何时被调用以及它发现了什么事件。

令我惊讶的是,在每个 ajaxCall 中都会调用 getEvents,而 ajax 调用是否更新日历并不重要。在执行服务器端方法之前,总是会为 12 个日历调用 getEvents。

我的结论是:更新发生在 ajax 请求完成之前。为了确认我在 ajax 方法中编写了一些 printlns(和刷新)。getEvents 确实在任何 ajax 方法完成之前被调用。我认为当 JSF 调用 update 时,它​​使用在方法执行之前获得的值,而不是新的值,这导致视图总是延迟一个请求。

有人见过这样的问题吗?提前感谢您的帮助。


编辑:代码很复杂。为了简化事情,我将问题简化为“更改年份”要求。如果我删除其他所有内容,问题仍然存在。

我的观点:

<h:outputStylesheet library="components" name="calendar/aqua/calendar.css"  />
<link href="css/event_calendar.css" rel="stylesheet" type="text/css" />

<f:metadata>
  <f:viewParam name="year" value="#{managementEventCalendar.year}" />
  <f:event type="preRenderView" listener="#{managementEventCalendar.preRender()}" />
</f:metadata>

<script type="text/javascript">
  <!-- update the url parameter "year" according to the managed bean -->
  $(document).ready(function(){
    updateUrlParameter('year', #{managementEventCalendar.year});
  });
</script>

<p:growl autoUpdate="true" showDetail="true" life="#{initParam['primefaces.growl.life']}" widgetVar="growlWidgetVar" />

<!-- just a title, irrelevant -->                  
<comp:management_title image="imgs/title_image_eventcalendar.png" title="#{msg['title.eventcalendar']}" description="#{msg['description.eventcalendar']}" styleClass="event" />

<!-- form to change the year -->
<h:form>
  <p class="text-center year-block">
    <p:commandButton icon="ui-icon-circle-arrow-w" actionListener="#{managementEventCalendar.previousYear()}" update="@form :calendar" oncomplete="updateUrlParameter('year', #{managementEventCalendar.year - 1});" />
    <h:outputText styleClass="year-title" value="#{managementEventCalendar.year}" />
    <p:commandButton icon="ui-icon-circle-arrow-e" actionListener="#{managementEventCalendar.nextYear()}" update="@form :calendar" oncomplete="updateUrlParameter('year', #{managementEventCalendar.year + 1});" />
  </p>
</h:form>

<!-- Grid of calendars (12, one for each month) -->
<h:panelGrid id="calendar" columns="3" width="100%" cellpadding="0" cellspacing="0" style="margin-bottom: 15px"
                             columnClasses="calendar-column left, calendar-column center, calendar-column right">
                <comp:calendar month="0" year="#{managementEventCalendar.year}" disablePast="false" theme="aqua" 
                               type="edition" eventDates="#{managementEventCalendar.getEvents(0)}" />
                <comp:calendar month="1" year="#{managementEventCalendar.year}" disablePast="false" theme="aqua" 
                               type="edition" eventDates="#{managementEventCalendar.getEvents(1)}" />
                <comp:calendar month="2" year="#{managementEventCalendar.year}" disablePast="false" theme="aqua" 
                               type="edition" eventDates="#{managementEventCalendar.getEvents(2)}" />

                <comp:calendar month="3" year="#{managementEventCalendar.year}" styleClass="espacamento_vertical" disablePast="false" theme="aqua" 
                               type="edition" eventDates="#{managementEventCalendar.getEvents(3)}" />
                <comp:calendar month="4" year="#{managementEventCalendar.year}" styleClass="espacamento_vertical" disablePast="false" theme="aqua" 
                               type="edition" eventDates="#{managementEventCalendar.getEvents(4)}" />
                <comp:calendar month="5" year="#{managementEventCalendar.year}" styleClass="espacamento_vertical" disablePast="false" theme="aqua" 
                               type="edition" eventDates="#{managementEventCalendar.getEvents(5)}" />

                <comp:calendar month="6" year="#{managementEventCalendar.year}" styleClass="espacamento_vertical" disablePast="false" theme="aqua" 
                               type="edition" eventDates="#{managementEventCalendar.getEvents(6)}" />
                <comp:calendar month="7" year="#{managementEventCalendar.year}" styleClass="espacamento_vertical" disablePast="false" theme="aqua" 
                               type="edition" eventDates="#{managementEventCalendar.getEvents(7)}" />
                <comp:calendar month="8" year="#{managementEventCalendar.year}" styleClass="espacamento_vertical" disablePast="false" theme="aqua" 
                               type="edition" eventDates="#{managementEventCalendar.getEvents(8)}" />

                <comp:calendar month="9" year="#{managementEventCalendar.year}" styleClass="espacamento_vertical" disablePast="false" theme="aqua" 
                               type="edition" eventDates="#{managementEventCalendar.getEvents(9)}" />
                <comp:calendar month="10" year="#{managementEventCalendar.year}" styleClass="espacamento_vertical" disablePast="false" theme="aqua" 
                               type="edition" eventDates="#{managementEventCalendar.getEvents(10)}" />
                <comp:calendar month="11" year="#{managementEventCalendar.year}" styleClass="espacamento_vertical" disablePast="false" theme="aqua" 
                               type="edition" eventDates="#{managementEventCalendar.getEvents(11)}" />
                </h:panelGrid>

关于组件“日历”的一些解释:disablePast 和主题是装饰性的。“type = edition”意味着日历单元格将是可选的。选择的工作方式无关紧要,因为我们只考虑要更改年份的情况。我删除了有关事件选择、编辑和取消的所有代码。所有问题仍然存在。

请参阅下面的组件“日历”的代码。

<composite:interface componentType="calendar">
            <composite:attribute name="month" type="java.lang.Integer" required="true" />
            <composite:attribute name="year" type="java.lang.Integer" required="true" />
            <composite:attribute name="eventDates" type="java.util.List" required="false" />
            <composite:attribute name="style" required="false" />
            <composite:attribute name="styleClass" required="false" />
            <composite:attribute name="disablePast" required="false" default="true" />
            <composite:attribute name="theme" required="false" default="rose" />
            <composite:attribute name="type" required="false" default="tooltip" />
            <composite:attribute name="eventManager" type="com.ae.client.web.management.EventManager" required="false" />
            <composite:attribute name="onselect" required="false" />
            <composite:attribute name="update" required="false" />
        </composite:interface>
        <composite:implementation>
            <h:outputScript library="components" name="calendar/calendar.js" target="head" />

            <c:set var="disabled" value="#{cc.attrs.disablePast and cc.isPastMonth()}" />

            <div class="calendar-component">
                <table cellspacing="0" cellpadding="0" border="0" width="254px" style="#{cc.attrs.style}"
                       class="calendar-table month_#{cc.attrs.month} #{disabled ? 'disabled' : ''} #{cc.attrs.type} #{cc.attrs.styleClass}">

                    <!-- Month name and week days -->
                    <tr>
                        <th rowspan="#{cc.numberOfWeeks + 1}" class="month">
                            <h:graphicImage library="components" alt="#{cc.getMonthName()}"
                                            name="calendar/#{cc.attrs.theme}/#{msg.locale}/month_#{disabled ? 'disabled_' : ''}#{cc.attrs.month}.png" />
                        </th>
                        <th class="week">D</th>
                        <th class="week">S</th>
                        <th class="week">T</th>
                        <th class="week">Q</th>
                        <th class="week">Q</th>
                        <th class="week">S</th>
                        <th class="week">S</th>
                    </tr>

                    <!-- Month days -->
                    <ui:repeat value="#{cc.calendar}" var="week">
                        <tr>
                            <ui:repeat value="#{week}" var="day">
                                <td class="#{(day.isToday() ? 'today' : '')} #{(empty (day.events)) ? '' : 'event'} #{(day.dayOfMonth eq 0) ? '' : 'selectable'}">
                                    <h:panelGroup layout="block" styleClass="relative" rendered="#{not (day.dayOfMonth eq 0)}">
                                        <h:outputText class="day-of-month" value="#{day.dayOfMonth}" />

                                        <!-- events in the day -->
                                        <ui:repeat value="#{day.events}" var="eventDate">
                                            <div class="event-data">
                                                <h:outputText class="id" value="#{eventDate.event.id}" />
                                                <span class="name">
                                                    <h:outputText value="#{eventDate.event.name}" />
                                                    <h:outputText value=" - #{eventDate.description}" rendered="#{not empty(eventDate.description) and eventDate.event.eventDates.size() gt 1}" />
                                                </span>
                                                <h:outputText  class="status" value="#{eventDate.event.status}" />
                                                <h:outputText class="time" value="#{eventDate.date}">
                                                    <f:convertDateTime timeZone="#{msg['date.timezone']}" pattern="#{msg['time.format']}" type="time" />
                                                </h:outputText>
                                            </div>
                                        </ui:repeat>
                                    </h:panelGroup>
                                </td>
                            </ui:repeat>
                        </tr>
                    </ui:repeat>
                </table>
            </div>
        </composite:implementation>

我的托管 bean 代码很简单,我在预渲染事件中分析 url 参数“年份”,并在首次调用 getEvents(int month) 时填充我的事件集合。事件集合实际上是将月份 (0-11) 与事件日期列表 (EventDate) 相关联的映射。一个事件可以发生在多个日期(eventDates)中,每个 eventDate 都有一个日期和一个简短的描述。要转到上一年,我调用方法 previousYear()。请参阅下面的一些 managedBean 代码:

    public void preRender(){
            GregorianCalendar today = new GregorianCalendar();
            if (year == 0) year = today.get(GregorianCalendar.YEAR);
        }

        @Override
        public void updateEventCollection() {
            eventMap = new HashMap<Integer, List<EventDate>>();
            Iterator<Event> eventIterator = eventRepository.findNotCanceledByYear(year).iterator();
            while (eventIterator.hasNext()){
                Event e = eventIterator.next();
                Iterator<EventDate> dateIterator = e.getEventDates().iterator();
                while (dateIterator.hasNext()){
                    EventDate date = dateIterator.next();
                    GregorianCalendar day = new GregorianCalendar();
                    day.setTime(date.getDate());
                    int month = day.get(GregorianCalendar.MONTH);
                    List<EventDate> eventsInMonth = eventMap.get(month);
                    if (null == eventsInMonth){
                        eventsInMonth = new ArrayList<EventDate>();
                    }
                    eventsInMonth.add(date);
                    eventMap.put(month, eventsInMonth);
                }
            }
        }

    public List<EventDate> getEvents(int month){
            if (null == eventMap) updateEventCollection();
            List<EventDate> result = eventMap.get(month);
            if (null == result) result = new ArrayList<EventDate>();
            System.out.println("*** getting events of month " + month + ". " + result.size() + " dates were found.");
            System.out.flush();
            return result;
        }

public void nextYear(){
        year++;
        updateEventCollection();
    }

    public void previousYear(){
        year--;
        updateEventCollection();
    }

我希望它有所帮助。

为了描述我的问题,特别是在“更改年份”要求中:如果页面在 2013 年加载并且我单击“上一个”按钮,则表示年份的文本正确更新为 2013 年,日历似乎也得到了更新,但是使用与我之前(2013 年)相同的值,就好像它根本没有更新。当我再次单击时,年份文本更新为 2011,但日历显示 2012。如果我再次单击,日历将始终显示上一个请求的结果。


编辑:经过一番挖掘,似乎问题在于使用 ,我不知道为什么会导致这样的问题,但是当我删除组件代码的以下部分时,每个变量都成功更新到视图中。

<!-- Month days -->
                    <ui:repeat value="#{cc.calendar}" var="week">
                        <tr>
                            <ui:repeat value="#{week}" var="day">
                                <td class="#{(day.isToday() ? 'today' : '')} #{(empty (day.events)) ? '' : 'event'} #{(day.dayOfMonth eq 0) ? '' : 'selectable'}">
                                    <h:panelGroup layout="block" styleClass="relative" rendered="#{not (day.dayOfMonth eq 0)}">
                                        <h:outputText class="day-of-month" value="#{day.dayOfMonth}" />
                                    </h:panelGroup>
                                </td>
                            </ui:repeat>
                        </tr>
                    </ui:repeat>

如果我删除第二个 ui:repeat 问题继续存在,如果我删除两个问题都会消失,是什么让我认为它与“cc.calendar”的迭代有关。变量 cc.calendar 没有特别的问题,因为要进行测试,删除 后,我在视图中打印以下内容:

<h:outputText value="#{cc.attrs.year}" /><br />
<h:outputText value="#{cc.calendar.size()}" /><br />
<h:outputText value="#{cc.attrs.eventDates.size()}" /><br />

cc.calendar.size() 的值表明一切都在正确的时间正确更新。如果我让这些“打印”在代码中并恢复 ui:repeats,则打印的值是错误的,只要我发出另一个 ajax 请求,它就会更新,总是一个请求迟到。

似乎问题完全在于 ui:repeat 标签,我不知道如何解决它。我试过 c:forEach,同样的问题。

4

0 回答 0