Динамически генерировать лед: компоненты commandButton - PullRequest
2 голосов
/ 04 августа 2009

Я пробовал много разных вещей, которые, я думаю, работали бы так, как ожидалось. Тем не менее, они вызывают у меня некоторое разочарование. Вот совок:

Я использую компоненты ICEFaces 1.8 в веб-приложении Java EE. Моя цель состоит в том, чтобы отобразить на странице кучу льда: commandButtons на основе запроса к моей базе данных. Я хочу, чтобы эти кнопки могли переключать выборки, которые я позже буду использовать для параметров, для другого запроса к базе данных (в основном, это внешний вид запроса для набора пользователей). Я бы хотел, чтобы результат выглядел так:

Deselected

Когда я нажимаю на кнопку, я бы хотел, чтобы на моей странице было следующее обновление:

Selected

Когда я создавал кнопки на своей странице статически, вот так:

<ice:commandButton id="seasonSEP09" style="background-color: #FFFFFF;" partialSubmit="true" actionListener="#{bean.updateSeasons}" value="2009-2010" />
<ice:commandButton id="seasonSEP08" style="background-color: #FFFFFF;" partialSubmit="true" actionListener="#{bean.updateSeasons}" value="2008-2009" />
<ice:commandButton id="seasonSEP07" style="background-color: #FFFFFF;" partialSubmit="true" actionListener="#{bean.updateSeasons}" value="2007-2008" />
<ice:commandButton id="seasonSEP06" style="background-color: #FFFFFF;" partialSubmit="true" actionListener="#{bean.updateSeasons}" value="2006-2007" />

это прекрасно работает, и каждая кнопка работает индивидуально, как я и ожидал. Мой вспомогательный компонент обновлен, параметры правильно добавлены в методе updateSeasons (), а мои выходные данные в конце дают правильные записи.

Однако я знаю, что это не то, чего я хочу. Я не хочу обновлять их каждый раз, когда в систему вводится другой сезон. Ведение кошмара, верно?

Итак, я хочу динамически генерировать эти компоненты ice: commandButton на основе моей таблицы базы данных, полной объектов Season. Вот класс Season, который я использую:

public class Season
{
    String StartMonth;
    String Season;

    public String getStartMonth()
    {
        return StartMonth;
    }
    public void setStartMonth(String startMonth)
    {
        StartMonth = sweep;
    }    
    public void setSeason(String season)
    {
        Season = season;
    }
    public String getSeason()
    {
        return Season;
    }
}

Очень просто. Два свойства, которые я гарантированно буду уникальными в базе данных.

Вот бэк, который я использую:

public class Bean
{
    public Bean()
    {
        defineSeasonsList();
    }

    public List<HtmlCommandButton> seasonsList;

    // seasonsList getter & setter omitted

    public List<String> selectedSeasons;

    // selectedSeasons getter & setter omitted

    private void defineSeasonsList()
    {
        seasonsList = new ArrayList<HtmlCommandButton>();
        selectedSeasons = new ArrayList<String>();

        try
        {
            hibernate.openTransaction();

            for(Season season:defineSeasonsListFromDataSource()))
            {
                HtmlCommandButton button = new HtmlCommandButton();

                button.setId("season" + season.getStartMonth());
                button.setValue(season.getSeason);
                button.setStyle("background-color: #FFFFFF;");
                button.setPartialSubmit(true);

                seasonsList.add(button);
            }                         
        } 
        catch (Exception e)
        {
            System.out.println("Error defining seasons list: " + e.getMessage());
        }
        finally
        {
            hibernate.commitTransaction();
        }               
    }

    public void updateSeasons(ActionEvent ae)
    {
        HtmlCommandButton selected = (HtmlCommandButton) ae.getComponent();

        if(selectedSeasons.contains(selected.getValue().toString()))
        {
            selectedSeasons.remove(selected.getValue().toString());   
            selected.setStyle("background: #FFFFFF;");
        }
        else
        {
            selectedSeasons.add(selected.getValue().toString());
            selected.setStyle("background: #009DD9; color: #FFFFFF;");
        }
    }
}   

