Wicket: Lazy loading DropDownChoice - java

One DropDownChoice list on my webapp takes very long time to create, because of getting options by some operations with LDAP connection and SQL connection. And because of that the whole page is loading much more than a couple of seconds - I'd say too much.
So what I want to achieve, is to use (best for me) the built-in Ajax functionality of Wicket to lazy load this dropdown, but I have some problems.
I know how to make regular DropDownChoice list, this simple example working great for me - link
I also know how to make lazy-loaded paragraph, from wicket-examples - link (Source Code -> LazyLoadingPage.html/LazyLoadingPage.java)
But putting it together throwing me exceptions and resulting Wicket's Internal error.
Here is how I try to do it:
in HTML:
<select wicket:id="lazy"></select>
in Java:
private String selected = "abc";
(...)
add(new AjaxLazyLoadPanel("lazy") {
#Override
public Component getLazyLoadComponent(String id) {
//simulating long time for simple list
try {
Thread.sleep(5000);
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
return new DropDownChoice<String>(
id, new PropertyModel<String>(this,"selected"),
Arrays.asList("abc","def"));
}
});
}
And I'm getting Internal Error from Wicket, with that in logs:
ERROR Unexpected error occurred
Component [content] (path = [0:lazy:content]) must be applied to a tag of type [select], not: '<div wicket:id="content">' (line 0, column 0)
MarkupStream: [markup = jar:file:/C:/Program%20Files/Apache%20Software%20Foundation/Tomcat%207.0/webapps/devservices/WEB-INF/lib/wicket-extensions-1.5.7.jar!/org/apache/wicket/extensions/ajax/markup/html/AjaxLazyLoadPanel.html
, index = 0, current = ''
and stacktrace.
I would really appreciate some help, what I'm doing wrong, or maybe some better code examples.

Thanks to bert, I'm putting here full solution, in case someone will use it in the future.
We need to create our own panel, because AjaxLazyLoadPanel can only change one panel to another.
Example of MyPanel.html:
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
<body>
<wicket:panel>
<select wicket:id="project"></select>
</wicket:panel>
</body>
</html>
and MyPanel.java :
public class MyPanel extends Panel {
private String selected = <what you want>;
private List<String> projectList <what you want>;
public MyPanel(String id) {
super(id);
add(new DropDownChoice<String>(
"project", new PropertyModel<String>(this, "selected"), projectsList));
}
}
On your main page html simply add this:
<span wicket:id="lazy2"></span>
and in main page java file:
add(new AjaxLazyLoadPanel("lazy") {
#Override
public Component getLazyLoadComponent(String id) {
return new MyPanel(id);
}
});
Hope it will help someone else too :-)

Related

Wicket - Set Model from another panel

I am quite new to Wicket. I am adding a model to a sub-panel(ChartPanel) from a main panel (MainPanel) on a button click.
MainPanel.java
On button click, I am re-adding the chartPanel after I change its model. Following is the code I am using in the buttonClick of the MainPanel. Here the onRenderAnnotations event is generated on some click in the UI.
#OnEvent
public void onRenderAnnotations(RenderAnnotationsEvent aEvent)
{
LOG.trace("clicked on the annotation");
renderChart( aEvent.getRequestHandler());
}
private void renderChart(IPartialPageRequestHandler aRequestHandler)
{
MultiValuedMap<String, Double> recommenderScoreMap = getLatestScores(aRequestHandler);
Map<String,String> curveData = new HashMap<String,String>();
LearningCurve learningCurve = new LearningCurve();
for (String recommenderName : recommenderScoreMap.keySet()) {
String data = recommenderScoreMap.get(recommenderName).stream().map(Object::toString)
.collect(Collectors.joining(", "));
curveData.put(recommenderName,data);
learningCurve.setCurveData(curveData);
learningCurve.setMaximumPointsToPlot(MAX_POINTS_TO_PLOT);
}
chartPanel.setDefaultModel(Model.of(learningCurve));
// to avoid the error, A partial update of the page is being rendered
try {
aRequestHandler.add(chartPanel);
}
catch (IllegalStateException e) {
LOG.warn("Not updating the chart. " + e.toString());
setResponsePage(getPage());
}
}
ChartPanel.java
After this in the chartPanel, I want to use the updated model to add component inside the chartpanel. What would be the best way to do that?
I want to do something like this in the class ChartPanel:
#Override
protected void onRender()
{
super.onModelChanged();
LearningCurve newLearningCurve = getModel().getObject();
requestTarget = ???
String js = createJavascript(newLearningCurve);
requestTarget.prependJavascript(js);
}
My question is, in the above code how to get the request target since it is not an ajax request neither do I get it in the arguments. Should I use some other function where I also get a requestTarget. But I want it to be called every time the model of ChartPanel is updated from anywhere.
Pardon my ignorance. I have been trying for a few days but I am still stuck. I tried to explain it enough but if any information is missing, please comment and I will add it right away.
Thanks.
You should override renderHead() instead:
#Override
public void renderHead(IHeaderResponse response)
{
super.renderHead(response);
response.render(OnLoadHeaderItem.forScript(
createJavascript(newLearningCurve)));
}
This way your chart will be shown correctly regardless whether it was added due to an AjaxRequest or simply when the page is rerendered.

