У меня есть более серьезная проблема, чем та, которую я опишу здесь, но, поскольку было бы сложно объяснить и отладить мой код, я создал меньшую (и глупую) проблему, чтобы воспроизвести ту же ошибку.
На мой взгляд, я хочу напечатать список строк, этот список рассчитывается на основе смещения. В моем классе Java у меня есть огромный статический массив строк. Чтобы рассчитать мой список строк (которые будут напечатаны на странице), я копирую значения из моего статического массива в свой список, начиная со значения «смещение».
На моей странице также есть кнопка «Увеличить смещение», которая увеличивает значение «смещения» с помощью запроса ajax и обновляет результаты (список пересчитывается на основе нового смещения).
Моя проблема: когда я нажимаю кнопку «Увеличить смещение», выполняется код на стороне сервера, смещение увеличивается, но при обновлении возникает проблема: текст, показывающий текущее смещение, обновляется новым значением «смещение». , но то же самое не происходит со списком строк, он обновляется со старыми значениями, а новые значения учитываются, только если я делаю второй запрос, значения, рассчитанные во втором запросе, учитываются только в 3-м обновлении , из-за чего список строк в представлении всегда запаздывает на один запрос.
Посмотрите это изображение (откройте его в новой вкладке, чтобы увеличить):
Давайте посмотрим код...
Кнопка:
<h:form>
<h:commandButton value="Increment offsets">
<f:ajax listener="#{test.incrementOffset()}" render=":stringList" />
</h:commandButton>
</h:form>
Список строк:
<h:panelGroup id="stringList">
<h1>Offsets increment = #{test.offset}</h1>
<comp:stringPrinter offset="#{test.offset}" />
</h:panelGroup>
Вот это компонент, о котором я говорил в заголовке. Список строк не будет вычисляться и распечатываться на странице. Он будет рассчитан и напечатан в составном компоненте.
Прежде чем перейти к коду составного компонента, вот управляемый компонент «Test», используемый в представлении:
@ManagedBean(name="test")
@ViewScoped
public class Test implements Serializable {
private int offset;
public int getOffset() {
return offset;
}
public void incrementOffset(){
offset++;
}
}
Составной компонент: простой, как я уже сказал, он просто получает «смещение» в качестве параметра и печатает список строк. См. код представления ниже:
<h:body>
<composite:interface componentType="stringPrinter">
<composite:attribute name="offset" type="java.lang.Integer" required="true" />
</composite:interface>
<composite:implementation>
<ul>
<ui:repeat value="#{cc.list}" var="string">
<li>#{string}</li>
</ui:repeat>
</ul>
</composite:implementation>
</h:body>
И вот как вычисляется список строк:
@FacesComponent(value="stringPrinter")
public class StringPrinter extends UINamingContainer implements Serializable {
private ArrayList<String> list;
private static String[] LIPSUM = "...".split(" "); // "..." is not the actual string, the string is irrelevant (it has more than 400 words separated by spaces).
private static int ARRAY_SIZE = 12;
private void generateList(){
int offset = (Integer) getAttributes().get("offset");
int position = offset;
list = new ArrayList<String>();
for (int i = 0; i < ARRAY_SIZE; i++){
list.add(LIPSUM[position++ % LIPSUM.length]);
}
}
public ArrayList<String> getList() {
if (null == list) generateList();
return list;
}
}
}
Проблема не возникает, если я не использую компонент. Проблема не возникает, когда список вычисляется в родительском представлении, а компонент не имеет присоединенного класса Java. Но в моей реальной проблеме мне не нужно печатать строки, мне действительно нужно, чтобы вещи обрабатывались составным компонентом, поэтому для этого необходимо иметь выделенный класс.
Некоторая отладка:
Тег ui:repeat может быть проблемой. Если бы вместо печати списка (для чего потребовался бы ui:repeat) мне нужно было напечатать первую строку в списке, все было бы правильно обновлено в нужное время. См. код составного компонента без использования ui:repeats:
<h:body>
<composite:interface componentType="stringPrinter">
<composite:attribute name="offset" type="java.lang.Integer" required="true" />
</composite:interface>
<composite:implementation>
<h:outputText value="#{cc.list.get(0)}" />
</composite:implementation>
</h:body>
Приведенный выше код работает, но это не то, что мне нужно делать, мне действительно нужно использовать ui:repeat. Я также пробовал c: forEach, тот же результат.
Для дальнейшей отладки я задал себе вопрос: «Вызывается ли метод getList() в компоненте после выполнения метода incrementOffset() на странице?». Это журнал, который я получил:
*** GET LIST CALLED!
*** GET LIST CALLED!
*** OFFSET INCREMENTED!
*** GET LIST CALLED!
*** GET LIST CALLED!
Я использовал System.out.flush() для предотвращения буферизации сообщений. Кажется, что getList() вызывается после выполнения метода, я не вижу причин для обновления представления старыми значениями. В любом случае, тот факт, что он вызывается два раза непосредственно перед выполнением incrementOffset() и два раза после, немного странен.
Эта проблема затрагивает Mojarra 2.1.7, 2.1.22, 2.1.23 и 2.2.0. Другие версии не проверял.
Я собираюсь обновить этот пост с дальнейшей отладкой.
Чтобы загрузить проект Maven и протестировать его самостоятельно: zip-файл< /а>
Заранее спасибо за любой ответ.