uno.extensions
uno.extensions copied to clipboard
Refreshable ListFeed PaginatedByCursorAsync
What would you like to be added
- [Critcal] An ability to manually refresh the ListFeed.PaginatedByCursorAsync mechanism. This should re-run the GetPage function to get new data. This should cause any bound ListViews to clear current items and display new items. This should cause any bound FeedViews to re-check the ListFeed to determine which FeedView state to render. This can use Signals and have signature as follows:
ListFeed<T>.PaginatedByCursorAsync<TCursor>(TCursor firstPage, GetPage<TCursor, T> getPage, Signal? refresh = null);
- [Quality of Life] Automatically refresh ListFeed.PaginatedByCursorAsync when a depended on IFeed changes. An example of possible usage is attached at the end of the issue. This technique is already used in non-paginated types of ListFeed, example below:
public partial record PeopleModel(IPeopleService PeopleService)
{
public IState<uint> CurrentPage => State.Value(this, () => 1u);
public IListFeed<Person> PeopleManual =>
CurrentPage
.SelectAsync(async (currentPage, ct) =>
await PeopleService.GetPeopleAsync(
pageSize: DefaultPageSize,
// currentPage argument as index based - subtracting 1
firstItemIndex: (currentPage - 1) * DefaultPageSize, ct))
.AsListFeed();
}
(source: https://platform.uno/docs/articles/external/uno.extensions/doc/Learn/Mvux/Advanced/Pagination.html#model-1)
Why is this needed
Using AsyncPaginated at the moment is very restrictive because:
- It does not allow for changing filter or query requirements.
- The GetPage function must be ready to run the first time the FeedView is requested. This is not always possible, Example: The GetPage function could depend on a variable which is only set at a later time.
For which platform
No response
Anything else we need to know?
Using the automatic refresh triggered by changes in IState
public record PeopleFilter { /*...*/ }
public partial record PeopleModelAuto(IPeopleService PeopleService)
{
/// <summary> Changing this from the UI automatically refreshes <see cref="PeoplePaginatedAuto"/> </summary>
public IState<PeopleFilter> ResultFilter => State.Value(this, () => PeopleFilter.None);
public IListFeed<Person> PeoplePaginatedAuto =>
ResultFilter
.InPaginatedByCursorAsync(
// Cursor starts as null because no people have been fetched yet.
firstPage: default(PersonId?),
// This will be automatically invoked by the ISupportIncrementalLoading the ListView supports
getPage: async (cursor, desiredPageSize, filter, ct) =>
{
var result = await PeopleService.GetPeopleAsync(cursor, desiredPageSize ?? DefaultPageSize, filter, ct);
return new PageResult<PersonId?, Person>(result.Value, result.NextCursor);
}
);
}
Using the manual refresh option by raising the signal.
public partial record PeopleModelManual(IPeopleService PeopleService)
{
public IState<PeopleFilter> ResultFilter => State.Value(this, () => PeopleFilter.None);
/// <summary> Pressing a button on the UI refreshes <see cref="PeoplePaginatedManual"/>. </summary>
public void RefreshPeople() => _refreshPeople.Raise();
private Signal _refreshPeople = new();
public IListFeed<Person> PeoplePaginatedManual =>
ListFeed.PaginatedByCursorAsync(
// Cursor starts as null because no people have been fetched yet.
firstPage: default(PersonId?),
// This will be automatically invoked by the ISupportIncrementalLoading the ListView supports
getPage: async (cursor, desiredPageSize, filter, ct) =>
{
var result = await PeopleService.GetPeopleAsync(cursor, desiredPageSize ?? DefaultPageSize, filter, ct);
return new PageResult<PersonId?, Person>(result.Value, result.NextCursor);
},
// This will cause the bound views to discard current items and refetch new items.
refresh: _refreshPeople
);
}
Hi @CalebSerafin, thanks for the report.
About your case 1.:
Are you aware that you can use the Refresh command on the FeedView to allow user to trigger a refresh? You can also use it with a RefreshContainer (a.k.a. pull to refresh). This works with any Feed of ListFeed, and in case of a paginated source, this will reset the index/cursor at the beginning and reload all pages.
For your case 2:
There are 2 operators that are not yet documented (https://github.com/unoplatform/uno.extensions/issues/2135), the SelectPaginatedAsync and SelectPaginateByCursorAsync that are equivalent to your InPaginatedByCursorAsync operator. This allows you to have what we call "paginated list with parameters" (if your need multiple parameters, you can use Feed.Combine). As soon as the parameter changes, like for the refresh, the index/cursor is reset and pagination reload all pages.
Does this cover all your cases ?
BTW We are trying to reduce the usage of the Signal as this is usually driven by imperative code and most of the time only highlights a dependency that is not "reactive" as it should.
Hi @dr1rrb 👋. 2. Yes that is perfect! I don't know how I didn't see that extension method while searching 🙈.
- The suggestion for Signal was to adapt from non-reactive code to reactive. But since the extension method for IFeeds exists, then it won't be needed for PaginatedByCursorAsync / PaginatedAsync.
Thank you for your help 🌟. I'll close this issue shortly.
Question: Is anyone working on adding an explanation & example to the MVUX documentation? If not I'll open another issue and PR to contribute.
We are continuously trying to improve documentation, it's never ending task, but all contributions are welcome :)
@CalebSerafin It would be awesome if you could submit a PR!!