JSP - List output

I currently have the following:
cartServlet.java
public class CartServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
CartBean cartBean = new CartBean();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int counter = 0;
while (request.getParameterMap().containsKey("id" + counter)){
String songID = request.getParameter("id" + counter);
cartBean.setCartInfo(songID);
++counter;
}
request.setAttribute("cart", cartBean);
RequestDispatcher rd = request.getRequestDispatcher("/cart.jsp");
rd.forward(request, response);
}
cart.jsp
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Shopping Cart</title>
</head>
<body>
"${cart.cartInfo}"
</body>
</html>
cartBean.java
public class CartBean implements Serializable {
private static final long serialVersionUID = 1L;
private List<String> cart;
public CartBean(){
cart = new ArrayList<String>();
}
public void setCartInfo(String cartItem) {
this.cart.add(cartItem);
}
public List<String> getCartInfo() {
return cart;
}
}
When I print "${cart.cartInfo}", my output is coming out like this:
"[381d3af3-c113-46c1-b9d0-2c46cf445e22}, 3913ac54-0c03-4025-8279-5cfad2fcab5f}, 50ed6861-f6e2-479b-865c-cbbbc5c27efd}, eb9b29d6-d93e-4cd8-8d7a-7fe26ff6c05d}]"
Is this the correct way the output should be printed out? I don't know why the additional } is appearing at the end of each item..
Also, should I be defining CartBean cartBean = new CartBean(); in cartServlet.java? If a user were to come back to this shopping cart page and select more items, would the new items be placed in a different bean to the one I was originally using?
Thanks for your help.
Is this the correct way the output should be printed out? I don't know
why the additional } is appearing at the end of each item..
I'm guessing that yes, what's being listed on your JSP is what's supposed to be listed. Now it may differ from what you want, which is another matter. What appears to be currently listed is the ID for a song. In this XML file, I see one of the IDs listed as for "The Sweetest Taboo" by Sade in this homework file: http://www.cse.unsw.edu.au/~cs9321/14s1/assignments/musicDb.xml.
You need to use the useBean tag in your JSP. The syntax is as follows:
<jsp:useBean id = "idName" class = "packageName.SpecificClass" scope = "desiredScope" />
Fill in id, class, and scope with the desired values. The most common value for scope seems to be session.
Then set the property:
<jsp:setProperty name = "idName" property = "*" />
For more information about setProperty (along with useBean), see: jsp:setproperty what does property="*" mean?.
Also, should I be defining CartBean cartBean = new CartBean(); in
cartServlet.java?
It's usually best to use the servlet for the bean, especially if there's some processing that's going on, like filling in lists. Avoid putting a lot of Java code in your JSPs. The JSP should be able to grab a list of products and place them on the page, but it should not be able to instantiate the list, populate it, massage it into the form you need, and then place the products.
If a user were to come back to this shopping cart
page and select more items, would the new items be placed in a
different bean to the one I was originally using?
Read this page: http://www.jguru.com/faq/view.jsp?EID=53309.
You need to put the statement of creating CartBean inside doPost() method as a local variable, otherwise each request will create a new Thread and these threads share the instance variable which means the data in CartBean will be corrupted by different users.

java.lang.IllegalArgumentException: Must be StyledEditorKit

