How do I model subpanels correctly using Wicket? - java

I have a panel (see BasePanel below) that has a list of items and an "action"-bar where buttons are placed. Clicking these buttons makes changes to the items in the list via models.
Now I want the same panel but with slightly different buttons (see CustomPanelA and CustomPanelB below). I have about three configurations for the buttons in the panel.
How can I model this? I thought to use wicket:child but since the buttons are inside another wicket component that did not work.
BasePanel.java
class BasePanel extends Panel {
public BasePanel(String id) {
super(id);
// note: I need this container for refreshing using AJAX
WebMarkupContainer outer = new WebMarkupContainer("outerContainer");
outer.setOutputMarkupId(true);
add(outer);
// ... create listview
outer.add(new ListView("items") { /* implementation of listview */ };
// This container is necessary to show/hide the buttons
WebMarkupContainer actionbar = new WebMarkupContainer("outerContainer");
actionbar.setOutputMarkupId(true);
// ... create default buttons
actionbar.add(new Link("add") { /* implementation of link */ );
}
}
BasePanel.html
<html>
<wicket:panel>
<div wicket:id="outerContainer">
<div wicket:id="actionbar" >
<a wicket:id="add">Add</a>
</div>
<div wicket:id="items">
<!-- ... markup for items is here -->
</div>
</div>
</wicket:panel>
</html>
CustomPanelA.java
class CustomPanelA extends BasePanel {
public CustomPanelA (String id) {
super(id);
// add additional buttons only
actionbar.add(new Link("actionA1") { /* implementation of link */ );
actionbar.add(new Link("actionA2") { /* implementation of link */ );
}
}
CustomPanelA.html
<html>
<wicket:extend>
<a wicket:id="actionA1">ActionA1</a>
<a wicket:id="actionA2">ActionA2</a>
</wicket:extend>
</html>
CustomPanelB.java
class CustomPanelB extends BasePanel {
public CustomPanelB (String id) {
super(id);
// add additional buttons only
actionbar.add(new Link("action_b1") { /* implementation of link */ );
actionbar.add(new Link("action_b2") { /* implementation of link */ );
}
}
CustomPanelB.html
<html>
<wicket:extend>
<a wicket:id="action_b1">Action b1</a>
<a wicket:id="action_b2">Action b2</a>
</wicket:extend>
</html>

I think a much better solution is to use RepeatingViews. This way you don't even need to have html for the subpanels.
BasePanel.java
class BasePanel extends Panel {
protected final RepeatingView actionbar;
public BasePanel(String id) {
super(id);
// note: I need this container for refreshing using AJAX
WebMarkupContainer outer = new WebMarkupContainer("outerContainer");
outer.setOutputMarkupId(true);
add(outer);
// ... create listview
outer.add(new ListView("items") { /* implementation of listview */ };
// This container is necessary to show/hide the buttons
actionbar = new RepeatingView("action");
// ... create default buttons
actionbar.add(new Link("add") { /* implementation of link */ }.
setBody(Model.of("Add"));
}
}
BasePanel.html
<html>
<wicket:panel>
<div wicket:id="outerContainer">
<div class="actions">
<a wicket:id="action"></a>
</div>
<div wicket:id="items">
<!-- ... markup for items is here -->
</div>
</div>
</wicket:panel>
</html>
CustomPanelA.java
class CustomPanelA extends BasePanel {
public CustomPanelA (String id) {
super(id);
// add additional buttons only
actionbar.add(new Link("actionA1") { /* implementation of link */ );
actionbar.add(new Link("actionA2") { /* implementation of link */ );
}
}
CustomPanelB.java
class CustomPanelB extends BasePanel {
public CustomPanelB (String id) {
super(id);
// add additional buttons only
actionbar.add(new Link("action_b1") { /* implementation of link */ );
actionbar.add(new Link("action_b2") { /* implementation of link */ );
}
}
Note: inside the RepeatingView you can add any component that inherits the markup of the RepeatingView component. Just make sure you don't add the same component id twice and you'll be fine.

There are plenty of solutions of your issue. I will show you one of them.
As you mentioned <wicket:child/>-based approach, then I will build my decision on it. Your BasePanel will be abstract class, which will hold all your components that already in it, except action links. It will provide one abstract method, called, for example addActionLinks:
class abstract BasePanel extends Panel {
public BasePanel(String id) {
super(id);
//...your code.
WebMarkupContainer actionBar = ...;
//call this abstract method to add links in actionBar.
addActionLinks( actionBar );
}
public abstract void addActionLinks( WebMarkupContainer container );
}
HTML:
<wicket:panel>
...
<div wicket:id="actionbar" >
<wicket:child/>
</div>
...
</wicket:panel>
And for other panels you just implement addActionLinks method:
class CustomPanelA extends BasePanel {
public CustomPanelA (String id) {
super(id);
}
protected void addChildren ( WebMarkupContainer container )
{
actionbar.add(new Link("actionA1") { /* implementation of link */ );
actionbar.add(new Link("actionA2") { /* implementation of link */ );
}
}
And HTML:
<wicket:extend>
<a wicket:id="actionA1">ActionA1</a>
<a wicket:id="actionA2">ActionA2</a>
</wicket:extend>
Of course you need to implement another panel with your BasePanel action link, removed from my implementation (create DefaultBasePanel class for example).
Also, you can create another ListView for action links in your BasePanel and also fetch them from one method, overriden by subclasses.
Also, maybe, using fragments could solve your issue too. You can look here to learn more about fragments.
I think, that there are more approaches, but for some reason this one came to my mind. Hope this helps.

Related

Wicket: Panel nesting as component

Is it possible (or what is the best way) to have own Panel with Component behaviours? Especially with HTML nesting...
I want something like that:
public HeaderPanel(String id) { /* extends Wicket Panel */
super(id);
add(new Link(ID_HOME_LINK, null) {
private static final long serialVersionUID = 1L;
{
add(new Image(ID_HOME_LOGO, new ContextRelativeResource("img/logo.png")));
}
#Override
public void onClick(AjaxRequestTarget target) {
setResponsePage(((BasicPage)getPage()).getLogoLinkPage());
}
});
}
With HTML:
<!DOCTYPE html>
<html>
<body>
<wicket:panel>
<a wicket:id="home-link">
<img wicket:id="home-logo" src="img/logo.png" title="LearnMe" style="max-height: 65px;" /> <!-- HERE is problem - I would like to set children here -->
</a>
</wicket:panel>
</body>
</html>
And this is the intent in Link (it is my "customized" panel that handles links):
public Link(String id, IModel<String> model, boolean ajax) { /* Extends Wicket Panel */
super(id);
this.ajax = ajax;
this.model = model;
setRenderBodyOnly(true);
if (ajax) {
add(link = new AjaxLink<String>(ID_LINK) {
#Override
public void onClick(AjaxRequestTarget target) {
Link.this.onClick(target);
}
});
} else {
add(link = new org.apache.wicket.markup.html.link.Link<String>(ID_LINK) {
#Override
public void onClick() {
Link.this.onClick(null);
}
});
}
}
With appropriate HTML:
<html>
<body>
<wicket:panel>
<a wicket:id="link"></a> <!-- HERE is problem - I don't know, which ones and how many components go there -->
</wicket:panel>
</body>
</html>
Now I get this Exception:
Last cause: Close tag not found for tag: <a wicket:id="home-link" id="home_link3">. For Components only raw markup is allow in between the tags but not other Wicket Component. Component: [Link [Component id = home-link]]
Maybe I am wrong with Panel capacibility or doing unnecessary thing with Link component, but I am looking for idea of nesting/inheriting Panels in one template like it is usual with Components in Wicket (like HeaderPanel Link were Wicket Link, not my customized Panel "Link" - that works, but it is not "common" solution).
Your problem is in Link.html. There you have a <a wicket:id="link" for the Wicket's Link but you do not have HTML element for the image. The one defined in HeaderPanel.html is completely overridden by the markup provided by (your) Link.html.

Either did not add the component to your page at all, or that the hierarchy does not match in wicket

Currently i m working one requirement in wicket framework.i done some coding part related to that.but i got hierarchy does not match expection always.i followed the hierarchy,but do not wt wrong i did.
here is my requirement:
by clicking on tab,i would like to display progress bar.i used ajax lazy load concept here..
Here is code:
ConditionalListView<Ancillary> orderAncillariesNavigationList = new ConditionalListView<Ancillary>(
"ancillaryItems", orderAncillaryList) {
#Override
protected void populateItem(ListItem<Ancillary> item) {
final Ancillary ancillary = item.getModelObject();
// add ajax link
final AjaxLink<String> navigationLink = new AjaxLink<String>("ancillaryLink") {
private static final long serialVersionUID = 1L;
#Override
public void onClick(AjaxRequestTarget target) {
// replace data with this ancillary
replaceWithSelectedAncillary(target, ancillary);
target.appendJavascript(CLEAR_BIG_EASY_FEEDBACK);
}
};
navigationLink.add(new Label("ancillaryLinkName", ancillary.getLabel()));
//progress bar
if(ancillary.getLabel().equals("Episode / Show Descriptions")){
add(new AjaxLazyLoadPanel("lazy")
{
private static final long serialVersionUID = 1L;
#Override
public Component getLazyLoadComponent(String id)
{
// sleep for 5 seconds to show the behavior
try
{
Thread.sleep(5000);
}
catch (InterruptedException e)
{
throw new RuntimeException(e);
}
return navigationLink;
}
});
}
// set class
if (ancillary.getId().longValue() == selectedAncillary.getId().longValue()) {
item.add(new AttributeAppender("class", new Model<String>("selected"), " "));
}
item.add(navigationLink);
//item.add(TestPage());
}
};
HTML:
<div class="reset"> </div>
<div wicket:id="feedback"></div>
<div wicket:id="lazy"></div>
<div id="ancillaryNavigation">
<ul id="ancillaryTabs" class="group">
<li wicket:id="ancillaryItems">
<span wicket:id="ancillaryLinkName">Music Cue Sheets</span>
</li>
</ul> <!-- /#ancillaryTabs -->
</div> <!-- /#ancillaryNavigation -->
<form wicket:id="ancillaryManualForm" name="ancillaryManualForm" class="epForm">
<div wicket:id="ancillaryOrderEpisodes" />
</form>
</div> <!-- /#ancillaryOrders -->
Please help me out.What i did wrong here.
You might want to refer to the wicket examples for adding links to the list view. Here is my observation from your code: 1. You are loading a link in lazy load. Is the loading of your link going to take long time or the panel being loaded on click of the link is going to take long time? 2.you don't add label to the link as child component, you can set the label of the link via property model. 3. Check the sequence of your markup components and java components structures. Parent -child components hierarchy must match.
I would first start with taking small steps one at a time; looking into wicket examples for all the different components I am using.

Wicket AjaxLink in Modal Window doesn´t work

I am trying to add an AjaxLink inside a ModalWindow. This AjaxLink is used to do some stuff like deleting something off the database and finally close the ModalWindow.
I added the ModalWindow accordording to the Wicket examples: Link to examples. But this doesn´t work.
My MainPage:
public class EventPanel extends Panel {
// some stuff happens here, the constructor accepts the eventModel
final ModalWindow modal;
add(modal = new ModalWindow("modal"));
modal.setCookieName("modal-1");
modal.setPageCreator(new ModalWindow.PageCreator() {
public Page createPage() {
// Use this constructor to pass a reference of this page.
return new DeleteEventWindow(eventModel, modal);
}
});
modal.setCloseButtonCallback(new ModalWindow.CloseButtonCallback() {
public boolean onCloseButtonClicked(AjaxRequestTarget target) {
// Change the passValue variable when modal window is closed.
return true;
}
});
// Add the link that opens the modal window.
add(new AjaxLink<Void>("showModalLink") {
#Override
public void onClick(AjaxRequestTarget target) {
modal.show(target);
}
});
}
Modal Window:
public class DeleteEventWindow extends WebPage {
public DeleteEventWindow(final IModel<Event> model,
final ModalWindow window) {
// some stuff happens
// this link doesn´t work
add(new AjaxLink<Void>("closeOK") {
#Override
public void onClick(AjaxRequestTarget target) {
// Just a print to console for debugging
System.out.println("nooo");
window.close(target);
}
});
}
}
ModalWindow HTML
<html>
<head>
<title>Modal Content Page</title>
</head>
<body>
<!-- some other fields output --!>
<a wicket:id="closeOK">close</a><br/>
</body>
</html>
The ModalWindow itself works fine, also the link is rendered. But if I click onto it, the onClick function doesn´t seem to be triggered. I also tried a normal Link, this works fine..
I also found this question : stackoverflow question, but I am using JQuery 1.9.1..
Your code is not giving enough information; you have the modal window added to an item (row Item I assume), but the link to show the modal is added to the panel.

Customizing IndicatingAjaxLink in wicket

in my current project i've faced a problem of customizing IndicatingAjaxLink in wicket, is there any solution to change standart gif image to my own?
For example we have following listeneer
add(new IndicatingAjaxLink("closeReceivedBillspanel") {
public void onClick(AjaxRequestTarget art) {
// some timeconsuming calculations
}
});
as user clicks this link, the gif with loading appears, and i want to change this gif, is there any solution for this problem?
Have your page implements the IAjaxIndicatorAware interface
public class BasePage extends WebPage implements IAjaxIndicatorAware {
public BasePage(final PageParameters parameters) {
// Home link
AjaxLink<Page> homeLink = new AjaxLink<Page>("homeLink") {
private static final long serialVersionUID = 1L;
#Override
public void onClick(AjaxRequestTarget target) {
setResponsePage(HomePage.class);
}
};
add(homeLink);
}
#Override
public String getAjaxIndicatorMarkupId() {
return "indicator";
}
This way, you can set, in the html, any image you want to display when the loading appears by changing the image in the "img" tag
<div id="indicator" style="display: none;">
<div class="indicator-content">
Please wait... <wicket:link><img src="images/loading.gif" width="16" height="16" alt="loading" /></wicket:link>
</div>
</div>
Create yoru own custom class like, (copy whats inside IndicatingAjaxLink and update)
public class MyIndicatingAjaxLink<T> extends AjaxLink<T> implements IAjaxIndicatorAware {
private final MyAjaxIndicatorAppender indicatorAppender = new MyAjaxIndicatorAppender();
.
//rest of the code is same as IndicatingAjaxLink class
.
}
Also you need a custom AjaxIndicatorAppender within your customIndicatingAjaxLink and you need to override below method of indicatorAppender to return path of your custom image
protected CharSequence getIndicatorUrl()

Wicket - Replace Label By TextField and vice versa

I am trying to replace a Label by a TextField in a ListView. I saw a similar wicket example few weeks ago over internet, but I don't remember the link. I have added AjaxEventBehavior - "onDblClick" to the ListItem by which I want to replace a Label by a TextField and also added AjaxFormComponentUpdatingBehavior - "onBlur" to the TextField such that the TextField will be replaced by Label. Somehow it is not working. The List model for the ListView contain only {"aaaaaaaaaaaaa", "bbbbbbbbbbbbbbbb", "cccccccccccccc"} [as I am testing it] so the first Label will be "aaaaaaaaaaaaa", if I double click on this Label the TextField is appearing the place of the Label "cccccccccccccc", which is unexpected. And also the "onBlur" event is not working. Hope I can explain the problems. The code is given below:
public class TaskTypeSettingsPage extends BasePage implements Serializable {
private String val;
public TaskTypeSettingsPage() {
add(new TaskTypeSettingsForm("form"));
}
public void setVal(String val) {
this.val = val;
}
public String getVal() {
return val;
}
private class TaskTypeSettingsForm extends Form {
private static final long serialVersionUID = 10058L;
private Fragment labelFragment;
private Fragment textFragment;
public TaskTypeSettingsForm(String id) {
super(id);
setOutputMarkupId(true);
ListView listView = new ListView("row", Arrays.asList("aaaaaaaaaaaaa", "bbbbbbbbbbbbbbbb", "cccccccccccccc")) {
#Override
protected void populateItem(ListItem item) {
String str = (String) item.getModelObject();
item.add(new Label("listLabel", str));
item.setOutputMarkupId(true);
labelFragment = new Fragment("frag", "labelFragment", this.getPage());
Label label = new Label("label", str);
label.setOutputMarkupId(true);
labelFragment.setOutputMarkupId(true);
labelFragment.add(label);
item.add(labelFragment);
textFragment = new Fragment("frag", "textFragment", this.getPage());
TextField text = new TextField("text", new PropertyModel(TaskTypeSettingsPage.this, "val"));
text.setOutputMarkupId(true);
textFragment.setOutputMarkupId(true);
textFragment.add(text);
item.add(new AjaxEventBehavior("onDblClick") {
#Override
protected void onEvent(AjaxRequestTarget target) {
labelFragment.replaceWith(textFragment);
labelFragment = textFragment;
target.addComponent(textFragment);
}
});
text.add(new AjaxFormComponentUpdatingBehavior("onBlur") {
#Override
protected void onUpdate(AjaxRequestTarget target) {
textFragment.replaceWith(labelFragment);
textFragment = labelFragment;
target.addComponent(labelFragment);
}
});
}
};
add(listView);
}
}
}
And
<html>
<body>
<wicket:extend>
<div class="heading"><wicket:message key="extras.taskType" /></div>
<form wicket:id="form" autocomplete="off">
<table>
<tr wicket:id="row">
<td>
<span wicket:id="frag"></span>
</td>
</tr>
</table>
<wicket:fragment wicket:id="labelFragment"><span wicket:id="label"></span>
</wicket:fragment>
<wicket:fragment wicket:id="textFragment"><input type="text" wicket:id="text">
</wicket:fragment>
</form>
</wicket:extend>
</body>
</html>
Any information or example code will be very helpful to me. Thank you.
Edit: I found the link: example but the source code is not available.
You can replace an entire component, but you also have to consider that the same markup might not work for both a label and a text field. But you can always replace a fragment with another fragment, so if you wrap your field and label in a fragment each, you can switch between them anytime.
However you're better off using a dedicated component for this purpose, I seem to remember an Ajax field component in either core Wicket or Wicket Extensions that did it. It is called AjaxEditableLabel
The example you are trying to remember might be this editable label example.
There is a Visural Wicket library that has ViewOrEdit component. It sounds something like you are looking for.
The ListView component may not be the best basis for a form. See http://wicketinaction.com/2008/10/building-a-listeditor-form-component/

Categories