Rellenar p:selectOneMenu basada en otra p:selectOneMenu en cada fila de una p:dataTable

Tengo un <p:dataTable> con carga diferida. En dos de las columnas, hay una <p:selectOneMenu> en cada uno de ellos.

La primera columna contiene una lista de países y el segundo contiene una lista de los estados a partir de una base de datos.

Quiero el segundo menú (la que contiene una lista de los estados) para mostrar sólo aquellos estados en cada fila de la tabla de datos que corresponden al país en el primer menú en cada fila de la tabla de datos.

Durante el modo de edición, cuando un país en su menú se cambia, los estados correspondientes a ese país deben llenarse en su menú, en el que la fila actual.

Cómo cargar listas de estados que corresponden a sus países en cada fila de la tabla de datos?


Estas dos columnas en la tabla de datos está a la izquierda incompleto, ya que no tengo una idea precisa acerca de cómo lograr esto.

<p:column>
    <p:cellEditor>
        <f:facet name="output">
            <h:outputText value="#{row.state.country.countryName}"/>
        </f:facet>

        <f:facet name="input">
            <p:selectOneMenu value="#{row.state.country}">
                <f:selectItems var="country"
                               value="#{cityBean.selectedCountries}"
                               itemLabel="#{country.countryName}"
                               itemValue="#{country}"/>

                <p:ajax update="states" listener="#{cityBean.getStates}"/>
            </p:selectOneMenu>
        </f:facet>
    </p:cellEditor>
</p:column>

<p:column>
    <p:cellEditor>
        <f:facet name="output">
            <h:outputText value="#{row.state.stateName}"/>
        </f:facet>

        <f:facet name="input">
            <p:selectOneMenu id="states">

                <f:selectItems var="state"
                               value="#{cityBean.selectedStates}"
                               itemLabel="#{state.stateName}"
                               itemValue="#{state}"/>
            </p:selectOneMenu>
        </f:facet>
    </p:cellEditor>
</p:column>

cityBean.selectedCountries recupera todos los países en los que es necesaria, pero cityBean.selectedStates también recupera todos los estados de la base de datos que es innecesario y debe ser modificado para recuperar únicamente aquellos estados que corresponden a su país en otro menú.

¿Cómo puedo continuar a partir de aquí?

Aquí es donde usted puede tomar ventaja de su clase auxiliar. Su countryId no se encuentra en su bean gestionado directamente, pero en su clase auxiliar (recuerde que la lista se utiliza para generar la tabla está compuesta por este auxiliares). Entonces usted tiene el ajax de escucha de eventos (esto va en el MB), que recibirá la fila que se ha cambiado. El MB sólo se necesita auxiliar de objeto que fue cambiado (su countryId) y las cargas de su estado actual lista que va EN el auxiliar.

OriginalEl autor Tiny | 2013-07-12