We are trying to render both HTML and plain text using JTextPane. Basically the actual content is hosted on remote server, this content can contain some degree of HTML tags or none at all. In my JTextPane I have it defined as shown:
JTextPane jText = new JTextPane();
jText.setEditable(false);
jText.setContentType("text/html");
String content = "Please view article <a href=mydomain.com/content.txt>Link..</a>";
jText.setText(content);
And then using HyperlinkListener wants to be able to render the content when the link is clicked. I am doing so using the syntax below;
jText.addHyperlinkListener(new HyperlinkListener()
{
public void hyperlinkUpdate(final HyperlinkEvent he)
{
//Render the page
try{
setPage(he.getURL()); //Error on this line
}catch(Exception e){e.printStackTrace();}
}
});
Unfurtunately when we click on the link to render the content, we end up with the exception:
java.lang.IllegalArgumentException: Must be StyledEditorKit
at javax.swing.JTextPane.setEditorKit(JTextPane.java:474)
at javax.swing.JEditorPane.setContentType(JEditorPane.java:888)
at javax.swing.JEditorPane.getStream(JEditorPane.java:713)
at javax.swing.JEditorPane.setPage(JEditorPane.java:408)
This looks like when the content has no HTML tag available. Can someone help us resolve this issue so we can render both plain text and HTML.
Thanks in advance.
EDITED.
It sounds like what you're saying is that since you want to support both HTML and plain-text from your JTextPane as input, then not receiving a URL isn't really considered a problem. In that case, you should consider logging/eating the exception:
jText.addHyperlinkListener(new HyperlinkListener()
{
public void hyperlinkUpdate(final HyperlinkEvent he)
{
try {
//Render the page
setPage(he.getURL()); //Error on this line
} catch (IllegalArgumentException e) {
// either log the argument here, or just eat it and do nothing with it. logger.error() recommended
}
}
});

Wicket: Changing the text of an AjaxButton on submit

I'm a noob to Wicket and trying to change the text of a AjaxButton on submit. So the idea is that the for the first time the page loads, the user sees an AjaxButton labeled e.g. "1", after clicking the button, the label of the button changes to "2" and after the next click to "3" and so on...This can't be hard, but as I said, I'm a noobie when it comes to wicket. All help appreciated!
form.add(new AjaxButton("ajax-button", form)
{
#Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form)
{ //how to change Button label here?
}
}
The answer is simple: use a model.
//counter field declared in page class
private int counter;
...
form.add(new AjaxButton("ajax-button", new PropertyModel<String>(this,
"counter", form)) {
#Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
counter++;
target.addComponent(this);
}
});
This is probably the most important rule of Wicket: when you need something changing, use a model. This takes some time getting used to, especially if you have experience with more "traditional" frameworks, and haven't used Swing either.
N.b.: keeping the counter in your page class may not be a good idea, but the general idea is the same.
Additionally to biziclop's answer, here is a solution for text with changing parameter.
In your java code:
AjaxButton yourButton = new AjaxButton("btnId"){
//your button's implementation goes here
};
int yourVariable = 42;
Label yourLabel = new Label("labelId", new Model<String>() {
public String getObject() {
String text = MessageFormat.format(new Localizer().getString("IdForLocalizerInYourLocalizerFile", null), yourVariable);
return text;
}
})
yourButton.add(yourLabel);
In your html:
<a type="submit" wicket:id="btnId">
<span wicket:id="labelId">[This text will never be seen, will be replaced by "The var..."]</span>
</a>
Finally your localization file will contain a line like:
IdForLocalizerInYourLocalizerFile= The variable's value is {0}. It will be replaced whenever it changes and button component is added to target. Text will remain.

Problem changing language application

as I asked time ago in this question, I solved my problem using this method:
In loging.xhtm, for instance:
<f:view locale="#{languageDetails.locale}" >
<head>
.....
<f:loadBundle basename="messages.Messages" var="msg1"/>
.....
</h:form>
</body>
</f:view>
2.In java source code I also made some changes:
public class LanguageDetails {
private static String locale = Locale.getDefault().getDisplayLanguage();
public void setLocale(String locale1) {
this.locale = locale1;
}
public synchronized String getLocale() {
return locale;
}
public synchronized String changeLanguage() {
return "changed";
}
}
But now I'm trying to have the same option, not just in Login page, but in other pages.
Adding the same code in other pages, doesn't work, because function setLocale is not called. Any help?
Thanks in advance
I realized, it's really important to put
<f:view locale="#{languageDetails.locale}" >
....
</f:view>
Or in every single file, or just in top file. Later, put <h:selectOneMenu> where necessary, but having always in mind that you can not have all <h:form> , <a4j:form>... etc. you want, it makes things more complicated. I put this form tags just on top files, and now everything is ok.
Hope this could help somebody.

Categories

Resources