Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Java Новый топик    Ответить
 JSF, PrimeFaces - динамическое добавление, редактирование и удаление элементов  [new]
Aghial
Member

Откуда:
Сообщений: 25
Добрый день

Столкнулся с проблемой, при реализации на Primefaces с версиями <jsf.version>2.2.8</jsf.version> <primefaces.version>3.0</primefaces.version> динамического добавления и удаления компонентов inputTextarea с редактированием внутри них значений

В итоге удалось реализовать более менее работающий макет, но столкнулся с трудностью- при редактировании значения - если тут же не убирая фокус добавить новый элемент - подтягивается предыдущее нередактированное значения. Приложил скрин с описанием проблемы

Создал репо, в котором текущий код можно легко запустить используя mvn clean install run:jetty
https://github.com/aradess/jsf-jetty-dyn

Собственно вопрос, - каким должен быть код, чтобы редактирования и последующая вставка нового значения проходила корректно?

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:c="http://java.sun.com/jsp/jstl/core">

<h:head>
    <title>PrimeFaces Hello World Example</title>
</h:head>

<h:body>
    <h:form>
        <p:panel id="main">
            <h:panelGrid columns="2">
                <h:outputText value="Add value"
                              style="vertical-align: top; width: 200px;"/>
                <p:selectOneMenu
                        widgetVar="type" id="type"
                        style="width: 350px"
                        value="#{helloWorld.selectedType}">
                    <f:selectItem itemValue="#{null}" itemLabel=""/>
                    <f:selectItems value="#{helloWorld.availableFields}" var="def" itemLabel="#{def}" itemValue="#{def}"/>
                    <p:ajax event="change"
                            update="main"
                            listener="#{helloWorld.change(helloWorld.selectedType)}"/>/>
                </p:selectOneMenu>
            </h:panelGrid>
            <h:panelGroup id="panelRepeatWrapper">
                <c:forEach id="panelRepeat" items="#{helloWorld.selectedFields}" var="field">
                    <h:panelGrid columns="3">
                        <h:outputText value="#{field}"
                                      style="vertical-align: top; width: 200px;"/>
                        <h:inputTextarea value="#{helloWorld.values[field]}"
                                         style="width: 550px; height: 50px;"/>
                        <p:commandButton value="x" action="#{helloWorld.onDelete(field)}" update="main">
                        </p:commandButton>
                    </h:panelGrid>
                </c:forEach>
            </h:panelGroup>
        </p:panel>
    </h:form>
</h:body>
</html>


package groupid;

import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

@ManagedBean
@SessionScoped
public class HelloWorld {
    private final static List<String> ENTITIES = Arrays.asList(new String[]{"1", "2", "3", "4", "5"});

    @PostConstruct
    public void init() {
        selectedFields = ENTITIES.stream().filter(s -> !"5".equals(s)).collect(Collectors.toSet());
        availableFields = ENTITIES.stream().filter(s -> "5".equals(s)).collect(Collectors.toSet());
        values.put("1", "1");
        values.put("2", "2");
        values.put("3", "3");
        values.put("4", "4");
    }

    private String selectedType = "";
    private Set<String> availableFields;
    private Set<String> selectedFields;
    private Map<String, String> values = new HashMap<String, String>();

    public Set<String> getAvailableFields() {
        return availableFields;
    }

    public void setAvailableFields(Set<String> availableFields) {
        this.availableFields = availableFields;
    }

    public Set<String> getSelectedFields() {
        return selectedFields;
    }

    public void setSelectedFields(Set<String> selectedFields) {
        this.selectedFields = selectedFields;
    }

    public Map<String, String> getValues() {
        return values;
    }

    public void setValues(Map<String, String> values) {
        this.values = values;
    }

    public void change(String type) {
        Optional<String> f = availableFields.stream().filter(s -> s.equals(type)).findFirst();
        selectedFields.add(f.get());
        availableFields.remove(f.get());
        values.put(f.get(), null);
    }

    public void onDelete(String type) {
        Optional<String> f = selectedFields.stream().filter(s -> s.equals(type)).findFirst();
        selectedFields.remove(f.get());
        availableFields.add(f.get());
        values.put(f.get(), null);
    }

