0
votes

I am new to Wicket. After reading the wicket documentation, I decided to create a table using datatable. However, I really need some assistance here. How can I made the row in the table expandable in datatable? Is it true that this cannot be done in datatable and I have to switch over to repeatingview?

HTML Code

<!DOCTYPE html>
<html lang="en"
      xml:lang="en"
      xmlns:wicket="http://wicket.apache.org>
   <head>
      <title> Wicket test </title>
   </head>
   <body>
      <table cellspacing="0" wicket:id="table"></table>
   </body>
</html>

Java Code

import java.util.ArrayList;
import java.util.List;

import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.extensions.markup.html.repeater.data.table.DefaultDataTable;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
import org.apache.wicket.model.Model;

public class TestPage extends WebPage {
    public TestPage() {
        final UserProvider userProvider = new UserProvider();
        List<IColumn<UserProvider.Contact, String>> columns = new ArrayList<IColumn<UserProvider.Contact, String>>(2);
        columns.add(new PropertyColumn<UserProvider.Contact, String>(new Model<String>("First Name"), "name.first", "name.first"));
        columns.add(new PropertyColumn<UserProvider.Contact, String>(new Model<String>("Last Name"), "name.last", "name.last"));
        DefaultDataTable<UserProvider.Contact, String> dataTable = new DefaultDataTable<UserProvider.Contact, String>("table", columns, userProvider, 10);
        add(dataTable);
    }
}

Java Code for UserProvider

import java.util.*;
import java.io.Serializable;

import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.PropertyModel;

public Class UserProvider extends SortableDataProvider {
    class SortableDataProviderComparator implements Comparator<Contact>, Serializable {
        public int compare(final Contact o1, final Contact o2) {
            PropertyModel<Comparable> model1 = new PropertyModel<Comparable>(o1, (String) getSort().getProperty());
            PropertyModel<Comparable> model2 = new PropertyModel<Comparable>(o2, (String) getSort().getProperty());
            int result = model1.getObject().compareTo(model2.getObject());
            if (!getSort().isAscending()) {
                result = -result;
            }
            return result;
        }
    }

    private List<Contact> list = new ArrayList<Contact>();
    private SortableDataProviderComparator comparator = new SortableDataProviderComparator();

    public UserProvder() {
        setSort("name.first", SortOrder.ASCENDING);
        list.add(new Contact(new Name("Abbie", "Zed LaBlanc Kayle Garcia")));
        list.add(new Contact(new Name("Benny", "Yellen")));
        list.add(new Contact(new Name("Charles", "Wukong Mike Alistar")));
        list.add(new Contact(new Name("Dennis Woody Tresh", "Rose")));
        list.add(new Contact(new Name("Elaine", "Poppy")));
        list.add(new Contact(new Name("Peter Jose Brian", "Jax")));
    }

    public Iterator<Contact> model(long first, long count) {
        List<Contact> newList = new ArrayList<Contact>(list);
        Collections.sort(newList, comparator);
        return newList.subList((int) first, (int) (first + count)).iterator();
    }

    public IModel<Contact> model(final Object object) {
        return new AbstractReadOnlyModel<Contact>() {
            @Override
            public Contact getObject() {
                return (Contact) object;
            }
        };
    }

    public long size() {
        return list.size();
    }

    class Contact implements Serializable {
        private final Name name;
        public Contact(final Name name) {
            this.name = name;
        }
        public Name getName() {
            return name;
        }
    }

    class Name implements Serializable {
        private String firstName;
        private String lastName;
        public Name(final String fName, final String lName) {
            firstName = fName;
            lastName = lName;
        }
        public String getFirst() {
            return firstName;
        }
        public String getLast() {
            return lastName;
        }
        public setFirst(final String firstName) {
            this.firstName = firstName;
        }
        public setLast(final String lastName) {
            this.lastName = lastName;
        }
    }
}

The above Java code are from some online example. The goal would be the default table only show the first word of the last name and the first word of the first name. Once the row is click, then it will expand and show the entire first name and last name of the row.

Thanks in advance for any inputs and suggestions. Hopefully, I don't have to switch away from datatable.

2

2 Answers

1
votes

If I understand this correct, you essentially want to change the contents of the row when a user clicks on it. There is one important thing to understand here: if you want to do this on client-side, or server-side.

If you want to do this purely on client side, then you have to use javascript. I assume this is not what you want to do, so I won't go into too much detail, but the point would be attaching a Behavior to each Item in the DataTable that would trigger a javascript function performing the expansion. I assume this is not what you want to do though, as it is not quite wicket.

There are multiple ways to do this in wicket on server-side. You can either change the data representation elements (i.e. change IColumns to populate the data table with something that would detect the clicks and then change their display), or change the data source (in which case the PropertyColumn would pick up the changes on re-rendering).

In either case, you would probably have to attach a behavior to the row item. Do something like this:

Override the newRowItem() method on the DataTable. Run the super method and on the Item it returns, attach a custom Behavior, something along the lines of the following:

@Override
protected Item<T> newRowItem(final String id, final int index, final IModel<T> model)
{
    Item rowItem = super(id, index, model);
    rowItem.add(new AjaxEventBehavior("onclick") {
         protected void onEvent(AjaxRequestTarget target) {
             //do something
         }
     }
     return rowItem;
}

Where I just wrote //do something, you would have to take appropriate action depending on which approach you took. What I would do is use a custom model, which would have a flag to track whether it was clicked or not.

Then, use that flag in wherever you decided it suits you best. If you choose to affect the display, then use the AbstractColumn and in the populate method you override, use a custom label which would detect whether the flag is ticket and display an appropriate value. Alternatively, you can check the flag whenever you return the object from the model to instead return a copy of the real object with the values on it set to what you want to display; using this method your PropertyColumn instances would continue working with no modifications.

I left quite a lot out of this answer still, if you would like me to elaborate, please leave a comment.

0
votes

To do it in Wicket i would try the "Making cells clickable" aproach from the Apache Wicket Cookbook: http://www.packtpub.com/sites/default/files/1605OS-Chapter-5-Displaying-Data-Using-DataTable.pdf

You will instead of making an link change the model object. To quickly get the first word of a string you can use Apache Commons StringUtils.substringBefore(contact.getName(), "")