Trouble with CellList empty widget and AsyncDataProvider - java

I have a CellList that I'm populating with an AsyncDataProvider:
#UiField(provided = true)
CellList<PlayerDataEntity> friendCellList;
#Inject
FriendListViewImpl(FriendListController controller) {
friendCellList = new CellList<PlayerDataEntity>(new PlayerCell());
initWidget(uiBinder.createAndBindUi(this));
// if we don't set the row data to empty before trying to get the real data,
// the empty list widget will never appear. But if we do set this,
// then the real data won't show up.
friendCellList.setRowData(Lists.<PlayerDataEntity>newArrayList());
new AsyncDataProvider<PlayerDataEntity>() {
#Override
protected void onRangeChanged(final HasData<PlayerDataEntity> display) {
rpcService.getPlayers(new AsyncCallback<List<PlayerDataEntity>>() {
#Override
public void onSuccess(List<PlayerDataEntity> result) {
display.setRowData(0, result);
}
});
}
}.addDataDisplay(friendCellList);
friendCellList.setEmptyListWidget(new Label("No friends found"));
}
If I don't initially set the row data to an empty list, then the empty list widget won't show up if the RPC service returns an empty list. However, if I do initially set the row data to the an empty list, and the RPC returns a non-empty list, that data won't show up in the widget.
I must be misunderstanding some aspect of the CellList's API. What am I doing wrong?

If you know, that the total list (not just the list in the range) is empty, then you can set display.setRowCount(0), so the CellList will display "No friends found".
If your service always returns the entire list, this can be done easily like
public void onSuccess(List<PlayerDataEntity> result) {
display.setRowCount(result.size());
display.setRowData(0, result);
}

Add the following line to the method:
#Override
public void onSuccess(List<PlayerDataEntity> result) {
display.setRowData(0, result);
((CellList<PlayerDataEntity>) display).redraw();
}

Related

Unable to get List from response.body() from GET Retrofit Android

I am trying to get List from GET request with retrofit. But whenever I try to access the value from the list value from the response.body() (which is successful to retrieve the data from database),the error is always index out of bound, which the List returns null.
I am trying to populate my RecycleView with all the data from the List. However it always return null and hence my RecycleView is always empty.
Below is the code for my service class:
#GET("products/getallproducts") Call<List<Product>> getAllProducts();
Class for retrieving my List and populate the RecycleView:
public class HomeActivity extends AppCompatActivity {
List<Product> proGet = new ArrayList<Product>();
//some more codes here
//some more codes here
//some more codes here
//lastly followed by the method createDummyData()
public void createDummyData() {
Call<List<Product>> call = restServices.getService().getAllProducts();
call.enqueue(new Callback<List<Product>>() {
#Override
public void onResponse(Call<List<Product>> call, Response<List<Product>> response) {
int statusCode = response.code();
productGet = response.body();
//^^^this is where the problem is
//class used for populating one section
SectionDataModel dm = new SectionDataModel();
dm.setHeaderTitle("trend");
//class SingleItemModel is for adding single item into the section
ArrayList<SingleItemModel> singleItem = new ArrayList<SingleItemModel>();
//adding each item into the SingleItem array list
singleItem.add(new SingleItemModel(proGet.get(0).status, productList.get(0).category));
singleItem.add(new SingleItemModel(proGet.get(0).name, productList.get(0).productid));
//populate the singleItem array list into one section
dm.setAllItemsInSection(singleItem);
//populate the whole RecycleView
allSampleData.add(dm);
}
#Override
public void onFailure(Call<List<Product>> call, Throwable t) {
}
});
}
`
Error in logcat as such:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.kennethlo.kickgoo/com.example.kennethlo.kickgoo.HomeActivity}: java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0
I am desperate and hoping someone able to help. Thanks in advance.
First need to check your response size is greater than 0 (Zero).
if(productGet.size() > 0){
//Code...
}
And also check the size of productList is greate than 0 (Zero).

Strange TableView.itemsProperty Behavior?

Let's say I'll add a ChangeListener to a TableView's itemsProperty. When would the ChangeListener's changed method be called?
I tried adding to the empty List where the TableView's items points. The result - The ChangeListener's changed method didn't get called.
tableView.itemsProperty().addListener(new ChangeListener() {
#Override
public void changed(ObservableValue ov, Object t, Object t1) {
System.out.println("Changed!");
}
});
final ObservableList data = FXCollections.observableArrayList(new ArrayList());
data.clear();
//data.add(new Object()); don't call this yet
tableView.setItems(data);
data.clear();
data.add(new Object());
tableView.setItems(data);
However, I also tried adding to an empty List and then let TableView's items point on it. The result - The ChangeListener's changed method got called.
tableView.itemsProperty().addListener(new ChangeListener() {
#Override
public void changed(ObservableValue ov, Object t, Object t1) {
System.out.println("Changed!");
}
});
final ObservableList data = FXCollections.observableArrayList(new ArrayList());
data.clear();
data.add(new Object());
tableView.setItems(data);
data.clear();
data.add(new Object());
tableView.setItems(data);
I looked it up on http://docs.oracle.com/javafx/2/api/javafx/scene/control/TableView.html#itemsProperty() but it only says "The underlying data model for the TableView. Note that it has a generic type that must match the type of the TableView itself."
I'm asking this because I might miss out on some other important circumstances.
A not fully documented fact (aka: implementation detail) is that ObjectProperty only fires on
!oldValue.equals(newValue); // modulo null checking
That's critical for a list-valued object property, as lists are specified to be equal if all their elements are equal. In particular, all empty lists are equal to each other, thus replacing one empty list by another empty list as in your first snippet will not make the property fire:
// items empty initially
TableView table = new TableView()
table.itemsProperty().addListener(....)
ObservableList empty = FXCollections.observableArrayList();
// replace initial empty list by new empty list
table.setItems(empty);
// no change event was fired!
That's nasty if your code wants to listen to changes of the content - it would need to re-wire the ListChangeListeners whenever the identity of the items value changes but can't with a changeListener because that fires based on equality. BTW, even fx-internal code got that wrong and hot-fixed by a dirty hack
And no nice solution available, just a couple of suboptimal options
use an InvalidationListener instead of a changeListener
bind (unidirectionally!) a ListProperty to the list-valued object property and listen to the latter
use an adapter that combines the above to at least have it out off the way
A code snippet I use:
public static <T> ListProperty<T> listProperty(final Property<ObservableList<T>> property) {
Objects.requireNonNull(property, "property must not be null");
ListProperty<T> adapter = new ListPropertyBase<T>() {
// PENDING JW: need weakListener?
private InvalidationListener hack15793;
{
Bindings.bindBidirectional(this, property);
hack15793 = o -> {
ObservableList<T> newItems =property.getValue();
ObservableList<T> oldItems = get();
// force rewiring to new list if equals
boolean changedEquals = (newItems != null) && (oldItems != null)
&& newItems.equals(oldItems);
if (changedEquals) {
set(newItems);
}
};
property.addListener(hack15793);
}
#Override
public Object getBean() {
return null; // virtual property, no bean
}
#Override
public String getName() {
return property.getName();
}
#Override
protected void finalize() throws Throwable {
try {
Bindings.unbindBidirectional(property, this);
property.removeListener(hack15793);
} finally {
super.finalize();
}
}
};
return adapter;
}
TableView does not have implemented the method add view documentation
My aproach is the following:
To initialize the TableView itemList:
tableX.setItems(itemListX);
you could also initialize it by using the default list of the TableView:
tableX.getItems.addAll(itemListX);
in this case it will copy the list.
And the aproach to add items dynamically:
1-If you still have a reference to itemListX:
itemListX.add(item);
this you will update the TableView since the table observes the ObservableList itemListX.
2-Else, if you dont any more:
tableX.getItems().add(item);

Binding data to the UI in GWT

In Silverlight, a frequently used pattern is:
Request data
Get back an empty container for the data
Asynchronously fire off a query to fill the container
When the query returns, fire an event on the container
Update the UI according to the container's contents
Can this be done in GWT?
The reason I ask is that I'm trying to make a SuggestBox that contains a list of group names and icons. First, I query Facebook to get a list of groups IDs that are close to the current String in the SuggestBox. Then, I fire off queries to get icons for each group id. The problem is that I have to return the suggestions before those queries are done. I'm not sure how to go back and insert the data after I have it. I don't want to block until the calls are complete, and there's no real way to know in advance what data to load.
I could return a widget for the suggestion that loads an image, but the suggestion must be a plain String.
What is the right approach here?
Let's assume you're using GWT RPC. You'll have some service interface that lets you fetch the groupIds for a suggestion and the icon for a specific group id.
public interface FacebookService extends RemoteService {
List<String> getFacebookGroupIds(String suggestion);
Icon getIconForGroup(String groupId);
}
You should build your own implementation of Suggestion that can display itself with either just a groupId or a groupId and an Icon.
public class FacebookGroupSuggestion implements Suggestion {
private String groupId;
private Icon icon;
public FacebookGroupSuggestion(String groupId) {
this.groupId = groupId;
}
public String getDisplayString() {
StringBuilder builder = new StringBuilder();
builder.append("<b>");
builder.append(this.groupId);
builder.append("</b>");
if (this.icon != null) {
builder.append(this.icon.toSafeHtml());
}
return builder.toString();
}
}
I'm using Icon as your own implementation of an icon, it's not a standard class.
Then, you can make your implementation of SuggestOracle to fetch the groupIds and icons asynchronously. The SuggestOracle uses a callback to inform the suggestBox that some response to a request is available. So fetch your results, and call the callback when you get them. It'll look something like this.
public class FacebookSuggestOracle extends SuggestOracle {
private FacebookServiceAsync service = GWT.create(FacebookService.class);
private Request currentRequest;
private Callback currentCallback;
#Override
public void requestSuggestions(Request request, Callback callback) {
// Save request & callback for future use.
this.currentRequest = request;
this.currentCallback = callback;
// Fetch the groupIds
service.getFacebookGroupIds(request.getQuery(), new AsyncCallback<List<String>>() {
public void onSuccess(List<String> result) {
createSuggestionsForGroupIds(result);
}
});
}
private void createSuggestionsForGroupIds(List<String> groupIds) {
List<FacebookGroupSuggestion> suggestions = new ArrayList<FacebookGroupSuggestion>();
for (String groupId : groupIds) {
suggestions.add(new FacebookGroupSuggestion(groupId));
}
Response response = new Response(suggestions);
// Tell the suggestBox to display some new suggestions
currentCallback.onSuggestionsReady(currentRequest, response);
// Fetch the icons
for (String groupId : groupIds) {
service.getIconForGroup(groupId, new AsyncCallback<Icon>() {
public void onSuccess(Icon result) {
// match the icon to the groupId in the suggestion list
// use the callback again to tell the display to update itself
}
});
}
}
}

In Wicket, why isn't my results refreshing after form submission?

I'm struggling with a very basic Wicket issue. I'm trying to query a backend database, but can't get the results to display. Below is the code I'm using. currentQuery and currentResult is correctly updated after submission, but my SearchResults class is never rerendered with the new data in currentResults. I suppose that the results class just doesn't notice that the model has in fact been updated. I've been experimenting with modelChanged, but can't get it to work. I'm a bit new to Wicket, so I'm probably doing something fundamental completely wrong. Any help is much appreciated!
public class SearchPage extends WebPage {
Query currentQuery = new Query();
Result currentResult = new Result();
public SearchPage() {
add(new SearchForm("searchForm", new CompoundPropertyModel<Query>(currentQuery)));
add(new SearchResults("searchResults", new PropertyModel<List<Hit>>(currentResult, "hits")));
}
public void doSearch(Query Query) {
currentResult = getResults(query);
}
public class SearchForm extends Form<Query> {
public SearchForm(String id, CompoundPropertyModel<Query> model) {
super(id, model);
add(new TextField<String>("query"));
}
protected void onSubmit() {
super.onSubmit();
doSearch(currentQuery);
}
}
public class SearchResults extends WebMarkupContainer {
public SearchResults(String id, PropertyModel<List<Hit>> model) {
super(id, model);
add(new ListView<Hit>("hit", model) {
protected void populateItem(ListItem<Hit> item) {
item.add(new Label("column", item.getModelObject().getColumnValue("column")));
}
});
}
}
}
PropertyModel uses reflection to look up the named property on a given target object instance. When you constructed the PropertyModel, you passed it a specific instance of Result, i.e. the new Result() from SearchPage's constructor. The PropertyModel will continue to hold a reference to that same Result instance from render to render of this page, serializing the Result at the end and then deserializing the Result at the start of each new request cycle (page view). The fact that you later change the page's currentResult variable to reference a different Result instance does not affect which Result instance the PropertyModel uses to look up its model value. Your PropertyModel does not care what currentResult later refers to.
There are two possible solutions that I can think of off the top of my head.
Have the PropertyModel read hits from the actual current value of the Page's currentResult variable:
new PropertyModel<List<Hit>>(SearchPage.this, "currentResult.hits")
Use a LoadableDetachableModel to load hits once per request cycle/page view:
new LoadableDetachableModel<List<Hit>>()
{
protected Object load()
{
return getResults(currentQuery);
}
}
Note that a LoadableDetachableModel has to be detached at the end of the request cycle or it will never again call getObject() to recalculate the List<Hit>. That said, since your code shows you'd be using it as the default model of the SearchResults component, the SearchResults component would detach the model for you at the end of the request cycle automatically.
I got it working. This seems to be the offending row:
add(new SearchResults("searchResults", new PropertyModel<List<Hit>>(currentResult, "hits")));
The type of the PropertyModel, i.e. List<Hit>, must have been making the model static. So the only data SearchResults ever saw was the initial object, which was empty.
I changed the line to the below, and updated SearchResult accordingly.
add(new SearchResults("searchResults", new Model<Result>(currentResult, "hits")));
If anyone can explain this further, or feel that I'm incorrect, please comment! In any case, I'm marking my own answer as correct as this solved the problem.

How to get an EasyMock mock to return an empty list multiple times

I would like an EasyMock mock to be able to expect an empty list multiple times, even when the list that is returned the first time has elements added to it.
Is this possible? As the empty list created in the expectation persists for the whole replay and so retains any elements added to it in between calls.
Here is a code example showing what I'm trying to avoid:
public class FakeTest {
private interface Blah {
public List<String> getStuff();
};
#Test
public void theTest(){
Blah blah = EasyMock.createMock(Blah.class);
//Whenever you call getStuff() an empty list should be returned
EasyMock.expect(blah.getStuff()).andReturn(new ArrayList<String>()).anyTimes();
EasyMock.replay(blah);
//should be an empty list
List<String> returnedList = blah.getStuff();
System.out.println(returnedList);
//add something to the list
returnedList.add("SomeString");
System.out.println(returnedList);
//reinitialise the list with what we hope is an empty list
returnedList = blah.getStuff();
//it still contains the added element
System.out.println(returnedList);
EasyMock.verify(blah);
}
}
You can use andStubReturn to generate a new list each time.
//Whenever you call getStuff() an empty list should be returned
EasyMock.expect(blah.getStuff()).andStubAnswer(new IAnswer<List<String>>() {
#Override
public List<Object> answer() throws Throwable {
return new ArrayList<String>();
}
}

Categories