Same AjaxEventBehavior for multiple components - wicket

I add an image and a text component to a WebMarkupContainer as described here:
filter.add(newFilterLabel("textSub", customerText, filtervalue));
filter.add(newFilterImage("imgSub", filtervalue));
For each component, there is an AjaxEventBehavior doing different things. I would like to change it in a way that both do the same thing independent from which component has been clicked on.
private Component newFilterLabel(String id, IModel<String> customText,
final SourceFilterValue currentValue) {
final BBLabel label = new BBLabel(id, customText);
label.add(new AjaxEventBehavior("onclick") {
private static final long serialVersionUID = 1L;
#Override
protected void onEvent(AjaxRequestTarget target) {
doSomething(currentValue,filteredsources, target);
}
});
return label;
}
private Image newFilterImage(String id, final SourceFilterValue filterValue) {
final Image img = new Image(id, resources.getImage(EXPAND_ICON));
img.add(new AjaxEventBehavior("onclick") {
private static final long serialVersionUID = 1L;
#Override
protected void onEvent(AjaxRequestTarget target) {
doSomething(img);
}
});
return img;
}
Do you have any suggestions how to change it or any workaround? I use Wicket 1.5.8.

An AjaxBehavior can be bound to a single element only. Either add it to a parent in the hierarchy, or just let both behaviors call the same method:
#Override
protected void onEvent(AjaxRequestTarget target) {
doSomething(filterValue); //filterValue has to be final to be able to access it from the inner class
}

Related

How can I configure the color of the feedback messages in Wicket Sessions?