    public String getSelectedType() {
        return selectedType;
    }

    public void setSelectedType(String selectedType) {
        this.selectedType = selectedType;
    }
}


К сообщению приложен файл. Размер - 30Kb
6 авг 19, 15:44    [21943044]     Ответить | Цитировать Сообщить модератору
 Re: JSF, PrimeFaces - динамическое добавление, редактирование и удаление элементов  [new]
забыл ник
Member

Откуда:
Сообщений: 3062
попробуй ui:repeat из facelets вместо c:forEach из jstl. Но я сильно не вчитывался, просто знаю что c:orEach хреновенько работает с динамически добавляемыми элементами
6 авг 19, 15:49    [21943048]     Ответить | Цитировать Сообщить модератору
 Re: JSF, PrimeFaces - динамическое добавление, редактирование и удаление элементов  [new]
vas0
Member

Откуда: Таможенный союз (Россия, Казахстан)
Сообщений: 1291
JSTL по моему вообще не работает и вмести с JSF их лучше не использовать
6 авг 19, 15:52    [21943053]     Ответить | Цитировать Сообщить модератору
 Re: JSF, PrimeFaces - динамическое добавление, редактирование и удаление элементов  [new]
Aghial
Member

Откуда:
Сообщений: 25
забыл ник
попробуй ui:repeat из facelets вместо c:forEach из jstl. Но я сильно не вчитывался, просто знаю что c:orEach хреновенько работает с динамически добавляемыми элементами


пробовал ui:repeat - работоспособного варианта не получилось, то есть с c:forEach удалось продвинуться дальше, но попробую
6 авг 19, 17:09    [21943127]     Ответить | Цитировать Сообщить модератору
 Re: JSF, PrimeFaces - динамическое добавление, редактирование и удаление элементов  [new]
Aghial
Member

Откуда:
Сообщений: 25
vas0,

совет по использованию JSTL был найден на stackoverflow, в то же время там была найден пост о различиях https://stackoverflow.com/questions/3342984/jstl-in-jsf2-facelets-makes-sense

Проблема в том, что я практически не работал ранее с JSF и не хотелось бы собирать все его грабли
Потому и прошу совета
6 авг 19, 17:12    [21943132]     Ответить | Цитировать Сообщить модератору
 Re: JSF, PrimeFaces - динамическое добавление, редактирование и удаление элементов  [new]
Aghial
Member

Откуда:
Сообщений: 25
Попробовал ui:repeat
https://github.com/aradess/jsf-jetty-dyn/blob/feature/repeat/src/main/webapp/helloworld.xhtml
https://github.com/aradess/jsf-jetty-dyn/blob/feature/repeat/src/main/java/groupid/HelloWorld.java

Как бы работает, но это не то что нужно. Это все реализовано через события вида onclick=submit() - но это приводит к ререндерингу всей формы. А реальный кейс, в котором используется данная функциональность - сложнее, и ререндеринг в нем лишний.

Попробую уточнить загвоздку, которую нужно решить, которая есть в следующем коде:

Нужно чтобы при выборе значения в selectOneMenu и соответственно запуска в ajax listener="#{helloWorld.change(helloWorld.selectedType)}"/> , чтобы перед запуском этого listener значения которые введены в h:inputTextarea были синхронизированы с helloWorld.values[field]

Потому что сейчас наблюдается ситуация:
1) Ввели значение в Поле 3 (и это никак не отобразилось на значениях в helloWorld.values)
2) Выбрали добавление элемента из выпадающего списка
3) Обновили элемент c id="main" значениями которые в helloWorld.values (а они устаревшие)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:c="http://java.sun.com/jsp/jstl/core">

<h:head>
    <title>PrimeFaces Hello World Example</title>
</h:head>