Хорошо, вот и возникла моя дилемма (ы).

Сначала я попытался отобразить эту разметку:

<p>
    <ice:panelGroup>
        <ice:panelSeries id="seasonsList" value="#{bean.seasonsList}" var="season">
                <ice:commandButton binding="#{season}"/>                                        
        </ice:panelSeries>
    </ice:panelGroup>
</p>     

И я получаю этот вывод:

Bad Buttons

Итак, будучи разочарованным и предприимчивым, я попытался сделать эту разметку для достижения своей цели:

<p>
    <ice:panelGroup>
        <ice:panelSeries id="seasonsList" value="#{bean.seasonsList}" var="season">
                <ice:commandButton id="#{season.id}" partialSubmit="true" style="background-color: #FFFFFF" value="#{season.value}" actionListener="#{bean.updateSeasons}"/>                                        
        </ice:panelSeries>
    </ice:panelGroup>
</p>   

Который дал следующую трассировку стека:

4 августа 2009 14:28:11 com.sun.faces.lifecycle.Phase doPhase SEVERE: JSF1054: (идентификатор фазы: RENDER_RESPONSE 6, идентификатор представления: /phase1.jspx) Исключение, генерируемое во время выполнения фазы: javax.faces.event.PhaseEvent [source=com.sun.faces.lifecycle.LifecycleImpl@1a477b7] 4 августа 2009 14:28:11 вызов org.apache.catalina.core.StandardWrapperValve SEVERE: Servlet.service () для сервлета Persistent Faces Servlet выбросил исключение java.lang.IllegalArgumentException: # {season.id} в javax.faces.component.UIComponentBase.validateId (UIComponentBase.java:549) в javax.faces.component.UIComponentBase.setId (UIComponentBase.java:351) в javax.faces.webapp.UIComponentTag.createComponent (UIComponentTag.java:219) в javax.faces.webapp.UIComponentClassicTagBase.createChild (UIComponentClassicTagBase.java:486) в javax.faces.webapp.UIComponentClassicTagBase.findComponent (UIComponentClassicTagBase.java:670) в javax.faces.webapp.UIComponentClassicTagBase.doStartTag (UIComponentClassicTagBase.java:1142) в com.icesoft.faces.component.CommandButtonTag.doStartTag (CommandButtonTag.java:741) на com.icesoft.faces.webapp.parser.Parser.executeJspLifecycle (Parser.java:204) на com.icesoft.faces.webapp.parser.Parser.executeJspLifecycle (Parser.java:229) на com.icesoft.faces.webapp.parser.Parser.executeJspLifecycle (Parser.java:229) на com.icesoft.faces.webapp.parser.Parser.executeJspLifecycle (Parser.java:229) на com.icesoft.faces.webapp.parser.Parser.executeJspLifecycle (Parser.java:229) на com.icesoft.faces.webapp.parser.Parser.executeJspLifecycle (Parser.java:229) на com.icesoft.faces.webapp.parser.Parser.executeJspLifecycle (Parser.java:229) на com.icesoft.faces.webapp.parser.Parser.executeJspLifecycle (Parser.java:229) на com.icesoft.faces.webapp.parser.Parser.executeJspLifecycle (Parser.java:229) на com.icesoft.faces.webapp.parser.Parser.executeJspLifecycle (Parser.java:229) на com.icesoft.faces.webapp.parser.Parser.executeJspLifecycle (Parser.java:229) на com.icesoft.faces.webapp.parser.Parser.executeJspLifecycle (Parser.java:229)на com.icesoft.faces.webapp.parser.Parser.parse (Parser.java:162) на com.icesoft.faces.application.D2DViewHandler.renderResponse (D2DViewHandler.java:464) в com.icesoft.faces.application.D2DViewHandler.renderView (D2DViewHandler.java:153) на com.sun.faces.lifecycle.RenderResponsePhase.execute (RenderResponsePhase.java:110) на com.sun.faces.lifecycle.Phase.doPhase (Phase.java:100) на com.sun.faces.lifecycle.LifecycleImpl.render (LifecycleImpl.java:139) в com.icesoft.faces.webapp.http.core.JsfLifecycleExecutor.apply (JsfLifecycleExecutor.java:17) на com.icesoft.faces.context.View $ 2 $ 1.respond (View.java:47) на com.icesoft.faces.webapp.http.servlet.ServletRequestResponse.respondWith (ServletRequestResponse.java:197) в com.icesoft.faces.webapp.http.servlet.ThreadBlockingAdaptingServlet $ ThreadBlockingRequestResponse.respondWith (ThreadBlockingAdaptingServlet.java:36) на com.icesoft.faces.context.View $ 2.serve (View.java:72) на com.icesoft.faces.context.View.servePage (View.java:133) на com.icesoft.faces.webapp.http.core.SingleViewServer.service (SingleViewServer.java:52) на com.icesoft.faces.webapp.http.common.ServerProxy.service (ServerProxy.java:11) на com.icesoft.faces.webapp.http.servlet.MainSessionBoundServlet $ 4.service (MainSessionBoundServlet.java:114) на com.icesoft.faces.webapp.http.common.standard.PathDispatcherServer.service (PathDispatcherServer.java:24) на com.icesoft.faces.webapp.http.servlet.MainSessionBoundServlet.service (MainSessionBoundServlet.java:160) на com.icesoft.faces.webapp.http.servlet.SessionDispatcher $ 1.service (SessionDispatcher.java:42) на com.icesoft.faces.webapp.http.servlet.ThreadBlockingAdaptingServlet.service (ThreadBlockingAdaptingServlet.java:19) на com.icesoft.faces.webapp.http.servlet.EnvironmentAdaptingServlet.service (EnvironmentAdaptingServlet.java:63) на com.icesoft.faces.webapp.http.servlet.SessionDispatcher.service (SessionDispatcher.java:62) на com.icesoft.faces.webapp.http.servlet.PathDispatcher.service (PathDispatcher.java:23) на com.icesoft.faces.webapp.http.servlet.MainServlet.service (MainServlet.java:153) на javax.servlet.http.HttpServlet.service (HttpServlet.java:717) в org.apache.catalina.core.ApplicationFilterChain.internalDoFilter (ApplicationFilterChain.java:290) в org.apache.catalina.core.ApplicationFilterChain.doFilter (ApplicationFilterChain.java:206) в org.apache.catalina.core.StandardWrapperValve.invoke (StandardWrapperValve.java:233) в org.apache.catalina.core.StandardContextValve.invoke (StandardContextValve.java:191) в org.apache.catalina.core.StandardHostValve.invoke (StandardHostValve.java:128) в org.apache.catalina.valves.ErrorReportValve.invoke (ErrorReportValve.java:102) в org.apache.catalina.core.StandardEngineValve.invoke (StandardEngineValve.java:109) в org.apache.catalina.connector.CoyoteAdapter.service (CoyoteAdapter.java:286) в org.apache.coyote.http11.Http11Processor.process (Http11Processor.java:845) в org.apache.coyote.http11.Http11Protocol $ Http11ConnectionHandler.process (Http11Protocol.java:583) в org.apache.tomcat.util.net.JIoEndpoint $ Worker.run (JIoEndpoint.java:447) at java.lang.Thread.run (Thread.java:619)

