Creating master-detail table and dialog, how to reuse same dialog for create and edit
Asked Answered
E

1

3

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.

Erythritol answered 11/10, 2015 at 5:57 Comment(6)
value="#{guestList.hasSelected() ? '' : guestList.selectedGuest.name}" During edition the value will be empty because it is not connected to any field.Sanderson
@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.Erythritol
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:Sanderson
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.Erythritol
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?Heeley
I hadn't gotten that far yet, but I can't even get to the point of being able to modify the sample entries I have yet.Erythritol
H
5

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:

Heeley answered 11/10, 2015 at 14:50 Comment(3)
thank you for this example. I will give it a try and let you know how it goes. I noticed that your example doesn't use setPropertyActionListener in the command button like I was which I think has something to do my issue, although I still fell short with how I was trying to populate the form. I may have been able to populate, you have definitely shown that that would be all I'd been able to do.Erythritol
The <f:setPropertyActionListener> would work too, I'm just preferring the EL 2.2 feature of passing method arguments as it ends in more clear code. See also a.o. stackoverflow.com/questions/4994458/…Heeley
your example has helped quite a bit. There were several subtleties that your example included and I was missing, for example, some IDs for some of the elements. I had some trouble getting the update attribute to resolve, but that was due to some nesting that wasn't present in my provided code. I have quite a bit to learn still as I am just now cutting my teeth in the JSF stack. Thank you.Erythritol

© 2022 - 2024 — McMap. All rights reserved.