<h:body>
    <h:form>
        <p:panel id="main">
            <h:panelGrid columns="2">
                <h:outputText value="Add value"
                              style="vertical-align: top; width: 200px;"/>
                <p:selectOneMenu
                        widgetVar="type" id="type"
                        style="width: 350px"
                        value="#{helloWorld.selectedType}">
                    <f:selectItem itemValue="#{null}" itemLabel=""/>
                    <f:selectItems value="#{helloWorld.availableFields}" var="def" itemLabel="#{def}" itemValue="#{def}"/>
                    <p:ajax event="change"
                            update="main"
                            listener="#{helloWorld.change(helloWorld.selectedType)}"/>/>
                </p:selectOneMenu>
            </h:panelGrid>
            <h:panelGroup id="panelRepeatWrapper">
                <c:forEach id="panelRepeat" items="#{helloWorld.selectedFields}" var="field">
                    <h:panelGrid columns="3">
                        <h:outputText value="#{field}"
                                      style="vertical-align: top; width: 200px;"/>
                        <h:inputTextarea value="#{helloWorld.values[field]}"
                                         style="width: 550px; height: 50px;"/>
                        <p:commandButton value="x" action="#{helloWorld.onDelete(field)}" update="main">
                        </p:commandButton>
                    </h:panelGrid>
                </c:forEach>
            </h:panelGroup>
        </p:panel>
    </h:form>
</h:body>
</html>
7 авг 19, 15:09    [21943865]     Ответить | Цитировать Сообщить модератору
 Re: JSF, PrimeFaces - динамическое добавление, редактирование и удаление элементов  [new]
Alexander A. Sak
Member

Откуда: Омск
Сообщений: 1040
Aghial
Нужно чтобы при выборе значения в selectOneMenu и соответственно запуска в ajax listener="#{helloWorld.change(helloWorld.selectedType)}"/> , чтобы перед запуском этого listener значения которые введены в h:inputTextarea были синхронизированы с helloWorld.values[field]


Давно не трогал JSF, но вдруг угадаю.
Добавить у p:ajax атрибут process="main" не пробовали? Вроде по умолчанию процессится только компонент, в котором расположен этот p:ajax.
7 авг 19, 18:53    [21944103]     Ответить | Цитировать Сообщить модератору
 Re: JSF, PrimeFaces - динамическое добавление, редактирование и удаление элементов  [new]
olegeos
Member

Откуда:
Сообщений: 166
А что такая старая версия primefaces?
21 авг 19, 22:07    [21954883]     Ответить | Цитировать Сообщить модератору
 Re: JSF, PrimeFaces - динамическое добавление, редактирование и удаление элементов  [new]
olegeos
Member

Откуда:
Сообщений: 166
 
            <p:panel id="main2">
                <h:panelGrid  columns="2">
                    <h:outputText value="Add value"
                                  style="vertical-align: top; width: 200px;"/>
                    <p:selectOneMenu 
                        widgetVar="type" id="type2"
                        style="width: 350px"
                        value="#{helloWorld.selectedType}">
                        <f:selectItem  itemLabel=""/>
                        <f:selectItems value="#{helloWorld.availableFields}" var="def" itemLabel="#{def}" itemValue="#{def}"/>
                        <p:ajax event="change"
                                update="main2"
                             listener="#{helloWorld.change2()}"/>/>
                    </p:selectOneMenu>
                </h:panelGrid>
                <p:panel id="panelRepeatWrapper2">

                    <ui:repeat id="panelRepeat1" value="#{helloWorld.listTextArea}" var="field" >
                        <h:panelGrid columns="4">
                            <h:outputText value="#{field.nomerRow}"
                                          style="vertical-align: top; width: 200px;"/>
                            <h:inputTextarea value="#{field.area}"
                                             style="width: 550px; height: 50px;"/>
                            <p:commandButton id="button1" value="x" actionListener="#{helloWorld.onDelete2(field)}"  update="form1:panelRepeatWrapper2"  />
                            <p:message  for="button1" showDetail="true" showSummary="true"/>
                        </h:panelGrid>

                    </ui:repeat>

                </p:panel>
            </p:panel>


public class HelloWorld {

    private final static List<String> ENTITIES = Arrays.asList(new String[]{"1", "2", "3", "4", "5"});

    private final String[] array = new String[]{"1", "2", "3", "4", "5"};
    private List<TestList> listTextArea;