Я пытаюсь сделать что-то, чего не должен делать?

Есть ли лучший способ достичь этой цели?

Если потребуется дополнительная информация, я с радостью ее предоставлю.

Заранее спасибо, друзья.

UPDATE

Итак, я попытался изменить коллекцию seasonsList с List на List и отобразить какую-то другую разметку, например:

<p>
    <ice:panelGroup>
        <ice:panelSeries value="#{bean.seasonsList}" var="season">
                <ice:commandButton partialSubmit="true" style="background-color: #FFFFFF" value="#{season}" actionListener="#{Phase1EventBean.updateSeasons}"/>                                        
        </ice:panelSeries>
    </ice:panelGroup>
</p>

И изменение метода defineSeasonsList () на:

public void defineNationalSeasonsList()
{
    try
    {
        seasonsList = new ArrayList<String>();
        selectedSeasonsList = new ArrayList<String>();

        hibernate.openTransaction();

        for(UedaNationalDates season:hibernate.getList(new UedaNationalDates(), QueryFactory.getUedaNationalSeasons(hibernate.getHibSession())))
        { 
            nationalSeasonsList.add(season.getSeason());
        }       
    } 
    catch (Exception e)
    {
        System.out.println("Error defining nationalMeasurementPeriods: " + e.getMessage());
    }
    finally
    {
        hibernate.commitTransaction();
    }               
}

