p:calendar 在呈现第一个视图后触发 javascript 方法

p:calendar trigger javascript method after first view is rendered

我想创建一个组合作为 <p:calendar> 的增强。这是我的:

calendar.xhtml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:composite="http://java.sun.com/jsf/composite">

  <composite:interface>
    <composite:attribute name="bean" />
  </composite:interface>

  <composite:implementation>
    <h:form id="calendarForm" class="calendarForm" >
      <h:outputScript library="js" name="calendar.js" />
      <p:inputText type="hidden" id="dateInfoMap" class="dateInfoMap" value="#{cc.attrs.bean.dateInfoMap}"/>

        <p:calendar
          id="calendar"
          value="#{cc.attrs.bean.selectedDate}"
          mode="inline" >
          <p:ajax event="viewChange" listener="#{cc.attrs.bean.makeDateInfoMapForMonth}" update="dateInfoMap" oncomplete="alterCalendar(this)" />
        </p:calendar>

    </h:form>
  </composite:implementation>
</html>

函数 cc.attrs.bean.makeDateInfoMapForMonth 创建一个对象,其中包含有关当月每一天的信息(可从 viewChange 事件中获取)并将其存储在 cc.attrs.bean.dateInfoMap.

calendar.js

function alterCalendar(ajaxRequest) {
  let calendar = document.getElementById(ajaxRequest.source);
  let dateInfoMap = getDateInfoMap(calendar);
  // do stuff with this calendar and dateInfoMap
}

function getDateInfoMap(calendar) {
  return JSON.parse($(calendar).closest(".calendarForm").find(".dateInfoMap")[0].value);
}

所有这些都按预期工作:当我更改月份时,新月份会显示 alterCalendar() 中使用 dateInfoMap 中的正确信息所做的所有更改。

但是,这是我第一次更改月份后才完成的。 <p:calendar> 似乎在第一次呈现后不会触发事件。 alterCalendar() 如何在此时应用?

两种可能的解决方案似乎都不适合这样的组件:

  1. 我可以告诉组件的用户调用一个 javascript 函数作为 oncomplete-callback 如果有一个 ajax 触发日历被呈现。这是 a) 对用户不好,b) 并不总是适用,c) 很难动态获取日历实例,而不必手动键入组件 ID。

  2. 我可以使用像 $(document).ready() 这样的表达式,但是如果日历的呈现确实是由另一个 ajax 触发的,这可能不起作用,并且 1 c) 也适用这里。

是否有另一种更优雅的方式可以动态获取日历的实例或 ID?

编辑:我尝试使用 <p:calendar>beforeShow 属性,但它没有任何作用。我正在使用 Primefaces 6.2

kukeltje的帮助下,我解决了问题,也排除了其他问题。

1) 密钥是 #{cc.clientId}。这在复合命名空间中可用,并根据需要为我提供组件的 ID Javascript.

2) 表单不应该是组合的一部分。 See here for more info.

3) 我可以在 xhmtl 页面 Javascript 中的任何地方使用 #{...}

4) 我不需要隐藏输入字段,我可以使用 RequestContext 将值传递给 Javascript 函数。如果我使用 #{cc.attrs.bean.dateInfoMap},该值不会总是最新的,因为它是使用 <p:ajax> 呈现的。

这让我想到了这个解决方案:

calendar.xhtml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:composite="http://java.sun.com/jsf/composite">

  <composite:interface>
    <composite:attribute name="bean" />
  </composite:interface>

  <composite:implementation>
    <h:outputScript library="js" name="calendar.js" />
    <p:calendar
      id="calendar"
      value="#{cc.attrs.bean.selectedDate}"
      mode="inline" >
    <p:ajax
      event="viewChange"
      listener="#{cc.attrs.bean.makeDateInfoMapForMonth}"
      oncomplete="alterCalendar('#{cc.clientId}', args.dateInfoMap)" />
    </p:calendar>
    <script>
      alterCalendar('#{cc.clientId}', '#{cc.attrs.bean.dateInfoMap}');
    </script>
  </composite:implementation>
</html>

豆中:

public void makeDateInfoMapForMonth(DateViewChangeEvent event) {
    dateInfoMap = //...
    RequestContext.getCurrentInstance().addCallbackParam("dateInfoMap", dateInfoMap);
  }

calendar.js

function alterCalendar(componentClientId, dateInfoMap) {
  let calendar = document.getElementById(componentClientId + ":calendar");
  dateInfoMap = JSON.parse(dateInfoMap);
  $(calendar).ready(function () {
    // do stuff
  });
  });
}