    @PostConstruct
    public void init() {
        selectedFields = ENTITIES.stream().filter(s -> !"5".equals(s)).collect(Collectors.toSet());
        availableFields = ENTITIES.stream().filter(s -> "5".equals(s)).collect(Collectors.toSet());
        values.put("1", "1");
        values.put("2", "2");
        values.put("3", "3");
        values.put("4", "4");
        //----------
        listTextArea = new ArrayList<>();

    }

    private String selectedType = "";
    private Set<String> availableFields;
    private Set<String> selectedFields;
    private Map<String, String> values = new HashMap<>();

    public Set<String> getAvailableFields() {
        return availableFields;
    }

    public void setAvailableFields(Set<String> availableFields) {
        this.availableFields = availableFields;
    }

    public Set<String> getSelectedFields() {
        return selectedFields;
    }

    public void setSelectedFields(Set<String> selectedFields) {
        this.selectedFields = selectedFields;
    }

    public Map<String, String> getValues() {
        return values;
    }

    public void setValues(Map<String, String> values) {
        this.values = values;
    }

    public void change(String type) {
        Optional<String> f = availableFields.stream().filter(s -> s.equals(type)).findFirst();
        selectedFields.add(f.get());
        availableFields.remove(f.get());
        values.put(f.get(), null);
    }

    public void onDelete(String type) {
        Optional<String> f = selectedFields.stream().filter(s -> s.equals(type)).findFirst();
        selectedFields.remove(f.get());
        availableFields.add(f.get());
        values.put(f.get(), null);
    }

    public void change2() {
        TestList ttt = new TestList();
        ttt.setNomerRow("1");
        ttt.setArea(selectedType);
        listTextArea.add(ttt);
    }

    public void onDelete2(TestList test) {
        if (!listTextArea.isEmpty()) {
            listTextArea.remove(test);
            System.out.println(listTextArea.size());
        }
    }

    public String getSelectedType() {
        return selectedType;
    }

    public void setSelectedType(String selectedType) {
        this.selectedType = selectedType;
    }

    public List<TestList> getListTextArea() {
        return listTextArea;
    }

    public void setListTextArea(List<TestList> listTextArea) {
        this.listTextArea = listTextArea;
    }

такой вариант
21 авг 19, 23:44    [21954924]     Ответить | Цитировать Сообщить модератору
 Re: JSF, PrimeFaces - динамическое добавление, редактирование и удаление элементов  [new]
Aghial
Member

Откуда:
Сообщений: 25
olegeos
А что такая старая версия primefaces?


Прощу прощения за столь поздний ответ.
Потому что это суровый legacy с кучей функциональности и без тестов. Поменять версию компонентов нереально
26 авг 19, 00:16    [21957390]     Ответить | Цитировать Сообщить модератору
 Re: JSF, PrimeFaces - динамическое добавление, редактирование и удаление элементов  [new]
Aghial
Member

Откуда:
Сообщений: 25
olegeos
такой вариант


К сожалению у меня не взлетел. Были ошибки компиляции, исправил их, выложил код
https://github.com/aradess/jsf-jetty-dyn/blob/feature/2/src/main/webapp/helloworld.xhtml
По-прежнему была трабла:
1) Ввели новое значение в Поле 3 (и это никак не отобразилось на значениях в helloWorld.values)
2) Выбрали добавление элемента из выпадающего списка
3) После автоматического обновления значение в Поле 3 - старое, до редактирования

Нашел, путем неоднократного гугления по разным запросам следующий пост
https://stackoverflow.com/a/30216301

Предлагается использовать ajax и перехватывать значения в bean непосредственно из AjaxBehaviorEvent, и синхронизировать значение мапы в bean со значением, которое на экране
Вроде бы заработало, буду смотреть дальше

    public void fieldValue(AjaxBehaviorEvent e) {
        if (e.getSource() instanceof UIInput) {
            UIInput input = (UIInput) e.getSource();
            Object value = input.getValue();
            String id = input.getId();
            values.put(id.substring(2), value.toString());
        }
    }

https://github.com/aradess/jsf-jetty-dyn/tree/feature/AjaxBehaviorEvent
26 авг 19, 02:00    [21957403]     Ответить | Цитировать Сообщить модератору
Все форумы / Java Ответить