3
votes

I am attempting to create a dialog that will serve the purpose of both creating objects and updating them. So if I happen to click the 'new' button I will be presented a dialog containing empty fields to be filled or if I click on an edit button for an entry, that entry's data will presented in the dialog for update.

Following the example in the primefaces showcase for version 5.2, I can present the data in a read only outputText form, however when I change it to an inputText, the field remains empty. The following code is an example of what I have:

<h:form id="form">    
  <p:dataGrid id="guestList" var="guest" value="${guestList.guests}" columns="3" paginator="true" rows="20">
    <f:facet name="header">
      Guest List
    </f:facet>

    <p:panel>
      <h:outputText value="${guest.name}" />
      <br />
      <h:outputText value="${guest.street}" />
      <br />
      <h:outputText rendered="#{guest.street2.length() gt 0}"
        value="${guest.street2}" />
      <h:panelGroup rendered="#{guest.street2.length() gt 0}">
        <br />
      </h:panelGroup>
      <h:outputText value="${guest.city}, " />
      <h:outputText value="${guest.state} " />
      <h:outputText value="${guest.zipCode}" />
      <p:commandButton update="@form:newGuestDetail" oncomplete="PF('newGuestDialog').show()" icon="ui-icon-edit" styleClass="ui-btn-inline">
        <h:outputText styleClass="ui-icon ui-icon-edit" style="margin:0 auto;" />
        <f:setPropertyActionListener value="#{guest}" target="#{guestList.selectedGuest}" />
      </p:commandButton>
    </p:panel>
  </p:dataGrid>

  <p:dialog header="#{guestList.hasSelected() ? 'Edit Guest' : 'New Guest'}" widgetVar="newGuestDialog" modal="true" showEffect="fade" hideEffect="fade">
    <p:outputPanel id="newGuestDetail">
      <h:outputText value="'#{guestList.selectedGuest.name}'"/>
      <p:inputText id="guestName" value="#{guestList.hasSelected() ? '' : guestList.selectedGuest.name}" pt:placeholder="Name"/>
      <p:commandButton value="#{guestList.selectedGuest == null ? 'Create Guest' : 'Update Guest'}"/>
    </p:outputPanel>
  </p:dialog>
</h:form>

The hasSelected() method evaluates whether the selected guest is null or not, returning true if not null. The selectedGuest should be set when the commandButton is clicked so that an object is available for retrieval by the dialog, however, with tracers in the get/set for selectedGuest, I am not seeing the setter called with the above snippet. If I remove the inputText, then even though the hasSelected is still returning false, and thus the 'New Guest' is heading the dialog, the outputText is filled with a value.

I found this great post talking about the order of execution with respect to the action, action listener, etc., but don't think this quite my issue: Differences between action and actionListener.

So the ultimate question is why will my setter get called with the command button when I only have an outputText, but with an inputText, I never see it called in the log?

I appreciate the time and help anyone can provide.

1
value="#{guestList.hasSelected() ? '' : guestList.selectedGuest.name}" During edition the value will be empty because it is not connected to any field.Geinmachi
@Geinmachi, I don't think I'm following what you said. I do notice, however, the snippet you pointed out should have the results of the expression swapped, i.e., #{guestList.hasSelected() ? guestList.selectedGuest.name : ''}. The logic was originally reversed. Even with this change, the setPropertyActionListener isn't getting fired as it does without the inputText on the dialog.klog
I just pointed out that you have empty value in inputText. If it is empty then why does it exist? You won't be able to do anything with it anyway. If you type there something it won't be saved because value is not bound to any bean. Maybe this causes some error and stops further processing, check server logs if you have any errors, like javax.faces.component.UpdateModelException: javax.el.PropertyNotWritableException:Geinmachi
It sounds like you are saying that I should just have: value="#{guestList.selectedGuest.name}"? I have tried that, but that didn't seem to help. Currently I was thinking that I'd pass null to differentiate 'new' from 'edit', but that seems like a bad approach with what you are saying. As for the log, I see only tracers, my get is returning null, there are no exceptions. With my new value="..." this exception is thrown: javax.el.PropertyNotFoundException: /index.xhtml @78,103 value="#{guestList.selectedGuest.name}": Target Unreachable, 'null' returned null. Meaning the set didn't happen.klog
Just create an empty selectedGuest instance using new Guest() in add button's action method? How exactly did you otherwise intend to collect/convert/validate the submitted values?BalusC

1 Answers

5
votes

Even if we fix your problem, this construct

<p:inputText value="#{guestList.hasSelected() ? '' : guestList.selectedGuest.name}">

is not ever going to work. It needs to reference a model property, not an empty string.

You'd best just reuse the edit form and let the create button precreate an empty entity. This would simplify a lot in the view side. It'd be more easy if the entity has an @Id property which is only present when it's persisted in the database.

Here's a kickoff example:

<h:form id="entitiesForm">
    <p:dataTable id="entitiesTable" value="#{bean.entities}" var="entity">
        <p:column>#{entity.foo}</p:column>
        <p:column>#{entity.bar}</p:column>
        <p:column>
            <p:commandButton id="edit" value="Edit" 
                process="@this" action="#{bean.edit(entity)}"
                update=":entityDialog" oncomplete="PF('entityDialog').show()" />
            <p:commandButton id="delete" value="Delete" 
                process="@this" action="#{bean.delete(entity)}"
                update=":entitiesForm:entitiesTable" />
        </p:column>
    </p:dataTable>
    <p:commandButton id="add" value="Add" 
        process="@this" action="#{bean.add}" 
        update=":entityDialog" oncomplete="PF('entityDialog').show()" />
</h:form>

<p:dialog id="entityDialog" widgetVar="entityDialog" 
    header="#{empty bean.entity.id ? 'New' : 'Edit'} entity">
    <h:form id="entityForm">
        <p:inputText id="foo" value="#{bean.entity.foo}" />
        <p:inputText id="bar" value="#{bean.entity.bar}" />
        <p:commandButton id="save" value="#{empty bean.entity.id ? 'Create' : 'Update'} entity" 
            process="@form" action="#{bean.save}"
            update=":entitiesForm:entitiesTable" oncomplete="PF('entityDialog').hide()" />
    </h:form>
</p:dialog>

With this @ViewScoped bean:

private List<Entity> entities; // +getter
private Entity entity; // +getter

@EJB
private EntityService entityService;

@PostConstruct
public void load() {
    entities = entityService.list();
    entity = null;
}

public void add() {
    entity = new Entity();
}

public void edit(Entity entity) {
    this.entity = entity;
}

public void save() {
    entityService.save(entity); // if (id==null) em.persist() else em.merge()
    load();
}

public void delete(Entity entity) {
    entityService.delete(entity); // em.remove(em.find(type, id))
    load();
}

See also: