How to implement pager change event in Tapestry's grid - java

How to implement pager change event in Tapestry's grid? I've read the documentation, but couldn't find the answer. So what I'm trying to do is, I want to reset the selectedItem to null everytime the selected page changing, and I noticed that actually setupRender() triggered, but I don't want to use it because it does a quite heavy process. There's no point to do this only for page changes. So is there any way to implement it specifically only for pager changes? Thank you. I'm using Tapestry 5.3.8 btw

If I understand your question correctly, you should implement your own GridPager and, for example, emit the event when a page is changing (you can then handle this event within page class). For example:
#Events({ PAGE_CHANGE_EVENT, InternalConstants.GRID_INPLACE_UPDATE + " (internal event)" })
public class CustomeGridPager
{
...
void onAction(int newPage)
{
currentPage = newPage;
this.resources.triggerEvent(PAGE_CHANGE_EVENT, new Object[] { newPage }, null);
}
}

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

DJ JWebBrowser: how to show but disable Address and Button bars?

I am using chrriis.dj.nativeswing.swtimpl.components.JWebBrowser in my swing application to open web page.
The page is going to show "Facebook Authentication" page and I want to prevent user from inputting some other URL other than I specify and also Forward and Back buttons should be visible but not has no affect.
So following functions are applicable for my goal
setButtonBarVisible(false);
setLocationBarVisible(false);
Once user completes the authentication I will handle the locationChanged event.
#Override
public void locationChanged(WebBrowserNavigationEvent arg0) {
System.out.println("locationChanged!");
....
}
}
I think what you want is a custom decorator. Check the demo application, under "JWebBrowser > Custom Decorator".
In your case, you could create a new decorator class, as an adapted copy of DefaultWebBrowserDecorator or a subclass with appropriate override.
You would also have to decide if this decorator is to be used only by one instance of the JWebBrowser or all instances (like child popups, etc.)

Why doesn't my Wicket Panel rerender after changing the default model?

When the page with the MessagePanel first renders, the message and the approve link render perfectly. When I click the approve link, all the business logic works as desired, the getNextMessage() method returns the appropriate object, but the message panel does not update on the page in the browser. That is, the message body Label does not update.
JPAEntityModel extends LoadableDetachableModel.
What am I missing? And how do I fix it?
public class MessagePanel(String id, IModel<Message> messageModel) extends Panel {
super(id, messageModel);
add(new Label("messageText", new PropertyModel<Message>(getModelObject(), Message.BODY_FIELD)));
add(new IndicatingAjaxFallbackLink<User>("approveLink", new JPAEntityModel<User> (getActiveUser())) {
#Override
public void onClick(AjaxRequestTarget target) {
Message nextMessage = getNextMessage();
MessagePanel.this.setDefaultModel(new JPAEntityModel<Message>(nextMessage));
target.add(MessagePanel.this);
}
});
setOutputMarkupId(true);
}
It is because you're not using the model properly.
This line takes the value of the panel's model object, as it is set during construction, and uses it to create the component model.
add(new Label("messageText", new PropertyModel<Message>(getModelObject(), Message.BODY_FIELD)));
To make matters worse, when you click the link, the panel is given a new model:
MessagePanel.this.setDefaultModel(new JPAEntityModel<Message>(nextMessage));
But this obviously doesn't affect the model of the label, as it is already set to refer to the original value.
So there are two things you need to change to make it work. First off, your label model should use your panel model directly:
new Model<Message>() {
#Override
public Message getObject() {
return MessagePanel.this.getModelObject().getMessage(); //or something similar
}
}
(Note: the code above isn't necessarily the best solution, but it is a working solution that demonstrates how models can be used dynamically.)
And ideally you shouldn't replace the model when you click the link, just change the model object. If you need a custom model class (JPAEntityModel), you shouldn't be accepting a pre-constructed model in the panel constructor anyway, just the first message object. The reason being the current implementation doesn't enforce the use of JPAEntityModel from the start, only after the first click of the link.
Can you try calling MessagePanel.this.modelChanged() before adding it to the target?
You must use call setOutputMarkupId(true) within you MessagePanel. The panel needs to have a markup identifier to be able to update the markup DOM in the browser.

Wicket AJAX + OnComponentTag

HI guys,
I wanted to add an AJAX Event to my Homepage, but it doesn't work! I figured out, that if I delete the onComponentTag function it works well. I have no clue why this happend, maybe you can help me!
Thats my Code:
final TextField<String> searchInput = new TextField<String>("searchInput", model) {
#Override
protected void onComponentTag(final ComponentTag tag) {
super.onComponentTag(tag);
tag.put("id", this.getId());
if (params.getString("search") != null) {
tag.put("value", params.getString("search"));
}
}
};
searchInput.add(new AjaxFormComponentUpdatingBehavior("onfocus") {
#Override
protected void onUpdate(final AjaxRequestTarget target) {
System.out.print("never saw that message :(");
searchInput.setDefaultModelObject("");
target.addComponent(searchInput);
}
});
Thx a lot for helping me!
CU
Firstly, you don't need to be overriding onComponentTag() at all. As seanizer states, if your really need to specify a markup ID yourself, use setMarkupId(id). You should understand why it is recommended that Wicket manages component IDs.
Secondly, the value attribute that you are adding is unnecessary - Wicket adds this automatically for this component. The value assigned is the value of the component's model object. See the source for TextField.onComponentTag().
Thirdly, again as seanizer states, components that are to be updated by ajax need to output their markup IDs - Wicket's ajax implementation uses the ID as the selector for the element. Additionally, all Wicket ajax behaviours that extend AbstractDefaultAjaxBehavior automatically set outputMarkupId(true) on the component they are bound to (see the source for AbstractDefaultAjaxBehavior.onBind()). This includes AjaxFormComponentUpdatingBehavior.
So:
String id = "searchInput";
final TextField<String> searchInput = new TextField<String>(id, model);
searchInput.setMarkupId(id);
searchInput.add(new AjaxFormComponentUpdatingBehavior("onfocus") {
#Override
protected void onUpdate(final AjaxRequestTarget target) {
System.out.print("never saw that message :(");
searchInput.setDefaultModelObject("");
target.setOutputMarkupId(true);
target.addComponent(searchInput);
}
});
Finally, I'd question what you're actually trying to achieve with this behaviour. I don't see any reason to round-trip this event to the server. Surely some client-side JS is more appropriate?
tag.put("id", this.getId());
is not the way to do it in wicket.
instead, use
component.setOutputMarkupId(true)
(either in your component constructor or in your behavior's bind() method) to make wicket write the id, and if you absolutely need to control what the id is (which is almost never the case) you can do
component.setMarkupId("myId")
also, you probably shouldn't assign the tag value yourself, use a model (model handling is extremely smart in wicket, read more about models). There are valid uses for onComponentTag, but they are way beyond what you are doing. Let wicket do what wicket does best and everything will be fine.
EDIT:
OK, some more clarification
have a look at the source code of AjaxFormComponentUpdatingBehavior, especially the part where the javascript event handler is generated.
protected final CharSequence getEventHandler()
{
return generateCallbackScript(
new AppendingStringBuffer("wicketAjaxPost('")
.append(getCallbackUrl(false)).append(
"', wicketSerialize(Wicket.$('"
+ getComponent().getMarkupId() + "'))"));
}
as you can see, wicket uses getMarkupId() to determine the actual id. The id you set using tag.put(id) is totally unknown to wicket and hence the behavior cannot work.
The standard thing to do is setOutputMarkupId(true). This is the only proper way to tell wicket to render the id (other than setOutputMarkupPlaceholder(true), which internally calls the former method). That way you make sure that the id wicket writes is the id wicket knows about. If this doesn't render the id, you are probably breaking some default behavior by overwriting onComponentTag.
Have a look at the source code of Component, especially at onComponentTag(), the method you are overriding:
protected void onComponentTag(final ComponentTag tag) {
// if(setOutputMarkupId(true) was set)
if (getFlag(FLAG_OUTPUT_MARKUP_ID)) {
// set id attribute
tag.put(MARKUP_ID_ATTR_NAME, getMarkupId());
}
}
[The comments are mine. BTW, this is the source of an ancient version, but I didn't find any current source online, and the functionality hasn't changed.]
Now if, as in your case, you want to set the component id manually, you must use
component.setMarkupId("myId")
and of course
setOutputMarkupId(true)
as well. If that doesn't work, go to the wicket JIRA site and file a bug. But I doubt it, this is standard functionality that works for thousands of users.

Modifying FormInjector context information in Tapestry 5 dynamically

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.

Categories

Resources