The Problem
Hello,
I am trying to configure the color of Wickets feedback messages. I am currently maintaining a Wicket GUI (Wicket 7.6.1). It seems that Session.get().warn("Watch out!") prints a green warning box, annotated with the CSS class alert-success. I would like it to change its color to yellow.
What I got so far:
I found that Session.get().getApplication().getResourceSettings() gives me access to some resource settings, including a properties factory. But I don't know how to use it. Also, I have looked for markup files related to my Session but not found any.
Any help would be greatly appreciated!
You can create your custom feedback panel if you want.
CustomFeedBackPanel.html
<wicket:panel>
<div wicket:id="feedbackul">
<wicket:container wicket:id="messages">
<p wicket:id="message"></p>
</wicket:container>
</div>
</wicket:panel>
CustomFeedBackPanel.java
public class CustomFeedbackPanel extends Panel implements IFeedback {
private static final long serialVersionUID = 1L;
private final MessageListView messageListView;
WebMarkupContainer messagesContainer = new WebMarkupContainer("feedbackul") {
private static final long serialVersionUID = 1L;
#Override
protected void onConfigure() {
super.onConfigure();
setVisible(anyMessage());
}
};
public CustomFeedbackPanel(final String id) {
this(id, null);
}
public CustomFeedbackPanel(final String id, IFeedbackMessageFilter filter) {
super(id);
add(messagesContainer);
messageListView = new MessageListView("messages");
messagesContainer.add(messageListView);
if (filter != null) {
setFilter(filter);
}
}
public final boolean anyErrorMessage() {
return anyMessage(FeedbackMessage.ERROR);
}
public final boolean anyMessage() {
return anyMessage(FeedbackMessage.UNDEFINED);
}
public final boolean anyMessage(int level) {
List<FeedbackMessage> msgs = getCurrentMessages();
for (FeedbackMessage msg : msgs) {
if (msg.isLevel(level)) {
return true;
}
}
return false;
}
public final FeedbackMessagesModel getFeedbackMessagesModel() {
return (FeedbackMessagesModel) messageListView.getDefaultModel();
}
public final IFeedbackMessageFilter getFilter() {
return getFeedbackMessagesModel().getFilter();
}
public final CustomFeedbackPanel setFilter(IFeedbackMessageFilter filter) {
getFeedbackMessagesModel().setFilter(filter);
return this;
}
public final Comparator<FeedbackMessage> getSortingComparator() {
return getFeedbackMessagesModel().getSortingComparator();
}
public final CustomFeedbackPanel setSortingComparator(Comparator<FeedbackMessage> sortingComparator) {
getFeedbackMessagesModel().setSortingComparator(sortingComparator);
return this;
}
#Override
public boolean isVersioned() {
return false;
}
public final CustomFeedbackPanel setMaxMessages(int maxMessages) {
messageListView.setViewSize(maxMessages);
return this;
}
protected String getCSSClass(final FeedbackMessage message) {
String css = "feedback";
if (message.getLevel() == FeedbackMessage.ERROR
|| message.getLevel() == FeedbackMessage.FATAL) {
css = "feedback error";
}
if (message.getLevel() == FeedbackMessage.SUCCESS) {
css = "feedback success";
}
if (message.getLevel() == FeedbackMessage.WARNING) {
css = "feedback warn";
}
return css;
}
protected final List<FeedbackMessage> getCurrentMessages() {
final List<FeedbackMessage> messages = messageListView.getModelObject();
return Collections.unmodifiableList(messages);
}
protected FeedbackMessagesModel newFeedbackMessagesModel() {
return new FeedbackMessagesModel(this);
}
protected Component newMessageDisplayComponent(String id, FeedbackMessage message) {
Serializable serializable = message.getMessage();
Label label = new Label(id, (serializable == null) ? "" : serializable.toString());
label.setEscapeModelStrings(CustomFeedbackPanel.this.getEscapeModelStrings());
//label.add(new AttributeModifier("class",getCSSClass(message)));
return label;
}
private final class MessageListView extends ListView<FeedbackMessage> {
private static final long serialVersionUID = 1L;
public MessageListView(final String id) {
super(id);
setDefaultModel(newFeedbackMessagesModel());
}
#Override
protected IModel<FeedbackMessage> getListItemModel(
final IModel<? extends List<FeedbackMessage>> listViewModel, final int index) {
return new AbstractReadOnlyModel<FeedbackMessage>() {
private static final long serialVersionUID = 1L;
#Override
public FeedbackMessage getObject() {
if (index >= listViewModel.getObject().size()) {
return null;
} else {
return listViewModel.getObject().get(index);
}
}
};
}
#Override
protected void populateItem(final ListItem<FeedbackMessage> listItem) {
final FeedbackMessage message = listItem.getModelObject();
message.markRendered();
final Component label = newMessageDisplayComponent("message", message);
final AttributeModifier levelModifier = AttributeModifier.replace("class",
getCSSClass(message));
//label.add(levelModifier);
listItem.add(levelModifier);
listItem.add(label);
messagesContainer.add(levelModifier);
}
}
}
Main thing you should consider getCssClass() method. You can change according to your requirement.I have modified just for your reference.
protected String getCSSClass(final FeedbackMessage message) {
String css = "feedback";
if (message.getLevel() == FeedbackMessage.ERROR
|| message.getLevel() == FeedbackMessage.FATAL) {
css = "alert error";
}
if (message.getLevel() == FeedbackMessage.SUCCESS) {
css = "alert success";
}
if (message.getLevel() == FeedbackMessage.WARNING) {
css = "alert warn";
}
return css;
}
Feedback messages are rendered by FeedbackPanel class. It seems your application uses custom implementation of FeedbackPanel that renders the messages as Bootstrap Alerts.
By default Wicket sets feedbackMessage<LogLevel> (e.g. feedbackMessageWarning) as a CSS class to all messages, so you can style them however you want.
An alternative to not create a custom FeedbackPanel and new HTML/Java files is to use enclosures:
Using Twitter Bootstrap classes:
<wicket:enclosure>
<div class="alert alert-danger alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<h4><i class="icon fa fa-ban"></i> Error on form validation!</h4>
<div wicket:id="errorMessages"></div>
</div>
</wicket:enclosure>
In page constructor:
FeedbackCollector collector = new FeedbackCollector(this);
ExactErrorLevelFilter errorFilter = new ExactErrorLevelFilter(FeedbackMessage.ERROR);
add(new FeedbackPanel("errorMessages", errorFilter) {
#Override public boolean isVisible() {
return !collector.collect(errorFilter).isEmpty();
}
});
Since Wicket 6 feedback messages are attached to components, so you can use a FeedbackCollector and a filter to get and display desired messages. The advantages of enclosures is:
you don't need to create new files;
it works similar to fragments/panels;
it's only rendered if desired messages exists;
Hope it helps.

ClassCashException on PullToRefreshListView

