GWT Async DataProvider always jumping to celtable' first page - gwt

I have a simple celltable with:
a)A timer for retrieving the Whole list of values from the server (via rpc). when the data comes from the server:
public void onSuccess(final Object result) {
myList ((List<myObject>) result);
setRowData(myList);
}
b)A AsyncDataProvider to refresh the current displayed page, where:
protected void onRangeChanged(HasData<myObject> display) {
final Range range = display.getVisibleRange();
int start = range.getStart();
int end = start + range.getLength();
List<myObject> dataInRange = myList.subList(start, end);
// Push the data back into the list.
setRowData(start, dataInRange);
}
This works fine, refreshing the table when new data arrives from the server ..BUT....It jumps to the first page of my displayed table regardless he current page (Page size=20).
It is like it is ignoring the start and dataInRange values of the onRangeChanged method
the sentence:
setRowData(myList);
Is firing properly the onRangeChanged event of the DataProvider, but some how the 'start' values get 0
Any tip?
Many thanks

The problem is that when you call setRowData(myList); you also change the row range.
See the documentation:
Set the complete list of values to display on one page.
Equivalent to calling setRowCount(int) with the length of the list of values, setVisibleRange(Range) from 0 to the size of the list of values, and setRowData(int, List) with a start of 0 and the specified list of values.
Literally, below is the implementation:
public final void setRowData(List<? extends T> values) {
setRowCount(values.size());
setVisibleRange(0, values.size());
setRowData(0, values);
}

Related

Flutter list stores values using for loop, but loses these values when used outside the loop

I am trying to read data from a firebase real time database and store it in a list to use in my flutter app.
As seen in the code below, I start by creating a reference to the database. I also create some global variables, where "itemName" stores the name of the item in the database, "itemID" stores the id of each item in the database and "itemNames" is a list of all the item names in the database.
The "activate listeners" method listens to the database, and returns any values if they are changed. Each item ID starts with a J, and continues onto J1, J2, J3 etc. Hence I am using a for loop to access all the item IDs.
The issue I am having is that the itemNames are successfully being stored in the itemNames list, and can be see when I print the list within the for loop (The first print line).
However, when I try print the list value OUTSIDE the for loop, it prints an empty list for loop (second print line).
So in other words, the list is not retaining the elements added to it during the for loop.
Any help would be much appreciated!
final DatabaseReference _dbRef = FirebaseDatabase.instance.ref();
late StreamSubscription _dailySpecialStream;
//Stores the description of each menu item in the DB
String itemName = "";
String itemID = "";
List<String> itemNames = [];
//"Listens for any changes being made to the DB, and updates our app in real time"
void _activateListeners() {
for (int i = 0; i <= 10; i++) {
itemID = "J$i";
_dailySpecialStream =
_dbRef.child("menuItem/$itemID/itemName").onValue.listen((event) {
itemName = event.snapshot.value.toString();
itemNames.addAll([itemName]);
print(itemNames);
});
}
print(itemNames);
}
That is the expected behavior. Data is loaded from Firebase (and most modern cloud APIs) asynchronously, and while that is happening your main code continues to run.
You can most easily see this by placing some print statements:
print('before starting to load data');
for (int i = 0; i <= 10; i++) {
itemID = "J$i";
_dailySpecialStream =
_dbRef.child("menuItem/$itemID/itemName").onValue.listen((event) {
print('loaded data: %i');
});
}
print('after starting to load data');
If you run this, you'll see something like:
before starting to load data
after starting to load data
loaded data: 0
loaded data: 1
loaded data: 2
loaded data: 3
...
So as you can see the after print statement that is lowest in your code, actually printed before any of the data was loaded. This is probably not what you expected, but explains perfectly why the print statement you had outside the loop doesn't print the data: it hasn't been loaded yet!
The solution for this type of problem is always the same: you have to make sure the code that requires the data is inside the callback, or it is called from there, or it is otherwise synchronized.
A simple way to do the latter is by using get() instead of onValue, and then use await on the Future that is returns:
print('before starting to load data');
for (int i = 0; i <= 10; i++) {
itemID = "J$i";
_dailySpecial = await _dbRef.child("menuItem/$itemID/itemName").get();
print('loaded data %i: ${_dailySpecial.value}');
}
print('after starting to load data');
Now with this, the print statements will be in the order you expected.

Flutter : How to Map values dynmically into list and iterate to its length and how can i do Update and Delete after maping values

I am trying to build json expected output as below through dart programing for my application, I have mapped data to list successfully. But when I trying to delete / add the list, the elements in the list are not getting updated accordingly instead they are hgetting reapeted same data.
Here is my code implimentation
in this set state i am getting required values like Phone, name, email e.t.c
for (int i = 0; i <= selectedContacts.length - 1; i++) {
SimplifiedContact? contact = selectedContacts.elementAt(i);
CreateContestModel(//<-- from here i am getting required values.
contact.phone,
contact.name,
contact.email,
);
}
in below code i am, mapping data and building json
class CreateContestModel {
static List models = [];
String phone = '';
String name = '';
String email;
CreateContestModel(this.phone, this.name, this.email) {
var invitemap = {
'name': name,
'phone': phone,
'email': email,
};
models.add(invitemap);
print(models);
}
}
Output
{
"invitations":[
{
"name":"Acevedo Castro",
"phone":982-475-2009,
"email":"floresjoyner#digifad.com"
},
{
"name":"Acevedo Castro",
"phone":982-475-2009,
"email":"floresjoyner#digifad.com"
},
{
"name":"Abby Webster",
"phone":888-561-2141,
"email":"howardnoel#perkle.com"
},
{
"name":"Abby Webster",
"phone":888-561-2141,
"email":"howardnoel#perkle.com"
},
{
"name":"Abby Webster",
"phone":888-561-2141,
"email":"howardnoel#perkle.com"
}
]
}
As you see above items are not getting updated, but they are getting added more.
Expected Output
{
"invitations":[
{
"name":"Acevedo Castro",
"phone":"982-475-2009",
"email":"floresjoyner#digifad.com"
},
{
"name":"Abby Webster",
"phone":"888-561-2141",
"email":"howardnoel#perkle.com"
}
]
}
That is some seriously flawed program flow. Your object creation as a side effect at the same time fills a static list. And it seems you call your object creation every build. So you would insert into your list whenver the user flips it's phone or drags his browser window.
I'm not entirely sure what you want, but you need state management in your application. There are different ways to do it, you can pick the one you like best from the Flutter documentation on state management.
This is a huge flow issue, You should never do data manipulation in build() function as in flutter build function is called multiple times so the manipulation code will also get called multiple times.
So, the proper way to manipulate data is before the data is being used so make sure that the data is only manipulated in initstate(). In Your case you are also doing something which is not required. You are trying to add data to a list via a constructor to a static list so it will always add the data whenever you call it, This is not a proper way.
class CreateContestModel {
late String phone = '';
late String name = '';
late String email;
CreateContestModel.fromMap(Map<String, String> json) {
phone = json['phone'] ?? '';
phone = json['name'] ?? '';
phone = json['email'] ?? '';
}
}
This is how you should create your class. And always create functions for manipulation if possible.
List<CreateContestModel> createContestModelList =
testData['invitations']!.map(
(data) {
return CreateContestModel.fromMap(data);
},
).toList();
Then use this code to construct your list in initstate() and manipulate this having a static variable in your stateful widget. Make Sure you do not construct the data on build() function.
Need More Help?? here's the Gist link
As per my question i find some work around, i.e., by clear up stack on selecting or updtaing existing contacts because as explained below
in this for loop i am itrating same values on each time, when i hit on submit button
for (int i = 0; i <= selectedContacts.length - 1; i++) { //<--- for every time same elements will be itrated
SimplifiedContact? contact = selectedContacts.elementAt(i);
CreateContestModel(//<-- from here i am getting required values.
contact.phone,
contact.name,
contact.email,
);
}
so the list of sets are not updating as per the displayed list as mentioned in question, so i done some work around in below code while selecting / updating contacts, because my model is static list
void onContactBtnPress(BuildContext context) async { CreateContestModel.models.clear(); //<-- clearing up stack
}

Manatee.Trello Moving Cards

I'm writing a small application to manage Trello Boards in only a few aspects such as sorting Cards on a List, moving/copying Cards based on Due Date and/or Labels, archiving Lists on a regular basis and generating reports based on Labels, etc. As such, I've been putting together a facade around the Manatee.Trello library to simplify the interface for my services.
I've been getting comfortable with the library and things have been relatively smooth. However, I wrote an extension method on the Card class to move Cards within or between Lists, and another method that calls this extension method repeatedly to move all Cards from one List to another.
My issue is that when running the code on a couple of dummy lists with 7 cards in one, it completes without error, but at least one card doesn't actually get moved (though as many as 3 cards have failed to move). I can't tell if this is because I'm moving things too rapidly, or if I need to adjust the TrelloConfiguration.ChangeSubmissionTime, or what. I've tried playing around with delays but it doesn't help.
Here is my calling code:
public void MoveCardsBetweenLists(
string originListName,
string destinationListName,
string originBoardName,
string destinationBoardName = null)
{
var fromBoard = GetBoard(originBoardName); // returns a Manatee.Trello.Board
var toBoard = destinationBoardName == null
|| destinationBoardName.Equals(originBoardName, StringComparison.OrdinalIgnoreCase)
? fromBoard
: GetBoard(destinationBoardName);
var fromList = GetListFromBoard(originListName, fromBoard); // returns a Manatee.Trello.List from the specified Board
var toList = GetListFromBoard(destinationListName, toBoard);
for (int i = 0; i < fromList.Cards.Count(); i++)
{
fromList.Cards[i].Move(1, toList);
}
}
Here is my extension method on Manatee.Trello.Card:
public static void Move(this Card card, int position, List list = null)
{
if (list != null && list != card.List)
{
card.List = list;
}
card.Position = position;
}
I've created a test that replicates the functionality you want. Basically, I create 7 cards on my board, move them to another list, then delete them (just to maintain initial state).
private static void Run(System.Action action)
{
var serializer = new ManateeSerializer();
TrelloConfiguration.Serializer = serializer;
TrelloConfiguration.Deserializer = serializer;
TrelloConfiguration.JsonFactory = new ManateeFactory();
TrelloConfiguration.RestClientProvider = new WebApiClientProvider();
TrelloAuthorization.Default.AppKey = TrelloIds.AppKey;
TrelloAuthorization.Default.UserToken = TrelloIds.UserToken;
action();
TrelloProcessor.Flush();
}
#region http://stackoverflow.com/q/39926431/878701
private static void Move(Card card, int position, List list = null)
{
if (list != null && list != card.List)
{
card.List = list;
}
card.Position = position;
}
[TestMethod]
public void MovingCards()
{
Run(() =>
{
var list = new List(TrelloIds.ListId);
var cards = new List<Card>();
for (int i = 0; i < 10; i++)
{
cards.Add(list.Cards.Add("test card " + i));
}
var otherList = list.Board.Lists.Last();
for(var i = 0; i < cards.Count; i++)
{
Move(card, i, otherList);
}
foreach (var card in cards)
{
card.Delete();
}
});
}
#endregion
Quick question: Are you calling TrelloProcessor.Flush() before your execution ends? If you don't, then some changes will likely remain in the request processor queue when the application ends, so they'll never be sent. See my wiki page on processing requests for more information.
Also, I've noticed that you're using 1 as the position for each move. By doing this, you'll end up with an unreliable ordering. The position data that Trello uses is floating point. To position a card between two other cards, it simply takes the average of the other cards. In your case, (if the destination list is empty), I'd suggest sending in the indexer variable for the ordering. If the destination list isn't empty, you'll need to calculate a new position based on the other cards in the list (by the averaging method Trello uses).
Finally, I like the extension code you have. If you have ideas that you think would be useful to add to the library, please feel free to fork the GitHub repo and create a pull request.

How to pass more than one record between two forms?

I want to pass more than one record between two forms. The user opens Form-A, selects multiple records and then clicks a button that opens Form-B.
In Form-B there are two (or more) StringEdit controls and they should display values from the selected records.
I know how to pass only one record, to do that I use the following code in a method of Form-B:
if (element.args().parmEnumType() == enumNum(NoYes)
&& element.args().parmEnum() == NoYes::Yes)
{
myTable = element.args().record();
stringEdit.text(myTable.Field);
}
How should I change my code so that I can set the text of another StringEdit control to the field value of the next record that the user selected?
To do this, you can use an args to pass the records, which you will need to prepare in your Form-A, as below (taking the SalesTable as example);
int recordsCount;
SalesTable salesTable;
container con;
Args args = newArgs();
// gets the total records selected
recordsCount = salesTable_ds.recordsMarked().lastIndex();
salesTable = salesTable_ds.getFirst(1);
while(salesTable)
{
// storing recid of selected record in container
con = conIns(con,1, salesTable.RecId);
salesTable = SampleTable_ds.getNext(); // moves to next record
}
// passing container converted to string
args.parm(con2Str(con,','));
Then on your Form-B, you will need to override the init() method to read the args you created,
In order to retrieve passed arguments in the recipient from. Override the init() method of new form as shown
public void init()
{
container con;
int i;
super();
// string to container
con = str2con(element.args().parm(),'','');
// for sorting
for(i = 1;i<= conLen(con) ;i++)
{
salesTable_ds.query().dataSourceTable(Tablenum(SalesTable)).addRange(fieldNum(SalesTable,RecId)).value(SysQuery::value(conPeek(con,i)));
}
}
Hope it helps.
Taken from Ax-Forum
This will typically imply looping through the selected records in Form-A and changing the query in Form-B.
The idiomatic way to loop through selected records involves a for loop and the getFirst and getNext methods of the datasource:
SalesLine sl;
FormDataSourcs ds = _salesLine.dataSource();
for (sl = ds.getFirst(true) ? ds.getFirst(true) : ds.cursor(); sl; sl = ds.getNext())
{
//Do your thing, add a query range
}
The MultiSelectionHelper class may be of use here, as it does the thing:
public void init()
{
MultiSelectionHelper ms;
super();
if (element.args() && element.args().caller() && element.args().record())
{
this.query().dataSourceTable(tableNum(SalesLine)).clearDynalinks();
ms = MultiSelectionHelper::createFromCaller(element.args().caller());
ms.createQueryRanges(this.query().dataSourceTable(tablenum(SalesLine)), fieldstr(SalesLine, InventTransId));
}
}

GWT filler rows in a CellTable

I currently have a CellTable which I expanded with a filter/paging/sorting.
I am working on adding more features too.
One things that bugs me though, is when you set the limit for a page to be for example 10.
If an object doesn't have 10 rows and only has 5 and you switch between them then the table will jump up and down if positioned in the center.
Is there an elegant way, that when the pageSize is set to 10, then if there are less than 10 rows in a table, empty rows will be added?
The ways I have tried so far effect my filtering/sorting/paging, which I do not want.
EDIT 1:
final AsyncDataProvider<Row> provider = new AsyncDataProvider<Row>() {
#Override
protected void onRangeChanged(HasData<Row> display) {
int start = display.getVisibleRange().getStart();
int end = start + display.getVisibleRange().getLength();
end = end >= rows.getFilterList().size() ? rows.getFilterList().size() : end;
List<Row> sub = rows.getFilterList().subList(start, end);
System.out.println("Checking if extra rows needed: " +
table.getVisibleItems().size());
for(int i = table.getVisibleItems().size(); i<10; i++){
System.out.println("ADDED EMPTY ROW");
sub.add(new Row());
}
updateRowData(start, sub);
}
};
I had the same problem. I solved it by adding null rows. Basically, you need to override onRangeChange(HasData<T> display) in your DataProvider to add null to the list representing the range asked for. You add enough null to fill the range you want.
#Override
protected void onRangeChanged(HasData<T> display){
Range range = display.getVisibleRange();
List<T> listOfObjects = getRange(range);
for(int i=listOfObject.size(); i<range.getLength(); i++){
listOfObjects.add(null);
}
updateRowData(range.getStart(), listofObjects);
}
In the above code getRange() returns a list containing the objects that are requested (within the range)
You'll also have to add some CSS to give a height to rows. If you don't specify the height then the rows will be very small so they won't pad the whole table.
It's disappointing that GWT doesn't have support for this type of padding. This way was the best one that I found but unfortunately when your row heights differ within a table it makes everything harder.
Paging
With the solution above you'll need to have a custom Pager if you want paging. You can extend your AsyncDataProvider with method like getVisibleRowCount() this will return how many objects in the current sublist are actually real data (not null). Then you can give a reference of the AsyncDataProvider to the Pager and override the createText() method:
#Override
protected String createText(){
return getPageStart() + " - " + dataProvider.getVisibileRowCount() + " of "
+ dataProvider.getTotalRowCount();
}