Primefaces p: inplace: Jak bardziej elegancko propagować wyrażenie EL dla scalenia jednostki

Chciałbym udostępnić następujący potężny „hack”, aby dostać się do jednostki, której wartość została edytowana przy użyciu p: inplace.

Moje pytanie dotyczy tego, jak osiągnąć to bardziej elegancko, wykorzystując inne istniejące atrybuty p: w miejscu lub w inny sposób.

Ta sztuczka została zaadaptowana z sugestii BalusC na temat Stackoverflow:

JSF (i PrimeFaces) Jak przekazać parametr do metody w ManagedBean

Pytanie polega na uzyskaniu obiektu Employee, który ma edytowane p: inplace # {emp.firstName}:

<h:outputText value="First name:"/>
<p:inplace id="firstname" editor="true">
 <p:ajax event="save" onsuccess="#{employeeController.saveName()}"/>
 <p:inputText id="firstName" value="#{emp.firstName}"  
              required="true" label="text"
              valueChangeListener="#{employeeController.firstNameChanged}">
 <p:ajax event="valueChange" listener="#{employeeController.onValueChangedStart}"/>
 </p:inputText>
</p:inplace>

Sugestia BalusC brzmi:

Zakładając, że rzeczywiście znajdujesz się w powtarzającym się komponencie, gdzie # {emp} został ujawniony przez jego atrybut var, możesz po prostu pobrać go z zakresu EL wewnątrz detektora zmiany wartości w następujący sposób:

FacesContext context = FacesContext.getCurrentInstance();
Employee employee = context.getApplication().evaluateExpressionGet(context, "#{emp}", Employee.class);
Long id = employee.getId();

Problem polega na tym, że wyrażenie EL (w tym przypadku # {emp}) jest zakodowane na stałe, więc nie jest to wielokrotne użycie.

Używam teraz identyfikatora p: umieszczam z powodzeniem w propagacji ciągu, który może być analizowany jako wyrażenie EL dla encji w słuchaczu, jednak ponieważ nie można użyć kropki „.” w id String muszę tłumaczyć co '.' jako hiphen '-', a następnie przekonwertuj to na poprawne wyrażenie EL w detektorze. W ten sposób mogę propagować bardzo skomplikowane wyrażenia EL, a następnie wiem, która jednostka ma zostać scalona z bazą danych (lub jakąkolwiek operacją trwałości jest wymagana).

public static String policy_convert_id_to_el_expression(String $id) {
    if ($id == null) {
        throw new IllegalArgumentException("null String $id");
    }
    return $id.replaceAll("-", ".");
}

Mam rodzinę jednostek o głębokiej wartości, które zawijają określone płytkie wartości, takie jak BooleanValue, FloatQuantity itp., Każda ze specjalnie wpisaną metodą getValue () i ogólną przejściową metodą getObject () oraz przenoszącą inne trwałe metadane o zawiniętej płytkiej wartości (takie jak jednostki, autor itp.).

Mam fasolę bazową ValueManager z:

public void onInplaceSaveEvent(AjaxBehaviorEvent ae) {
    UIComponent component = ae.getComponent();
    String id = component.getId();
    String $el = policy_convert_id_to_el_expression(id);
    FacesContext context = FacesContext.getCurrentInstance();
    Value v = context.getApplication().evaluateExpressionGet(context, "#{" + $el + "}", Value.class);
..
// merge Value entity to database (catches change to shallow wrapped value) 
// and/or perform other persistence operations such as metadata updates
..

}

To jest teraz strategia, która może być ponownie wykorzystana.

Na przykład, mam obiekt OfficeBuilding z List getFloors (), a każdy BuildingFloor ma deep FloatQuantity getArea (), gdzie FloatQuantity rozszerza wartość.

W dataTable I iteruj z var = floor ponad wartość = # {officeBuilding.floors} i edytuj w miejscu płytką wartość tego obszaru:

<p:inplace 
 id="floor-area" 
 editor="true" 
 emptyLabel="UNDEF"
>
 <p:ajax 
  event="save" 
  update="@form" 
  listener="#{valueManager.onInplaceSaveEvent}"
  process="@this floor-area-value"
 />
 <p:inputText 
  id="floor-area-value" 
  value="#{floor.area.value}" 
 />
</p:inplace>

Może to być enkapsulowane jako wysoce nadające się do ponownego użycia i wygodne zasoby komponentu kompozytowego / util / inplaceValue.xhtml w ten sposób:

<cc:interface>
  <cc:attribute name="value" type="com.example.entity.value.Value" required="true"/>
  <cc:attribute name="elid" required="true"/>
</cc:interface>

<cc:implementation>
    <p:inplace 
        id="#{cc.attrs.elid}" 
        editor="true" 
        emptyLabel="UNDEF"
        >
        <p:ajax 
            event="save" 
            update="@form" 
            listener="#{valueManager.onInplaceSaveEvent}"
            process="@this #{cc.attrs.elid}-value"
            />
        <p:inputText 
            id="#{cc.attrs.elid}-value" 
            value="#{cc.attrs.value.value}"                
            />
   </p:inplace>
</cc:implementation>

Używając powyższego przykładu, nazywa się to:

<util:inplaceValue elid="floor-area" value=#{floor.area}/>

Trzeba tylko pamiętać, aby zachować ostrożność przy określaniu atrybutu elid, który występuje we wszystkich wystąpieniach „.” w wyrażeniu EL są tłumaczone na hiphen „-”, ale działają jak urok dla szerokiej gamy złożonych przypadków i oszczędzają ogromne ilości kodowania. Jest dość elegancki, ale wciąż jest hackiem.

P: W jaki sposób można lepiej propagować do słuchacza wyrażenie EL potrzebne do uzyskania dostępu do obiektu, którego wartość została edytowana?

Ja teżzrobiłem propozycję funkcji Primefaces dla nowego atrybutu dla p: wstaw specjalnie dla tego celu.

questionAnswers(0)

yourAnswerToTheQuestion