I am trying to implement the PullToRefresh using ListView from eu.erikw.PullToRefreshListView project. I am using custom adapter to populate my listview. However on onItemClick event, I get this error java.lang.ClassCastException: android.widget.HeaderViewListAdapter cannot be cast to com.example.xxapp.RssAdapter where my RssAdapter class is
public class RssAdapter extends BaseAdapter{
private final List<RssItem> items;
private final Context context;
public RssAdapter(Context context, List<RssItem> items) {
this.items = items;
this.context = context;
}
#Override
public int getCount() {
return items.size();
}
#Override
public Object getItem(int position) {
return items.get(position);
}
#Override
public long getItemId(int id) {
return id;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = View.inflate(context, R.layout.rss_item, null);
holder = new ViewHolder();
holder.itemTitle = (TextView) convertView.findViewById(R.id.itemTitle);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.itemTitle.setText(items.get(position).getTitle());
}
static class ViewHolder {
TextView itemTitle;
}
}
and RssItem class is
public class RssItem {
private final String title;
private final String link;
public RssItem(String title, String link) {
this.title = title;
this.link = link;
}
public String getTitle() {
return title;
}
public String getLink() {
return link;
}
}
and this is the onclick method giving the error
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Log.e("Clicked item", "Position is " +position);
//line below gives ClasscastException error
RssAdapter adapter = (RssAdapter) parent.getAdapter();
RssItem item = (RssItem) adapter.getItem(position);
Uri uri = Uri.parse(item.getLink());
Intent i = new Intent(getActivity(),WebViewActivity.class);
i.putExtra("mystring",uri.toString());
startActivity(i);
}
Please I need help with the onclick method so I can get the link to display a webpage.
I have figured out a way. Before I set the adapter to the listview, I create another listarray with the same form as the RssItem so I can assign the adapter to this listarray. Later on when I need this adapter, I just call the new listarray which in this case is not affected by the header I added to the listview. Thanks njzk2 for your comment about keeping my adapter as an instance member. The solution looks like this:
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Log.e("Clicked item", "Position is " +position);
TextView title = (TextView) view.findViewById(R.id.itemTitle);
String titleItem = title.getText().toString();
Log.e("Title", titleItem);
//RssAdapter adapter = (RssAdapter) parent.getAdapter();
//RssItem item = (RssItem) adapter.getItem(position);
RssItem item = itemx.get(position);
Uri uri = Uri.parse(item.getLink());
Intent i = new Intent(getActivity(),WebViewActivity.class);
i.putExtra("mystring",uri.toString());
startActivity(i);
}
where itemx is the instance of my adapter. I don't know how effective it is to do this but it worked
You should use that
HeaderViewListAdapter hlva = (HeaderViewListAdapter)parent.getAdapter();
RssAdapter adapter = (RssAdapter)hlva.getWrappedAdapter();

Multi select drop down in Wicket

How to implement multiple select drop down in Wicket. I am able to create multi select drop down view using bootstrap but I am not able to get how to relate selected options with IModel of drop down component? Is there any possibility in Wicket? I do not want to use ListMultipleChoice.
Here is a sample code.
{
private IModel<List<? extends String>> statusChoices;
private DropDownChoice<String> status;
private String statusFilter = "firstChoice";
// List of Items in drop down
statusChoices = new AbstractReadOnlyModel<List<? extends String>>() {
#Override
public List<String> getObject() {
List<String> list = new ArrayList<String>();
list.add("firstChoice");
list.add("secondChoice");
list.add("thirdChoice");
return list;
}
};
status = new DropDownChoice<String>("status",new PropertyModel<String>(this, "statusFilter"), statusChoices);
status.add(new AjaxFormComponentUpdatingBehavior("onchange") {
#Override
protected void onUpdate(AjaxRequestTarget target) {
if(statusFilter.equals("firstChoice"))
// Do Somthing
else
// Do Somthing
}
});
}

Attempt to set model object on null model of component: form:checkgroup

