-
Notifications
You must be signed in to change notification settings - Fork 2
Home
Welcome to the reactivity wiki! Reactivity is a port of GWTP, the final goal is to make it a reactive friendly MVP library, but before that, I want to clean up some old APIs and avoid the necessity of code generation (as much as possible 😉).
There is an important premise, this lib depends directly on Dagger (IoC) and RxJava (Async). I hope (in my projects it's true 😬) this will simplify the library implementation and usage.
- Dagger is used because IoC is almost mandatory for big projects and dagger 2 uses code generation which is GWT friendly.
- RxJava is used because I really think that presenters behaviors definitions are much richer and descriptive using reactive code like the one RxJava exposes. Also, until you get forced to use RxJava, you can unify all callbacks strategies using RxJava (like browser events, widgets events, requests, timers… anything that responds with callback, promises or any kind of async response), and this end up being super handy because you can finally mix all those async (or maybe sync but callback based, yep this happens a lot too). So just to be clear, DO NOT EVER create a functional interface if you are going to use reactivity, all callback/async action should be a RxJava type.
This project is also a personal investigation of front-end frameworks. Here I'm trying to categorize the building materials that common front-end frameworks or libs have in common. I'll try also to compare different strategies and finally explain the motivation on this project to use each one.
- Components - every solution has components, why? in the end, a UI is just a VIEW, but a so complex view that it should be divided (so, this division should be composed to form the final unique VIEW), there is a pretty proven pattern called composite patter that solve this issue, so components are the common denominator for all UIs:
- HTML - elements can be considered a component, even more after the web-components standard
- GWT - widgets are the GWT component for building the UI, each widget is associated with an HTML element
- GWTP - use multi-class component Presenter-View-Proxy-Place, but we can focus on the Presenter as the main representative of the component. Each Presenter requires a View and each View is associated with a GWT Widget and this widget with an HTML element.
- React - component?
- Vue - component?
- Angular - component?
- Dependency injection - not critical, but almost always available because we build the UI as a composition of components, these components depend on other tools, DI remove a lot of boilerplate to manage the construction of those components, so any front-end framework should include some DI to manage construction and common utils.
- Routing - UI is like a giant state machine, you can move through states (aka. places, intentions, urls), routing is a common solution to map navigation to some part of the hierarchy of components. Routing can be applied to the UI component hierarchy from bottom-up or top-down. Bottom-up imply a direct update, i.e. when the routing notifies a route change, you can just set a different component in the parent component. From top-down means that when a route changes, a component is asked to be revealed, and this component recursively call its parent until a root (aka. atacched) is reached, revealing the whole hiearchy.
- Slots - is a technique to facilitate to assign components inside other components (is a decoupling technique)
In general, the idea is exactly the same. There are Places that reveal Presenters which has a View. The Presenter may have a parent or may reveal a child in a slot.
This has a lot of logic in GWTP, it was generated and is responsible for the async loading of the presenter and to bind events that should listen before the presenter is loaded. The first thing I realized was that it has part of the place reveal logic, all this logic should be in the PlaceManager so I move it to the PlaceManager. After some refactoring the Proxy end up as an async wrapper for the presenter, with the premises of this project this means Single<P extends Presenter>
.
Proxy events are discouraged. This is because proxies are for code splitting (without code splitting makes no sense), and proxy events wake up the place. This IMO is not useful, for example, GWT nested example shows how to use proxy events to capture navigations events so the presenter can accumulate all navigations changes and show the history of changes when the presenter is revealed. BUT, if you do that this awakes the presenter on any change so in practice this is equivalent to not having code splitting or to have the presenter configured as an eager singleton (no code splitting either). In both cases, this kind of proxy event just brokes the proxy purpose, so it NOT justifies its own existence. Another common use case is to actually reveal the presenter, maybe because you want to fire an event to create a new user, so the user edit presenter listen to this event, and should do it using proxy event because pretty probably the even is not revealed when the event is fired, but at that point, why don't you just use the place request which is actually an event that its unique purpose is to reveal a presenter. Those are, in my opinion, the main purpose of using proxy events and those are also the justification and alternative to not to use them.
If all of this doesn't convince you, you can implement the same behavior using the now unified Place
type which has the responsibility of waking up the presenter and it behaves as an eager singleton (I said behaves as because eager singletons don't exist in dagger). You can do something like this (equivalent to the GWTP nested example).
public static @Singleton class MyPlace extends Place {
@Inject MyPlace(Single<ContactPresenter> p, EventBus bus) {
super(NameTokens.contactPage, p); // this p.subscribe wait until code split has loaded
bus.addHandler(PlaceManager.NavigationEvent.TYPE, event -> p.subscribe(ready -> {
// We keep track of the previously visited pages
if (ready.navigationHistory.length() > 0) ready.navigationHistory += ", ";
ready.navigationHistory += event.getRequest().getNameToken();
ready.getView().setNavigationHistory(ready.navigationHistory);
}));
}
}
In GWTP this is quite coupled with Proxy
, it was auto-generated from the annotations of the Proxy. I want to remove the GWT generators, and in this process, I concluded that the APT is not needed either. So now instead of an annotation you just need to instantiate a Place
which behave as a proxy too (see more in the code splitting section).
public static class MyPlace extends Place {
@Inject MyPlace(Provider<Presenters> p) { super(NameTokens.homePage, p); }
}
UiHandlers to the view with the presenter makes no sense IMO. This lib depends on RxJava, so if you need a back-communication between the view and the presenter you just need to expose an Observable
in the View. This ends up being even better bc this encourage an event best-practice API instead of a command handler. I.e. instead of void UiHandler.goEditUser(User user)
method you will expose the Observable<User> View.rowClicked()
method.
It was a good solution, but after some refactoring, I realized that it was not needed anymore. PlaceRequestInternalEvent
was used to find the place to be revealed, all proxies subscribe to it and reveal itself if matches the with the PlaceRequest
. Now all the logic is in the PlaceManager
and this includes the list of all available Places
, so now the PlaceManager
iterate over all available the places and check if it matches, no event bus involved.