I am having a problem with custom components in facelets. The first time that the page is rendered, the attributes are set properly on the component class. When a form is submitted however, the attributes are not set.
Here is the class that I am using to test this.
public class TestEcho extends UIData
{
/** Logger. */
private static Log log = LogFactory.getLog(TestEcho.class);
private String msg;
public TestEcho()
{
log.debug("Constructor.");
}
public void encodeEnd(FacesContext context) throws IOException
{
ResponseWriter writer = context.getResponseWriter();
writer.startElement("div", this);
writer.writeText("The value of msg is '" + msg + "'.", null);
writer.endElement("div");
}
public void setMsg(String msg)
{
log.debug("Setting msg to '" + msg + "'.");
this.msg = msg;
}
}
The component is used in the .xhtml page like this.
<h:form>
<v:testEcho msg="hello" />
<h:commandButton action="#{PictureManager.trigger}" value="Click" />
</h:form>
When the page renders for the first time, the component renders the following html code.
<div>The value of msg is 'hello'.</div>
When the button is clicked, it renders this.
<div>The value of msg is 'null'.</div>
From the log, you can see that the component is constructed again, but the attribute is not set.
13:23:42,955 DEBUG [TestEcho] Constructor.
13:23:42,955 DEBUG [TestEcho] Setting msg to 'hello'.
----- Button was pressed here -----
13:25:48,988 DEBUG [TestEcho] Constructor.
13:25:49,144 DEBUG [PictureManager] Button pressed.
From what I understand, facelets does all the wiring of attributes to components so I don't need a tag class, but I don't understand why the attribute would be set correctly the first time, but not the second time.
You must save your state by overriding the saveState and restoreState methods.
So, saveState must return a Serializable object (e.g. a JavaBean or Object[] array) containing the value in msg and whatever is returned by super.saveState. This object will be provided to restoreState where the method must restore msg from the object and pass the parent state to super.restoreState.
McDowell's answer did it. Just for completeness, here's the two methods I added.
public Object saveState(FacesContext context)
{
Object[] rtrn = new Object[2];
rtrn[0] = super.saveState(context);
rtrn[1] = msg;
return rtrn;
}
public void restoreState(FacesContext context, Object state)
{
Object[] values = (Object[]) state;
super.restoreState(context, values[0]);
msg = (String) values[1];
}
Related
I'm actually writing a java code in the setupRender() method. Depending of a value provided by the server side, i would like to display an Alert dialog box to the user. By clicking on ok, the application should be closed.
I have not already found how to display an Alert dialog box with tapestry. Do somebody know how to procedd?
Thanks
It's not quite clear to me what you are trying to achieve, but perhaps the following two suggestions are useful.
Suggestion 1 - Display a message using AlertManager
In the page class, inject AlertManager and add the message to it.
public class YourPage {
#Inject
AlertManager alertManager;
Object setupRender() {
// ...
alertManager.alert(Duration.UNTIL_DISMISSED, Severity.INFO, "Love Tapestry");
}
}
Then use the <t:alerts/> component in the page template file to have the message displayed.
Note: The user may dismiss the message, that is, make it disappear. But it doesn't 'close the application' (whatever it is that you mean by that).
Suggestion 2 - Redirect to another page
The setupRender method can return different things. For example, it could return another page class, causing a redirect to that page. On that page, you could have the messages displayed and the session subsequently invalidated (if that's what you meant by 'application should be closed'.
public class YourPage {
Object setupRender() {
// ...
return AnotherPage.class;
}
}
public class AnotherPage {
#Inject
Request request;
void afterRender() {
Session session = request.getSession(false);
session.invalidate();
}
}
See the Tapestry docs for details about what setupRender() can return.
Suggestion 3 - Use JavaScript to display Alert and trigger Component Event
This approach uses JavaScript to display an Alert and subsequently trigger a component event via ajax. The event handler takes care of invalidating the session.
Note: Closing the current browser windows/tab with JavaScript isn't as easy as it used to be. See this Stackoverflow question for details.
YourPage.java
public class YourPage {
boolean someCondition;
void setupRender() {
someCondition = true;
}
#Inject
private JavaScriptSupport javaScriptSupport;
#Inject
ComponentResources resources;
public static final String EVENT = "logout";
void afterRender() {
if (someCondition) {
Link link = resources.createEventLink(EVENT);
JSONObject config = new JSONObject(
"msg", "See ya.",
"link", link.toAbsoluteURI()
);
javaScriptSupport.require("LogoutAndCloseWindow").with(config);
}
}
#Inject Request request;
#OnEvent(value = EVENT)
void logout() {
Session session = request.getSession(false);
if (session != null) session.invalidate();
}
}
YourPage.tml
<!DOCTYPE html>
<html
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd"
xmlns:p="tapestry:parameter">
<h1>Hit the Road Jack</h1>
</html>
LogoutAndCloseWindow.js
define(["jquery"], function($) {
return function(config) {
alert(config.msg);
$.ajax({
type: "GET",
url: config.link
});
window.close(); // Legacy. Doesn't work in current browsers.
// See https://stackoverflow.com/questions/2076299/how-to-close-current-tab-in-a-browser-window
}
})
I have wicket component with onClick event where I'd like to run javascript code which:
reloads the page
after page has been reloaded, scroll down to the markupId which was clicked
I have to pass as parameter the "markupId" value from wicket to javascript to find out to which position should I scroll down
WicketComponent.java
MyPanel div = new MyPanel("div");
div.add(new AjaxEventBehavior("click") {
#Override
protected void onEvent(AjaxRequestTarget target) {
// some requests...
String markupId = div.getMarkupId();
target.appendJavaScript("window.location.reload();");
target.appendJavaScript(jsReload(markupId));
}
div.add(AttributeModifier.replace("onclick", "clicked('" + div.getMarkupId() + "');"));
#Override
public void renderHead(IHeaderResponse response) {
super.renderHead(response);
response.render(JavaScriptReferenceHeaderItem.forReference(new JavaScriptResourceReference(this.getClass(), "script.js")));
}
WicketComponent.html
<div wicket:id="div" onclick="clicked('markupId');">Text</div>
script.js
function clicked(markupId) {
window.location.reload();
}
document.addEventListener("DOMContentLoaded", function (event) {
let elementOffset = $("#{markupId}").offset().top; // how to pass here markupId parameter from wicket ?
let windowOffset = $(window).scrollTop();
window.scrollTo(0, elementOffset- windowOffset);
});
how to pass parameter "markupId" in javascript file which was attached in renderHead() or may be there is another solution for this ? I'll appreciate any help. Thanks!
you should solve your problem using location hash as described here:
Can we have code after location.reload(true)?
for the hash value use a fixed markup id for your component, something like:
div.setMarkupId("myMarkupId");
div.add(new AjaxEventBehavior("click") {
#Override
protected void onEvent(AjaxRequestTarget target) {
// some requests...
String markupId = div.getMarkupId();
target.appendJavaScript("window.location.hash = 'myMarkupId'");
target.appendJavaScript("window.location.reload();");
//that's it! no other js is needed
}
}
I haven't tried it but after page reloading it should scroll down to your component.
Is there any solution for build a Feedback Panel that contains inside messages with Link? I try to use this:
StringBuilder stringBuilder= new StringBuilder(512);
stringBuilder.append("<a href=\"");
stringBuilder.append(Utils.getAbsoluteUrl(EditBookingSourcePage.class, new PageParameters())+"");
stringBuilder.append("\">Clicca Qui!</a>");
feedbackErrorPanel.info(stringBuilder);
public static <C extends Page> String getAbsoluteUrl(final Class<C> pageClass, final PageParameters parameters) {
CharSequence resetUrl = RequestCycle.get().urlFor(pageClass, parameters);
String abs = RequestUtils.toAbsolutePath("/", resetUrl.toString());
final Url url = Url.parse(abs);
return RequestCycle.get().getUrlRenderer().renderFullUrl(url);
}
but doesn't work.
You will need to call feedbackPanel.setEscapeModelStrings(false).
Without this Wicket will escape all HTML characters to prevent XSS attack.
As #martin-g mentioned first you should escape the html tag. feedbackPanel.setEscapeModelStrings(false)
As you mentioned you did. I believe url construct issue . So whatever you have done on Top of that Try below step. I have tried below solution it worked for me .
//since you are not passing any parameter we can send null
String absoluteUrl = RequestCycle.get().getUrlRenderer().renderFullUrl(
Url.parse(urlFor(EditBookingSourcePage.class, null).toString()));
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("<a href=");
stringBuilder.append(absoluteUrl);
stringBuilder.append("\">Clicca Qui!</a>");
feedbackErrorPanel.info(stringBuilder);
In WicketApplication page
mountPage("setting/booking-source", EditBookingSourcePage.class);
Another apporach is to:
1, extend org.apache.wicket.markup.html.panel.FeedbackPanel
2, extend org.apache.wicket.feedback.FeedbackMessage and store data
3, Create MyMessagePanel with its own markup, model object
4, override newMessageDisplayComponent method in extended FeedbackPanel
#Override
protected Component newMessageDisplayComponent(String id, FeedbackMessage message) {
//message with its own markup
if (message.getClass().equals(MyMessage.class)) {
MyMessage msg = (MyMessage) message;
return new MyMessagePanel(id, Model.of(new MyMessagePanelModelObject(msg.getModelObject()))); //my message markup
}
return super.newMessageDisplayComponent(id, message); //other messages
}
I am trying to highlight an p:inputText (used to hold an email address) if this value doesn't match the value which came from the database. Here is my java code :
private UIInput gUiInputemailAdd; //global variable
public void validateCredentials(ComponentSystemEvent event) {
UIComponent components = event.getComponent();
UIInput uiInputemailAdd = (UIInput) components.findComponent("emailAdd");//ID of textbox
gUiInputemailAdd = uiInputemailAdd;
// uiInputemailAdd.setValid(false);//works fine here(it highlights this particular textBox)
}
public boolean forgotPasswordValidator(UserDetailTO userDetailTO)
throws MessagingException {
if (!gEmailAddress.equalsIgnoreCase(userDetailTO.getEmailAddress())) {
System.out.println(gUiInputemailAdd.getValue().toString()); //works fine and print value
gUiInputemailAdd.setValid(false);//doesn't work here
}
}
Xhtml code:-
<p:outputLabel for="emailAdd" value="EmilAddresss" />
<p:inputText value="#{loginTo.emailAddress}" id="emailAdd"/>
Please help me on this I have been stuck here for so long.
I would like to dynamically change the text displayed for a HTML anchor tag. So, for example if I have the following in my markup -
<a class="point" style="font-family:courier" wicket:id="link">[+]</a>
I want to change the '[+]' to something else. Currently the code fragment looks like this:
equipmentFamilyName.add(new Link<String>("link") {
#Override
protected void onComponentTag(ComponentTag tag) {
String id = "link" + equipmentFamilyName.getModelObject();
tag.put("onclick", "toggle('" + collapsibleId + "','" + id + "')");
tag.put("id", id);
}
#Override
public void onClick() {
}
});
Which just adds various attributes. I tried using a model associated with the Link object like this
IModel<String> linkModel = new Model<String>("-");
equipmentFamilyName.add(new Link<String>("link", linkModel) {
...
But that had no effect on the displayed text i.e. I still get '[+]' shown on my web page.
Any suggestions or code examples clarifying how to do this would be much appreciated.
Edit: Following the pointers in the comments, I added a method to override onComponentTagBody(). I now have a solution to this for our current version of Wicket (1.4.17).
#Override
protected void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag) {
replaceComponentTagBody(markupStream, openTag, "[-]");
}
If you use Wicket 1.5 then this is quite easy: link.setBody(IModel).
The model's object will be used as link's body.