I'm trying to create a list of HITs (objects), where each has a checkbox next to it, so that I can select them and delete them all at once. I've made a form with a checkbox for each row in the table:
final HashSet<HIT> selectedValues = new HashSet<HIT>();
final CheckGroup checkgroup = new CheckGroup("checkgroup");
final Form form = new Form("form"){
#Override
public void onSubmit() {
super.onSubmit();
}
};
checkgroup.add(new CheckGroupSelector("checkboxSelectAll"));
UserHitDataProvider userHitDataProvider = new UserHitDataProvider(selectedIsReal, keyId, secretId);
final DataView<HIT> dataView = new DataView<HIT>("pageable", userHitDataProvider) {
private static final long serialVersionUID = 1L;
#Override
protected void populateItem(final Item<HIT> item) {
HIT hit = item.getModelObject();
item.add(new CheckBox("checkbox", new SelectItemUsingCheckboxModel(hit,selectedValues)));
item.add(new Label("hitName", String.valueOf(hit.getTitle())));
item.add(new Label("hitId", String.valueOf(hit.getHITId())));
}
};
//add checkgroup to form, form to page, etc.
I've also added a new class to take care of the selection/deletion:
public class SelectItemUsingCheckboxModel extends AbstractCheckBoxModel {
private HIT hit;
private Set selection;
public SelectItemUsingCheckboxModel(HIT h, Set selection) {
this.hit = h;
this.selection = selection;
}
#Override
public boolean isSelected() {
return selection.contains(hit);
}
#Override
public void select() {
selection.add(hit);
}
#Override
public void unselect() {
selection.remove(hit);
}
}
Everything renders fine, but I get an error when trying to submit:
Caused by: java.lang.IllegalStateException: Attempt to set model object on null model of component: form:checkgroup
at org.apache.wicket.Component.setDefaultModelObject(Component.java:3042)
at org.apache.wicket.markup.html.form.FormComponent.updateCollectionModel(FormComponent.java:1572)
at org.apache.wicket.markup.html.form.CheckGroup.updateModel(CheckGroup.java:160)
at org.apache.wicket.markup.html.form.Form$FormModelUpdateVisitor.component(Form.java:228)
at org.apache.wicket.markup.html.form.Form$FormModelUpdateVisitor.component(Form.java:198)
at org.apache.wicket.util.visit.Visits.visitPostOrderHelper(Visits.java:274)
at org.apache.wicket.util.visit.Visits.visitPostOrderHelper(Visits.java:262)
at org.apache.wicket.util.visit.Visits.visitPostOrder(Visits.java:245)
at org.apache.wicket.markup.html.form.FormComponent.visitComponentsPostOrder(FormComponent.java:422)
at org.apache.wicket.markup.html.form.Form.internalUpdateFormComponentModels(Form.java:1793)
at org.apache.wicket.markup.html.form.Form.updateFormComponentModels(Form.java:1757)
at org.apache.wicket.markup.html.form.Form.process(Form.java:913)
at org.apache.wicket.markup.html.form.Form.onFormSubmitted(Form.java:770)
at org.apache.wicket.markup.html.form.Form.onFormSubmitted(Form.java:703)
... 27 more
I think its some of the Ajax code breaking, since my SelectAllCheckBox button is also failing. Any ideas why? Is this even the best way to handle such a use case?
Your Checkgroup does not have a Model, thus Wicket can't copy the current state of the Model into a null object. You should use the constructor with an additional parameter representing the Model you want to store the value in.

Wicket children panels refresh