3 Kommentare

  1. 12

    Mientras que su solución inicial funciona, de hecho, es ineficiente. Este enfoque básicamente requiere la totalidad de su objeto gráfico de la Country y State tablas (incluso con referencias circulares) para cargar completamente en Java de la memoria por JSF vista o de la sesión a pesar de que cuando usted utiliza simultáneamente únicamente, por ejemplo, de 5 de 150 países (y por lo tanto teóricamente 5 estado de las listas habría sido suficiente en vez de 150 estado de las listas).

    No tengo visión de conjunto de sus requisitos técnicos y funcionales. Tal vez en realidad estás utilizando simultáneamente todos esos 150 países. Tal vez haya muchas páginas donde todos (al menos, «muchos») de los países y los estados son necesarios. Tal vez usted ha estado del arte de hardware de servidor con un montón de memoria, por lo que todos los países y los estados sin esfuerzo puede ser duplicada sobre todos los JSF y vistas HTTP sesiones en la memoria.

    Si ese no es el caso, entonces sería beneficioso para no ansiosamente recuperar el estado de la lista de cada país (es decir, @OneToMany(fetch=LAZY) debe ser utilizado en Country#states y State#cities). Dado que el país y el estado en listas (probable) de datos estáticos que cambia muy pocas veces en un año, al menos lo suficiente para cambiar por implementar solamente, es mejor guardarlas en un ámbito de la aplicación de frijol que se reutiliza en todos los puntos de vista y sesiones en lugar de ser duplicado en cada JSF ver o sesión HTTP.

    Antes de continuar con la respuesta, me gustaría comentar que hay un error de lógica en el código. Dado el hecho de que usted está editando una lista de las ciudades y por lo tanto #{row} es esencialmente #{city}, es extraño que se hace referencia en el país, a través del estado como en #{city.state.country} en el menú desplegable valor de entrada. Mientras que puede funcionar para mostrar, que no trabajo para la edición y el ahorro de energía. Básicamente, usted está aquí, al cambiar el país por el estado en lugar de por la ciudad. El seleccionado estado recibiría el nuevo país en vez de la iterada de la ciudad. Este cambio se ve reflejado en todas las ciudades de este estado!

    Este hecho no es trivial si le gustaría continuar con este modelo de datos. Idealmente, usted desea separado (virtual) Country propiedad en City para que los cambios no afectan a la ciudad del State de la propiedad. Se podría hacer sólo @Transient para que JPA no lo consideran como una @Column como por defecto.

    @Transient //This is already saved via City#state#country.
    private Country country;
    
    public Country getCountry() {
        return (country == null && state != null) ? state.getCountry() : country;
    }
    
    public void setCountry(Country country) { 
        this.country = country;
    
        if (country == null) {
            state = null;
        }
    }

    Todos en todos, usted debe tener en última instancia este (irrelevante/default/evidentes atributos omitidos por razones de brevedad):

    <p:dataTable value="#{someViewScopedBean.cities}" var="city">
        ...
        <p:selectOneMenu id="country" value="#{city.country}">
            <f:selectItems value="#{applicationBean.countries}" />
            <p:ajax update="state" />
        </p:selectOneMenu>
        ...
        <p:selectOneMenu id="state" value="#{city.state}">
            <f:selectItems value="#{applicationBean.getStates(city.country)}" />
        </p:selectOneMenu>
        ...
    </p:dataTable>

    Con un #{applicationBean} algo como esto:

    @Named
    @ApplicationScoped
    public class ApplicationBean {
    
        private List<Country> countries;
        private Map<Country, List<State>> statesByCountry;
    
        @EJB
        private CountryService countryService;
    
        @EJB
        private StateService stateService;
    
        @PostConstruct
        public void init() {
            countries = countryService.list();
            statesByCountry = new HashMap<>();
        }
    
        public List<Country> getCountries() {
            return countries;
        }
    
        public List<State> getStates(Country country) {
            List<State> states = statesByCountry.get(country);
    
            if (states == null) {
                states = stateService.getByCountry(country);
                statesByCountry.put(country, states);
            }
    
            return states;
        }
    
    }

    (esta es la carga diferida enfoque; usted también puede recuperar inmediatamente todos ellos en @PostConstruct, acabo de ver que es lo mejor para usted)

    Ejemplo completo / explicación, que funcionó muy bien. Gracias.
    Como una nota del lado, yo por lo general no terminan bondades util período de gracia por ninguna razón precisa(s). Hay algo irritante que estoy haciendo? 🙂
    Eres bienvenido. No hay problema en cuanto a la recompensa. La más larga es la pregunta queda en el «destacados» de la lista, más posibilidades tiene en algunos upvotes en el final de modo que usted puede ganar el pasado reputación de vuelta.

    OriginalEl autor BalusC

  2. 0

    En este caso, es bastante simple. No hay necesidad de código. En el menú del estado, la siguiente,

    <f:selectItems var="state" value="#{cityManagedBean.selectedStates}" 
                   itemLabel="#{state.stateName}" itemValue="#{state}" 
                   itemLabelEscaped="true" rendered="true"/>

    es sólo que requiere ser modificado de la siguiente manera.

    <f:selectItems var="state" value="#{row.state.country.stateTableSet}" 
                   itemLabel="#{state.stateName}" itemValue="#{state}" 
                   itemLabelEscaped="true" rendered="true"/>

    Ya que, el objeto de entidad (row en este caso) contiene un objeto incrustado de state que a su vez, contiene un objeto de country que finalmente contiene una lista de los estados correspondiente para que country sólo tan obvia.

    Para que

    cityManagedBean.selectedStates

    un extra bean gestionado método es que ahora no es necesario en todos y necesitaba ser modificado como

    row.state.country.stateTableSet

    Donde stateTableSet es un Set<StateTable> que contiene una lista de objetos de la StateTable entidad.


    También, listener en <p:ajax> ya no es necesaria. Simplemente debería parecerse a la siguiente.

    <p:ajax update="cmbStateMenu"/>

    Está ahí sólo para el propósito de actualizar el estado del menú, cuando un elemento (el país) está seleccionada en el país menú.


    El código en cuestión ahora el siguiente aspecto.

    <p:column id="country" headerText="Country" resizable="true" sortBy="#{row.state.country.countryName}" filterBy="#{row.state.country.countryName}" filterMatchMode="contains" filterMaxLength="45">
        <p:cellEditor>
            <f:facet name="output">
                <h:outputLink value="Country.jsf">
                    <h:outputText value="#{row.state.country.countryName}"/>
                    <f:param name="id" value="#{row.state.country.countryId}"/>
                </h:outputLink>                                                                                
            </f:facet>
            <f:facet name="input">
                <p:selectOneMenu id="cmbCountryMenu" converter="#{countryConverter}" value="#{row.state.country}" label="Country" required="true" filter="true" filterMatchMode="contains" effect="fold" rendered="true" editable="false" style="width:100%;">
                    <f:selectItems var="country" value="#{cityManagedBean.selectedCountries}" itemLabel="#{country.countryName}" itemValue="#{country}" itemLabelEscaped="true" rendered="true"/>
                    <p:ajax update="cmbStateMenu"/>
                </p:selectOneMenu>
            </f:facet>
        </p:cellEditor>
    </p:column>
    
    <p:column id="state" headerText="State" resizable="false" sortBy="#{row.state.stateName}" filterBy="#{row.state.stateName}" filterMatchMode="contains" filterMaxLength="45">
        <p:cellEditor>
            <f:facet name="output">
                <h:outputLink value="State.jsf">
                    <h:outputText value="#{row.state.stateName}"/>
                    <f:param name="id" value="#{row.state.stateId}"/>
                </h:outputLink>
            </f:facet>
            <f:facet name="input">
                <p:selectOneMenu id="cmbStateMenu" converter="#{stateConverter}" value="#{row.state}" label="State" required="true" filter="true" filterMatchMode="contains" effect="fold" rendered="true" editable="false" style="width:100%;">
                    <f:selectItems var="state" value="#{row.state.country.stateTableSet}" itemLabel="#{state.stateName}" itemValue="#{state}" itemLabelEscaped="true" rendered="true"/>
                </p:selectOneMenu>
            </f:facet>
        </p:cellEditor>
    </p:column>

    Disculpa : no he mencionado en la pregunta que se me fue recuperar una lista de las ciudades.

    Mientras que esto puede funcionar, esto es, esencialmente, la duplicación de datos y por lo tanto de la memoria ineficientes.
    Se puede saber de otra manera precisa?
    JPA caché + con parámetros de captador en el ámbito de la aplicación de frijol (o si usted está utilizando CDI, un @Produces método; no seguro lo que la Primavera es equivalente a los que como yo no hago Primavera)

    OriginalEl autor Tiny

  3. 0

    Usted necesita para recuperar el estado de la lista basada en el País seleccionado. Usted necesita un valueChangeListener para que.

    Probar esto ( he puesto ambos seleccione una opción del menú en la misma columna por ahora)

    En su xhtml

    <p:column id="state" headerText="State" sortBy="#{row.state.stateName}" filterBy="#{row.state.stateName}">
    <p:cellEditor>  
            <f:facet name="output">  
            <f:facet name="output">
                        <h:outputLink value="State.jsf">
                            <h:outputText value="#{row.state.stateName}"/>
                            <f:param name="id" value="#{row.state.stateId}"/>
                        </h:outputLink>
                </f:facet>
            <f:facet name="input">  
            <h:selectOneMenu id="cmbCountryMenu" style="width:100px" value="#{row.state.country}" converterMessage="Error message." label="Country" valueChangeListener = "#{countryController.handleCountrySelect}" immediate="true"  converter="#{countryConverter}">  
                <f:selectItem itemLabel = "Select" itemValue="#{null}" /> 
                <f:selectItems var="country" value="#{cityManagedBean.selectedCountries}" itemLabel="#{country.countryName}" itemValue="#{country}"/>
                <p:ajax update="cmbStateMenu" />
            </h:selectOneMenu>
            <h:selectOneMenu style="width:100px" value="#{row.state}" valueChangeListener = "#{stateController.handleStateSelect}" immediate="false" id="cmbStateMenu"  converter = "#{stateConverter}">
    
                 <f:selectItems var="state" value="#{row.state.country.stateTableSet}" itemLabel="#{state.stateName}" itemValue="#{state}" itemLabelEscaped="true" rendered="true"/>
                <p:ajax update="@this" />
            </h:selectOneMenu>
            </f:facet>  
        </p:cellEditor> 

    en que el controlador de

      public void handleCountrySelect( ValueChangeEvent event )
    {
        setStates( ( ( Country) event.getNewValue() ) );
    }
    En este caso, la respuesta que he publicado hace la función esperada, pero la expresión EL como row.state.country.stateTableSet (java.util.Set fue cambiado a java.util.List mucho tiempo después de este post) debe ser omitido/eliminado en su totalidad, ya que es muy costoso. En caso de Ejb remoto, se requiere de una ansiosamente cargado relación o una costosa colección de fetch en JPA para que funcione correctamente, lo que es una cosa peor para implementar y deben no ser utilizado util y a menos de que se trata de absolutamente es necesario.
    Gracias por el esfuerzo.

    OriginalEl autor user1351585

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea