Modifying FormInjector context information in Tapestry 5 dynamically - java

My current problem regards updating context information dynamically in FormInjector, my previous question Updating a zone inside a form in Tapestry 5 probably contains useful background information.
I added the following in my template.
<div t:type="FormInjector" t:id="injector" t:context="item.id"/>
And the following in my component class.
#OnEvent(component = "injector")
Block loadItemFields(String id) {
item = itemRepository.find(id);
return itemFieldsBlock;
}
Everything is working fine, new form fields appear, but the search is always done with the same id. I would like to change the id with JavaScript before triggering the event, but I don't know how to achieve this.
If there is additional information required I am happy to supply it.

Using the context parameter to pass a dynamic value wouldn't be my first option. (The FormInjector component generates a URL to trigger the event handler, which then includes the context - however, this is done when the component renders, and is not meant to be dynamic.)
I'd get rid of the context parameter and find a different way to submit the value. One possibility would be to submit the form via AJAX and trigger the injection in the callback:
this.myFormElement.observe('change', this.onChange.bindAsEventListener(this));
...
onChange: function(event) {
this.myFormElement.form.request({
onSuccess: this.afterFormSubmitted.bind(this)
});
},
afterFormSubmitted: function() {
this.formInjector.trigger();
}
That way, the value of the form element has been set on the server side when you trigger the form injection, and you can use it in your injection event handler.

Related

How to stop wicket-9 from throwing StalePageException