I'm just looking for extra pair of eyes to spot why children panels do not show up/change visibility on radio button selection/change that does call for their refresh
public class OptionsPanel extends Panel {
private AutoCompleteSearchField departureField;
private HiddenField departureCodeField;
private CompoundPropertyModel model;
private RadioGroup flightChoices;
private RadioGroup dateChoices;
private final TripSearchModel tripSearchModel;
private WebMarkupContainer optionsContainer;
private FlexibleDates flexibleDates;
private FixedDates fixedDates;
private PassengersAndClass passengersAndClass;
private static List FIX_CONTAINER_VISIBLE = Lists.newArrayList(true, false, true);
private static List FLEX_CONTAINER_VISIBLE = Lists.newArrayList(false, true, true);
private static List HIDE_CONTAINERS = Lists.newArrayList(false, false, false);
public OptionsPanel(String id, CompoundPropertyModel model) {
super(id);
this.model = model;
this.tripSearchModel = (TripSearchModel) model.getObject();
add(departureLabel());
add(departureField());
add(departureCodeField());
add(flightType());
add(travellingWhen());
add(dateType());
add(optionsContainer(HIDE_CONTAINERS));
}
private Component departureLabel() {
return new WebMarkupContainer("departureLabel").setOutputMarkupId(true);
}
private AutoCompleteSearchField departureField() {
departureField = new AutoCompleteSearchField("departureField", "From", "flightFromField", null, true, model.bind("departure"));
departureField.setOutputMarkupId(true);
departureField.add(new CityValidator(this, departureCodeField));
return departureField;
}
private HiddenField departureCodeField() {
departureCodeField = new HiddenField("departureCodeField", model.bind("departureCode"));
departureCodeField.setMarkupId("departureFieldCode");
return departureCodeField;
}
private Component flightType(){
flightChoices = new RadioGroup("flightTypes");
flightChoices.setModel(model.bind("tripType"));
flightChoices.add(listOfRadio(flightsTypeList(), "flightType"));
return flightChoices;
}
private List flightsTypeList() {
return Arrays.asList(
new RadioOptionObject("one way", new Model(TRIP_TYPE_ONE_WAY)),
new RadioOptionObject("return", new Model(TRIP_TYPE_RETURN))
);
}
private Component travellingWhen(){
return new Label("travellingWhen", new StringResourceModel("travelling_when", this, new Model("")).getString());
}
private Component dateType(){
dateChoices = new RadioGroup("dateTypes");
dateChoices.setModel(model.bind("dateType"));
dateChoices.add(listOfRadio(datesTypeList(), "dateType"));
return dateChoices;
}
private List datesTypeList() {
return Arrays.asList(
new RadioOptionObject("Flexible dates", new Model(DATE_TYPE_FLEX)),
new RadioOptionObject("Fixed dates", new Model(DATE_TYPE_FIX)));
}
private ListView listOfRadio(final List flightDateOptionValues, final String componentId) {
ListView listView = new ListView(componentId + "sList", flightDateOptionValues) {
#Override
protected void populateItem(final ListItem listItem) {
final Radio radio = new Radio(componentId + "Radio", ((RadioOptionObject) listItem.getModelObject()).getRadioModel()) {
#Override
public String getValue() {
return listItem.getDefaultModelObjectAsString();
}
#Override
protected boolean getStatelessHint() {
return true;
}
};
radio.add(new AjaxEventBehavior("onchange") {
#Override
protected void onEvent(AjaxRequestTarget target) {
tripSearchModel.setDateType(radio.getModelObject().toString());
refreshPanel(target);
}
});
listItem.add(radio);
listItem.add(new Label(componentId + "Name", new StringResourceModel(radio.getModelObject().toString(), this, radio.getModel())));
}
};
return listView;
}
private void refreshPanel(AjaxRequestTarget target) {
this.remove(optionsContainer);
target.addComponent(optionsContainer(visibility()));
}
private List visibility() {
return visibilityMode(((TripSearchModel) model.getObject()).getDateType());
}
private Component optionsContainer(List visibility){
optionsContainer = new WebMarkupContainer("optionsContainer");
optionsContainer.add(flexibleDates(visibility.get(0)));
optionsContainer.add(fixedDates(visibility.get(1)));
optionsContainer.add(passengersAndClass(visibility.get(2)));
optionsContainer.setOutputMarkupId(true);
optionsContainer.setVisible(true);
return optionsContainer;
}
private Component flexibleDates(Boolean visibility){
flexibleDates = new FlexibleDates("flexibleDates", model);
flexibleDates.setOutputMarkupId(true);
flexibleDates.setVisible(visibility);
return flexibleDates;
}
private Component fixedDates(Boolean visibility){
fixedDates = new FixedDates("fixedDates", model);
fixedDates.setOutputMarkupId(true);
fixedDates.setVisible(visibility);
return fixedDates;
}
private Component passengersAndClass(Boolean visibility){
passengersAndClass = new PassengersAndClass("passengersAndClass", model);
passengersAndClass.setOutputMarkupId(true);
passengersAndClass.setVisible(visibility);
return passengersAndClass;
}
private List visibilityMode(String dateType) {
if(DATE_TYPE_FIX.equalsIgnoreCase(dateType)){
return FIX_CONTAINER_VISIBLE;
} else if(DATE_TYPE_FLEX.equalsIgnoreCase(dateType)){
return FLEX_CONTAINER_VISIBLE;
} else{
return HIDE_CONTAINERS;
}
}
}
I think one potential issue you may have is that you listen for ajax-onchange events and you attempt to make changes to panels depending on the model supposedly having changed. In my experience with radio-type form components, you may need to use AjaxFormComponentUpdatingBehavior (instead of AjaxEventBehavior) in order to capture changes to a model of such form-components. Hope this helps!
Edit: Instead of listing caveats (you need to use another type of behavior for some form components), I'll just add a link to the documentation: Javadoc for AjaxFormComponentUpdatingBehavior
At the end of the day I find out that there was another Easter egg hidden for me.
radio.add(new AjaxEventBehavior("onchange") {
#Override
protected void onEvent(AjaxRequestTarget target) {
tripSearchModel.setDateType(radio.getModelObject().toString());
refreshPanel(target);
}
I was changing same date parameter on event of two different radio groups, which rendered form useless. That was one change, the second change was moving from WebMarkupContainer to EnclosureContainer that was suggested to use on Wicket mailing list for components changing their visibility status. Nevertheless I will give it a try with AjaxFormComponentUpdatingBehavior thank you #Martin Peters