Panayiotis Karabassis
Panayiotis Karabassis

Reputation: 2288

Strange behavior with <f:ajax> inside <h:form> inside <ui:repeat>

I have an table of data presented by an ui:repeat. Because I want the user to be able to change the data on a per row basis, each row is enclosed in an h:form. Finally each h:form has a button with the f:ajax tag. I am getting wildly inconsistent behavior.

<ui:repeat value="#{importManager.items}" var="item" varStatus="status">
    <h:form>
        <tr>
            <td>
                <h:outputText value="#{status.index}"/>
            </td>
            <td>
                <h:inputText id="title" value="#{item.title}" styleClass="#{item.validTitle ? 'valid' : 'invalid'}"/>
            </td>
            <td>
                <h:inputText id="artist" value="#{item.artist}" styleClass="#{item.validArtist ? 'valid' : 'invalid'}"/>
            </td>
            <td>
                <h:commandButton value="#{importMessages.submit}">
                    <f:ajax execute="@form" render="@all" listener="#{importManager.publish(item)}"/>
                </h:commandButton>
            </td>                           
        </tr>
    </h:form>
</ui:repeat>

The above works but obviously is not cheap on bandwidth.

If I change the render="@all" to render="@form", Firebug shows the partial response being sent ok, but my browser (Firefox) mysteriously does not display it. So I am guessing it (the browser) does not find the element to update?

If I change execute="@form" to execute="@all" I get very strange behavior, namely the data gets lost, and the affected fields go blank.

The backing bean is quite simple:

    public void publish(final Item item)
    {
        Set<ConstraintViolation<Item>> violations = item.validate();

        if (violations.isEmpty())
        {
            temporaryRegistry.deleteItem(item);
            registry.storeItem(item);
        }
        else
        {
            // Display error messages
        }
    }

And the model:

@Entity
public class Item implements Cloneable
{
    @Id @GeneratedValue
    private long identifier;

    @NotNull(groups={Warning.class})
    @Length(min=1, max=80, groups={Warning.class})
    private String title;

    @NotNull(groups={Warning.class})
    @Length(min=1, max=80, groups={Warning.class})  
    private String artist;

    @NotNull(groups={Warning.class})
    @Length(min=1, max=10, groups={Warning.class})
    private String media;

    @NotNull(groups={Warning.class})
    @Length(min=1, max=5, groups={Warning.class})
    @Column(name = "_condition")
    private String condition;

// Setters and Getters

    public boolean isValidTitle()
    {
        final ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        final Validator validator = factory.getValidator();

        final Set<ConstraintViolation<Item>> violations = validator.validateProperty(this, "title", Warning.class);

        return violations.isEmpty();        
    }

    public boolean isValidCondition()
    {
        final ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        final Validator validator = factory.getValidator();

        final Set<ConstraintViolation<Item>> violations = validator.validateProperty(this, "condition", Warning.class);

        return violations.isEmpty();        
    }

    public boolean isValidArtist()
    {
        final ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        final Validator validator = factory.getValidator();

        final Set<ConstraintViolation<Item>> violations = validator.validateProperty(this, "artist", Warning.class);

        return violations.isEmpty();
    }

    @Override
    public boolean equals(final Object object)
    {   
        return (object instanceof Item) && (object != null) && (((Item) object).getIdentifier() == identifier);
    }

    @Override
    public int hashCode()
    {
        return Long.valueOf(identifier).hashCode();
    }

    public Set<ConstraintViolation<Item>> validate()
    {
        final ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        final Validator validator = factory.getValidator();

        return validator.validate(this, Warning.class);
    }
}

Can anyone explain this, and does anyone have a way to submit the form and the form only by ajax and display the result?

Upvotes: 0

Views: 961

Answers (1)

BalusC
BalusC

Reputation: 1108632

Your HTML ends up having a <form> around each <tr>. This is illegal HTML syntax and the browser behaviour is therefore unspecified.

You need to put the <h:form> around the <table>. If you need a form around a single "row", then you might want to redesign the single <table> to be multiple <table>s with fixed column widths or a group of <div>s.

Upvotes: 1

Related Questions