Это фактически отображает все кнопки, которые я хотел бы видеть, и правильно добавляет их в selectedSeasonsList в моем компоненте поддержки, когда я нажимаю на них, и удаляет их из него при повторном нажатии.

Однако в пользовательском интерфейсе каждая кнопка переключается, когда я нажимаю только одну кнопку. Например, когда я нажимаю на 2009-2010, это то, что я вижу:

All Selected

1 Ответ

2 голосов
/ 05 августа 2009
<ice:commandButton binding="#{season}"/>

Атрибут привязки должен быть привязан к свойству бина типа UIComponent . Он используется там, где вы хотите, чтобы среда давала вам ссылку на компонент в компоненте поддержки или для предоставления экземпляра из компонента поддержки). См. Раздел 3.1.5 спецификации JSF 1.2 для получения более подробной информации.


<ice:commandButton id="#{season.id}"
   partialSubmit="true"
   style="background-color: #FFFFFF"
   value="#{season.value}"
   actionListener="#{Phase1EventBean.updateSeasons}"/>

Атрибут id не может быть динамическим - JSF позаботится о том, чтобы обеспечить его уникальность на клиенте, используя clientId ( прочитайте это для более подробной информации ).


EDIT:

Однако в пользовательском интерфейсе каждая кнопка переключается, когда я нажимаю только одну кнопку.

Я предполагаю, что ice:panelSeries не хранит состояние компонента каждой строки, как это делают некоторые повторяющиеся элементы управления (например, dataTable). Помните, что есть только один экземпляр кнопки, даже если он закодирован / декодирован один раз на «строку».

Я никогда не использовал ICEfaces, но я предлагаю привязку к bean-компонентам, подобным следующему:

public class Bean {

  private final List<SelectionBean> seasonsList = Arrays.asList(
      new SelectionBean("Spring"), new SelectionBean("Summer"),
      new SelectionBean("Autumn"), new SelectionBean("Winter"));

  public List<SelectionBean> getSeasonsList() { return seasonsList; }

  public static class SelectionBean {

    private String season;
    private boolean selected;

    public SelectionBean() {}
    public SelectionBean(String season) { this.season = season; }

    public String getSeason() { return season; }
    public void setSeason(String season) { this.season = season; }

    public String toggle() {
      System.out.println("toggle " + season);
      selected = !selected;
      return null;
    }

    public String getStyle() {
      return selected ? "background-color: yellow" : "background-color: blue";
    }
  }
}

Я сократил логику до минимума, но, надеюсь, вы получите, как изменить логику, чтобы вернуть поддержку гибернации. Ваш компонент станет примерно таким:

<ice:panelSeries value="#{bean.seasonsList}" var="item">
  <ice:commandButton partialSubmit="true"
     style="#{item.style}"
     value="#{item.season}"
     action="#{item.toggle}"/>                                        
</ice:panelSeries>

Таким образом, для каждого элемента в вашем списке вся привязка возвращается к одному состоянию (экземпляру SelectionBean), и вы не пытаетесь сохранить какое-либо не декларативное состояние на самом компоненте.

Я стараюсь использовать action сверх actionListener, когда могу - это не позволяет вещам JSF исключать POJO.

...