Wicket throws StalePageException only if some Ajax action has been performed on a page before duplicating the page.
Example wicket project can be found at, along with steps to reproduce the exception.
https://github.com/rjngshn/java-wicket-testing/tree/main
Is there a way to ensure this exception is not thrown?
Thanks
Rajani
The default behavior provided in Wicket is too queue AJAX requests as they come in. Let’s say you have a button with a callback that does some work when clicked and updates the UI. This means that if the button is quickly clicked three times successively, the last two requests will be queued up and processed after the first request finishes.
A simple solution is to change the behavior of the AjaxChannel from queueing to active. This means that if any AJAX requests are received while there is an active(unfinished) request being processed, they will be ignored.
So how do we override Wicket’s default behavior in one spot and ensure all AjaxChannel‘s are modified? We use a custom AjaxRequestTarget.IListener.
public class ActiveAjaxListener implements AjaxRequestTarget.IListener {
private static final AjaxChannel ACTIVE_CHANNEL = new AjaxChannel(AjaxChannel.DEFAULT_NAME, AjaxChannel.Type.ACTIVE);
#Override
public void updateAjaxAttributes(AbstractDefaultAjaxBehavior behavior, AjaxRequestAttributes attributes) {
attributes.setChannel(ACTIVE_CHANNEL);
}
}
Our ActiveAjaxListener class will modify every AJAX behavior and make sure it uses the active channel. To register it, we simple insert this line into our WebApplication init() method:
getAjaxRequestTargetListeners().add(new ActiveAjaxListener());
(I've copied this explanation from https://www.coderdreams.com/wicket-quick-tip-4-change-default-ajaxchannel-to-active/)
Another way is to use a veil that prevents the double clicks via JS/CSS. More details about this approach could be found at JavaScript / Wicket : How to display an overlay which prevents user interaction

Form with field options loaded from DB and validation

The situation is the following:
I have a JSP page with a form.
This form contains various <select> tags with options loaded from DB.
I want to use validation with an XML file.
The problem is the following: if I use an XML file and there are some errors in the form fields, the struts framework doesn't pass through the class method I laid out, but it will directly return the input result. So what's the point? That in this way I can't load the options for the various <select> tags I mentioned above.
So I thought to do something like this:
<result name="input" type="chain">
<param name="actionName">Class_method</param>
</result>
but with this trick I lose all the error messages, i.e. hasFieldErrors() returns always false.
How can I solve that?
Many questions, all good though.
Conversion and validation errors forces the Workflow interceptor to trigger the INPUT result, and the workflow will execute the INPUT result instead of reaching the action method (execute() or whatever).
If you need to populate some static data, like selectboxes sources, that must be available also in case of INPUT result, you should put that loading in a prepare() method, and make your action implement the Preparable interface. This method is run by an Interceptor before the INPUT result is returned, as described in the official docs.
Avoid using the chain result. It is officially discouraged since many years.
If you want to prevent double submits (by pressing F5 after a page has been submitted and the result rendered), you can use the PRG pattern with the redirectAction result. This way, however, you'd encounter the same problem of the chain result: the messages (and the parameters) will be lost.
To preserve the error messages, action errors and field errors across the redirections, you can use a predefined interceptor called Message Store Interceptor, that you must include in your stack because the defaultStack doesn't include it. I've described how it works in this answer.
If you decide to use the Message Store along with PRG there are more considerations, too long to be written here, but that could be explained in the future, about preventing infinite recursion due to Field Error -> INPUT -> PRG -> Retrieve Field Error -> INPUT -> etc... that will be blocked by the browser near the 10th recursion... but that's another story.
One option:
public class Foo extends ActionSupport {
public string myAction() { return SUCCESS; }
public void validateMyAction() { // executed after XML validation
// other complex validation here if needed
if (hasErrors()) {
// repopulate form data from DB here
}
}
}
hasErrors() method comes from the ValidationAware interface which ActionSupport implements.
Another option is to do a redirect on input result and use the message store interceptor to keep action messages

Execute function from model in viewcontroller

In my App (Fusion Web) exist a ViewObject from Oracle DB.
I created the java classes and build a specific method (makeUniqueSearchByDate(String)) to process the data.
This method appears in "Data controls" that I can drag to the "view" and use as any other function. When I try to use it in a "bean" (instead of dragging directly):
public void setDate(ActionEvent actionEvent) {
ApplicationModule appMod =
Configuration.createRootApplicationModule("com.svr.model.AppModule", "AppModuleLocal");
ViewModelosByDataImpl fo = (ViewModelosByDataImpl) appMod.findViewObject("ViewModelosByData1");
String dateV = "07-01-2013";
fo.makeUniqueSearchByDate(dateV);
}
This code has no effect on the table. Can anyone see why?
Btw, the program does not throw any exception. Just does not work. The table remains the same. But if I use the button, automatically generated by "drag and drop" the function runs normally. I know I should study ADF, but unfortunately I have no time.
i think after you have exposed the method written at VO as Client interface, you need to create a method binding in pageDef file of you page. after creating the method binding, you need to access the method in bean through binding layer something like this :
OperationBinding op=((DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry()).getOperationBinding("Method Binding");
op.execute();
i think the method used by you to call VO method from bean is not right.
i think one more thing you need to do in your bean after calling the VO method is that you need to do refresh the table / perform PPR programatically :
AdfFacesContext adfFacesContext = AdfFacesContext.getCurrentInstance();
adfFacesContext.addPartialTarget(component binding for your table component);
you can try setting autosubmit to true for command button which invokes action event, and set partial trigger for table to component id of the command button.
can you post VO method code as well ?
does the method get called and data gets committed / updated when you execute it through bean ? is it only a table refresh issue ? do you see changes to data if you manually refresh the page ?

Using Stripes, what is the best pattern for Show/Update/etc Action Beans?

I have been wrestling with this problem for a while. I would like to use the same Stripes ActionBean for show and update actions. However, I have not been able to figure out how to do this in a clean way that allows reliable binding, validation, and verification of object ownership by the current user.
For example, lets say our action bean takes a postingId. The posting belongs to a user, which is logged in. We might have something like this:
#UrlBinding("/posting/{postingId}")
#RolesAllowed({ "USER" })
public class PostingActionBean extends BaseActionBean
Now, for the show action, we could define:
private int postingId; // assume the parameter in #UrlBinding above was renamed
private Posting posting;
And now use #After(stages = LifecycleStage.BindingAndValidation) to fetch the Posting. Our #After function can verify that the currently logged in user owns the posting. We must use #After, not #Before, because the postingId won't have been bound to the parameter before hand.
However, for an update function, you want to bind the Posting object to the Posting variable using #Before, not #After, so that the returned form entries get applied on top of the existing Posting object, instead of onto an empty stub.
A custom TypeConverter<T> would work well here, but because the session isn't available from the TypeConverter interface, its difficult to validate ownership of the object during binding.
The only solution I can see is to use two separate action beans, one for show, and one for update. If you do this however, the <stripes:form> tag and its downstream tags won't correctly populate the values of the form, because the beanclass or action tags must map back to the same ActionBean.
As far as I can see, the Stripes model only holds together when manipulating simple (none POJO) parameters. In any other case, you seem to run into a catch-22 of binding your object from your data store and overwriting it with updates sent from the client.
I've got to be missing something. What is the best practice from experienced Stripes users?
In my opinion, authorisation is orthogonal to object hydration. By this, I mean that you should separate the concerns of object hydration (in this case, using a postingId and turning it into a Posting) away from determining whether a user has authorisation to perform operations on that object (like show, update, delete, etc.,).
For object hydration, I use a TypeConverter<T>, and I hydrate the object without regard to the session user. Then inside my ActionBean I have a guard around the setter, thus...
public void setPosting(Posting posting) {
if (accessible(posting)) this.posting = posting;
}
where accessible(posting) looks something like this...
private boolean accessible(Posting posting) {
return authorisationChecker.isAuthorised(whoAmI(), posting);
}
Then your show() event method would look like this...
public Resolution show() {
if (posting == null) return NOT_FOUND;
return new ForwardResolution("/WEB-INF/jsp/posting.jsp");
}
Separately, when I use Stripes I often have multiple events (like "show", or "update") within the same Stripes ActionBean. For me it makes sense to group operations (verbs) around a related noun.
Using clean URLs, your ActionBean annotations would look like this...
#UrlBinding("/posting/{$event}/{posting}")
#RolesAllowed({ "USER" })
public class PostingActionBean extends BaseActionBean
...where {$event} is the name of your event method (i.e. "show" or "update"). Note that I am using {posting}, and not {postingId}.
For completeness, here is what your update() event method might look like...
public Resolution update() {
if (posting == null) throw new UnauthorisedAccessException();
postingService.saveOrUpdate(posting);
message("posting.save.confirmation");
return new RedirectResolution(PostingsAction.class);
}

Passing info message to a Wicket page while using PageParameters

Does Wicket somehow allow passing both of the following kinds of params in a PageParameters object? Apparently not?
accountId which is shown in the URL (/account/<ID>)
infoMessage parameter which is not shown in the (bookmarkable) URL
I'm currently using IndexedHybridUrlCodingStrategy for the page in question, and simply trying parameters "0" and "infoMessage" gives this exception:
WicketMessage: Not all parameters were encoded. Make sure all
parameter names are integers in consecutive order starting with zero.
Current parameter names are: [0, infoMessage]
If I change "infoMessage" parameter name into "1", it works, but yields an ugly URL (in this case something like /account/42/Tosite%20108207%20tallennettiin.5) which is not what I want.
Now, the obvious answer perhaps is that infoMessage shouldn't be in PageParameters. But thing is, I tried adding it as normal constructor parameter instead, like so:
public AccountPage(PageParameters parameters, String infoMessage) {
// ...
}
But this approach fails in one important use case. After deleting a persistent "Record" object related to the Account, the following does not load the AccountPage properly (the deleted record is still visible). This code is executed in onClick() of an AjaxFallbackLink.
setResponsePage(new AccountPage(AccountPage.pageParameters(account), message));
On the other hand, my original approach...
setResponsePage(AccountPage.class, AccountPage.pageParameters(account));
... works fine, as it somehow loads the AccountPage "more thoroughly", but, again, I don't know how to pass the infoMessage parameter cleanly.
(AccountPage.pageParameters() above is a simple static utility for creating appropriate PageParameters with "0" = account id. The AccountPage constructor always loads the account from persistence using the ID.)
Any ideas? Perhaps using AjaxFallbackLink partially causes the problem?
Using Wicket 1.4.
From what I see in your question, you try to render both a bookmarkable page and show a feedback message to the user (most probably in a FeedbackPanel), but you don't want that message to be part of the URL.
What you want to do is tell the Session that you have an informational message, and let the feedback panel handle the message.
#Override void onSubmit() {
... save object ...
getSession().info("Object ... has been saved");
setResponsePage(ObjectPage.class, new PageParameters("id="+object.getId()));
}
In this case you tell Wicket to temporarily store a message in the session, until it gets rendered by a feedback panel. This idiom is also known as "flash messages".
You can't use both PageParameters and another parameter as constructor arguments, because Wicket can't create your page instance with such a constructor when the page is requested. Wicket only knows how to instantiate pages with default constructors or pages with a PageParameters parameter.

Categories

Resources