i'm wondering if there's a way to build the gwt uibinder logic into an abstract parent class so that i don't have to repeat the code in every class i want to bind.
for example, i'd like to be able to do something like this:
public abstract class BasePanel<Panel extends BasePanel> extends Composite {
interface Binder<BinderPanel extends BasePanel> extends UiBinder<Widget, BinderPanel> { }
private static final Binder binder = GWT.create(Binder<Panel>.class);
public BasePanel() {
initWidget(binder.createAndBindUi(this));
init();
}
}
basically this would allow any child classes to do something like this:
public MyPanel extends BasePanel<MyPanel> {
//my code here
}
the default constructor would take care of all the code to bind MyPanel to MyPanel.ui.xml.
basically i want to be lazy and only have to build the interface and the binder once so that it's done in a common way. thoughts?
thanks in advance.
Proper way to do abstract UI binder classes is to define a super class that will contain logic that is common to all subclass widgets. This class can have fields marked as #UiField, event handlers and anything else that goes into the UI binder class. And child classes actually have UI binder instantiation logic. So something like this:
public abstract BaseWidget extends Composite {
#UiField TextBox textBoxCommon1;
#UiField TextBox textBoxCommon2;
#UiHandler("textBoxCommon1")
void onTextBoxCommon1Changed( ValueChangeEvent<String> event ) {
//...
}
#UiHandler("textBoxCommon2")
void onTextBoxCommon2Changed( ValueChangeEvent<String> event ) {
//...
}
}
public class SomeWidget extends BaseWidget {
interface SomeWidgetUiBinder extends UiBinder<Widget,SomeWidget> {}
private static SomeWidgetUiBinder uiBinder = GWT.create(SomeWidgetUiBinder.class);
#UiField Button someWidgetButton;
public SomeWidget() {
initWidget(uiBinder.createAndBindUi(this));
}
#UiHandler("someWidgetButton")
void onButtonClicked(ClickEvent e) {
Window.alert(textBoxCommon1.getValue());
}
}
Related
Learning Dagger2 and maybe going off the rails here. I have a class - MapRoute that may or may not be needed in a Fragment. If MapRoute is needed, I want to create it and when the MapRoute is instantiated I want to inject it with dependencies created at the application level. I am also using Builder pattern to populate MapRouter.
Perhaps the generic question is when you are in nonAndroid classes (not Activity/Fragment/...) how can you inject dependencies from above? How to you construct the nonAndroid injector in place of AndroidInjection.inject(this)?
So my fragment is:
public class ActivityMapFragment extends DaggerFragment ... {
#Inject
MapRoute.Builder mapRouteBuilder;
private void plotRouteForMap(Cursor csr){
MapRoute.Builder builder = new MapRoute.Builder();
builder.setSupportMapFragment(supportMapFragment)
.setLocationExerciseRecord(ler)
.setMapType(mapType)
.setUseCurrentLocationLabel(useCurrentLocationLabel)
.setCursor(csr)
.setTitle(activityTitle)
.setActivityPhotosCallback(this);
mapRoute = builder.build();
mapRoute.plotGpsRoute();
}
...
MapRoute is: (Edit) added Builder code snippet
public class MapRoute ... {
#Inject
public DisplayUnits displayUnits; <<< Created at app level
#Inject
public PhotoUtils photoUtils; <<<< Create at app level
public MapRoute() {
// Use subcomponent builder
MapRouteSubcomponent component =
DaggerMapRouteSubComponent.builder().build(); <<< Want to do this
component.inject(this);
}
public static class Builder {
SupportMapFragment supportMapFragment;
LocationExerciseRecord ler;
boolean useCurrentLocationLabel;
int mapType;
Cursor cursor;
ActivityPhotosCallback activityPhotosCallback;
String title;
#Inject
public Builder() {
}
public Builder setSupportMapFragment(SupportMapFragment supportMapFragment){
this.supportMapFragment = supportMapFragment;
return this;
}
....
MapRouteSubcomponent best guess:
#Subcomponent(modules = {MapRouteModule.class, ApplicationModule.class})
public interface MapRouteSubcomponent {
// allow to inject into our MapRoute class
void inject(MapRoute mapRoute);
#Subcomponent.Builder
interface Builder extends SubComponentBuilder<MapRouteSubcomponent> {
Builder mapRouteModule(MapRouteModule mapRouteModule);
}
#Module
public class MapRouteModule {
// What to put here?
}
And finally a subcomponent builder:
// from https://github.com/Zorail/SubComponent/blob/master/app/src/main/java/zorail/rohan/com/subcomponent/SubComponentBuilder.java
public interface SubComponentBuilder<V> {
V build();
}
At this point I am at a stand on where to go from here.
I am using dependency injection according to google sample
The only external dependency I can pass is through AppComponent builder
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
MainTabActivityModule.class,
CoreActivityModule.class
})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
AppComponent build();
}
void inject(MyApplication myApplication);
}
and injected in app like this
#Override
public void onCreate() {
super.onCreate();
DaggerAppComponent
.builder()
.application(myApplication)
.build().inject(myApplication);
...
}
According to document injecting in Activity looks like this. I added what I would like to achieve.
public class YourActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
AndroidInjection
//.builder() THIS IS WHAT I WANT TO ACHIEVE
//.addActivityContext(this) THIS IS WHAT I WANT TO ACHIEVE
//.build() THIS IS WHAT I WANT TO ACHIEVE
.inject(this);
super.onCreate(savedInstanceState);
}
}
but the question is how can I add additional parameter to subComponent.
#Subcomponent
public interface CoreActivitySubComponent extends AndroidInjector<CoreAppActivity> {
// #Subcomponent.Builder
// interface Builder {
// Builder addContext(Context context) //did not work
// CoreActivitySubComponent build(); //did not work
// }
//==or using abstract class
// in this option I do not know where to add parameter to this builder
#Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder<CoreAppActivity> {
}
}
Did you add the ContextModule to your #Subcomponent similar to this answer?
I think your #Subcomponent should look something like this:
#Subcomponent(module = {ContextModule.class})
interface MainTabActivityComponent extends AndroidInjector<CoreAppActivity> {
#Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder<CoreAppActivity> {
abstract Builder addContextModule(ContextModule contextModule);
#Override
public void seedInstance(CoreAppActivity instance) {
addContextModule(new ContextModule(instance));
}
}
}
And finally don't forget to include this #Subcomponent in your binding-module.
One last question: is this really required?
I found that using the AndroidInjector on the Application as well as Activities and Fragments will give me the correct corresponding Context when I inject it.
The problem was that Dagger 2.1.0 method AndroidInjection.inject(this); which is supposed to be used in Activity and Fragment, do not provide any builder to add external dependency.
I wanted to create general module which depends on Activity/Fragment context.
sample:
public class ToastController {
private Context context;
#Inject
public ToastController(Context context) {
this.context = context;
}
public void showToast(#StringRes int res) {
Toast.makeText(context, context.getText(res), Toast.LENGTH_SHORT).show();;
}
}
But I was not able to generalize it to the level, that I could provide just one context modude, instead I had to do create binds module for every single Activity/ Fragment that uses this module.
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
MainTabActivityModule.class,// IMPORTANT CLASS
})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
AppComponent build();
}
void inject(MyApplication myApplication);
}
This is a place, where I provide context module for each Activity
#Module
public abstract class MainTabActivityModule
#ContributesAndroidInjector(modules = ContextMainTabActivityModule.class)//THIS MODULE
abstract MainTabActivity contributeMainActivity();
}
and Context is provided using #Binds annotation
#Module
public abstract class ContextMainTabActivityModule {
#Binds
abstract Context provideContext(MainTabActivity featureActivity);
}
=====================
It can be done by overriding method seedInstance according to sample
I tried this, but it did not work for me
#Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder<CoreAppActivity> {
abstract Builder addContextModule(ContextModule contextModule);
#Override
public void seedInstance(CoreAppActivity instance) {
addContextModule(new ContextModule(instance));
}
}
next class
#Module
public class ContextModule {
private CoreAppActivity coreAppActivity;
#Provides
Context getContext() {
return coreAppActivity.getBaseContext();
}
public ContextModule(CoreAppActivity coreAppActivity) {
this.coreAppActivity = coreAppActivity;
}
}
building on this answer, i try to integrate the GWT editors into a popup presenter widget. What is the right way to do that?
My view looks like this:
public class DeviceEditorDialogView extends
PopupViewWithUiHandlers<DeviceEditorDialogUiHandlers> implements
DeviceEditorDialogPresenterWidget.MyView {
interface Binder extends UiBinder<PopupPanel, DeviceEditorDialogView> {
}
public interface Driver extends SimpleBeanEditorDriver<DeviceDto, DeviceEditorDialogView> {
}
#Inject
DeviceEditorDialogView(Binder uiBinder, EventBus eventBus) {
super(eventBus);
initWidget(uiBinder.createAndBindUi(this));
}
#Override
public SimpleBeanEditorDriver<DeviceDto, ?> createEditorDriver() {
Driver driver = GWT.create(Driver.class);
driver.initialize(this);
return driver;
}
}
and my presenter looks like this:
public class DeviceEditorDialogPresenterWidget extends PresenterWidget<DeviceEditorDialogPresenterWidget.MyView> implements
DeviceEditorDialogUiHandlers {
#Inject
DeviceEditorDialogPresenterWidget(EventBus eventBus,
MyView view) {
super(eventBus, view);
getView().setUiHandlers(this);
}
/**
* {#link LocalDialogPresenterWidget}'s PopupView.
*/
public interface MyView extends PopupView, DevicesEditView<DeviceDto>, HasUiHandlers<DeviceEditorDialogUiHandlers> {
}
private DeviceDto currentDeviceDTO = null;
private SimpleBeanEditorDriver<DeviceDto, ?> driver;
public DeviceDto getCurrentDeviceDTO() {
return currentDeviceDTO;
}
public void setCurrentDeviceDTO(DeviceDto currentDeviceDTO) {
this.currentDeviceDTO = currentDeviceDTO;
}
#Override
protected void onBind() {
super.onBind();
driver = getView().createEditorDriver();
}
//UiHandler Method: Person person = driver.flush();
}
Is this the right approach? What is missing? Currently nothing happens when i try to use it like this:
#Override
public void showDeviceDialog() {
deviceEditorDialog.setCurrentDeviceDTO(new DeviceDto());
addToPopupSlot(deviceEditorDialog);
}
showDeviceDialog is in the parent presenter and called when clicking a button in that parent Presenter, that instantiates the dialog with private final DeviceEditorDialogPresenterWidget deviceEditorDialog;
Thanks!
Here are a few key points that are missing from your code above:
Your DeviceEditorDialogView should implement Editor<DeviceDto>. This is required in order for the fields of DeviceEditorDialogView to be populated with data from you POJO.
Your DeviceEditorDialogView should have child editors that are mapped to fields in your POJO. For example, given the field deviceDto.modelName (type String), you could have a GWT Label named modelName in your DeviceEditorDialogView. This Label implements Editor<String> and will be populated with the modelName from your DeviceDto when you call driver.edit(deviceDto)
You should call driver.initialize(this) only once, in DeviceEditorDialogView's constructor
You should override onReveal() like this:
#Override
public void onReveal() {
super.onReveal();
driver.edit(currentDeviceDTO); // this will populate your view with the data from your POJO
}
This method will be called when the popup is displayed, just after your DeviceEditorDialogPresenterWidget has been addToPopupSlot
Firstly, is doing such thing a good practice ?
I tried what seems to be the right way for me but wasn't successful :
public class FormViewImpl extends CompositeView implements HasUiHandlers<C>, FormView {
public interface SettlementInstructionsSearchFormViewUiBinder extends UiBinder<Widget, SettlementInstructionsSearchFormViewImpl> {}
#Inject
static FormViewImpl uiBinder;
#Inject
static Provider<DateEditorWidget> dateEditorProvider;
#UiField(provided = true)
MyComponent<String> myComp;
#UiField
DateEditorWidget effectiveDateFrom;
// .. other fields
#Inject
public FormViewImpl () {
myComp = new MyComponent<String>("lol");
if (uiBinder == null)
uiBinder = GWT.create(SettlementInstructionsSearchFormViewUiBinder.class);
initWidget(uiBinder.createAndBindUi(this));
}
#UiFactory
DateEditorWidget createDateEditor() {
return dateEditorProvider.get();
}
}
What other things than a class with no arguments is required ? In my company's project the same kind of code works at some other place. Sorry from the high level of noob here...
If you guys had any pointers it would be nice.
Thanks
Two issues:
First, two of your #Inject fields are static - have you done anything to make static fields be injected? Static fields don't get set when Gin (or Guice) creates new instances, those have to be set once and done. As they are static, they will never be garbage collected - this may be okay with you, or it might be a problem, and you should change them to instance fields. If you want to keep them static, then you must invoke requestStaticInjection in your module to ask Gin to initialize them when the ginjector is created.
Next, if you do choose to remove static, the uiBinder field must still be null in that constructor, because the fields can't have been injected yet! How do you set a field on an object that you haven't yet created? That's what you are expecting Gin to be able to do. Instead, consider passing that as an argument into the #Inject decorated constructor. You don't even need to save it as a field, since the widget will only use it the one time.
To have a class generated by GIN (doesn't matter if it is a uiBinder or not) it is not necessary for it to have a default constructor (i.e. the one without parameters). The class you want to inject must have the constructor annotated with #Inject:
#Inject
public InjectMeClass(Object a, Object b)
The other class which is injected, suppose it is a UiBinder, must have the injected fields annotated with #UiField(provided=true):
public class Injected extends Composite {
private static InjectedUiBinder uiBinder = GWT
.create(InjectedUiBinder.class);
interface InjectedUiBinder extends UiBinder<Widget, Injected> {
}
#UiField(provided=true)
InjectMeClass imc;
public Injected(final InjectMeClass imc) {
this.imc=imc;
initWidget(uiBinder.createAndBindUi(this));
}
So, back to your case:
#UiField(provided = true)
MyComponent<String> myComp;
#Inject
public FormViewImpl (MyComponent<String> myComp) {
this.myComp = myComp;
and for example:
public class MyComponent<T> extends Composite {
private T value;
#Inject
public MyComponent(T t) {
this.value = t;
...
}
...
}
In the GIN module you can have a provider:
#Provides
#Singleton
public MyComponent<String> createMyComponent() {
return new MyComponent<String>("lol");
}
I'm using gwt-platform and tried to implement GWT's editor framework. But I don't get it working from within the presenter. There are some answers around the web, that say I have to inject the EditorDriver somehow into the Presenter, but I don't know how to do this...
At the moment I tried this without success:
public class MyPresenter extends Presenter<MyPresenter.MyView, MyPresenter.MyProxy> implements MyUiHandlers {
public interface MyView extends View, HasUiHandlers<MyUiHandlers>, Editor<MyModel> {}
#ProxyStandard
#NameToken(NameTokens.myPage)
#NoGatekeeper
public interface MyProxy extends ProxyPlace<MyPresenter> {}
interface Driver extends SimpleBeanEditorDriver<MyModel, MyView> {}
private Driver editorDriver;
DispatchAsync dispatcher;
#Inject
public MyPresenter(EventBus eventBus, MyView view, MyProxy proxy, DispatchAsync dispatcher) {
super(eventBus, view, proxy);
getView().setUiHandlers(this);
this.dispatcher = dispatcher;
MyModel m = new MyModel();
m.setId(1L);
m.setUsername("username");
m.setPassword("password");
editorDriver = GWT.create(Driver.class);
editorDriver.initialize(this.getView());
editorDriver.edit(m);
}
...
}
It works if I explicitly specify the ViewImplementation, but that's not the way MVP should work:
interface Driver extends SimpleBeanEditorDriver<MyModel, MyViewImpl> {}
...
editorDriver.initialize((MyViewImpl) this.getView());
I would be nice if someone could give me an example how to do it right.
Thanks
An approach similar to what was used in an earlier version of the Expenses sample worked for me:
An interface that the view should implement. The wildcard is used so that the presenter does not need to know the concrete view implementation:
import com.google.gwt.editor.client.Editor;
import com.gwtplatform.mvp.client.View;
/**
* Implemented by views that edit beans.
*
* #param <B> the type of the bean
*/
public interface BeanEditView<B> extends View, Editor<B> {
/**
* #return a new {#link SimpleBeanEditorDriver} initialized to run
* this editor
*/
SimpleBeanEditorDriver<B, ?> createEditorDriver();
}
Your presenter should look something like this now:
public class MyPresenter extends Presenter<MyPresenter.MyView, MyPresenter.MyProxy> implements MyUiHandlers {
public interface MyView extends BeanEditView<MyModel>, HasUiHandlers<MyUiHandlers> {}
#ProxyStandard
#NameToken(NameTokens.myPage)
#NoGatekeeper
public interface MyProxy extends ProxyPlace<MyPresenter> {}
private SimpleBeanEditorDriver<MyModel, ?> editorDriver;
DispatchAsync dispatcher;
#Inject
public MyPresenter(EventBus eventBus, MyView view, MyProxy proxy, DispatchAsync dispatcher) {
super(eventBus, view, proxy);
getView().setUiHandlers(this);
this.dispatcher = dispatcher;
MyModel m = new MyModel();
m.setId(1L);
m.setUsername("username");
m.setPassword("password");
editorDriver = getView().createEditorDriver();
}
...
}
And the view implmementation:
public class MyViewImpl extends ViewWithUiHandlers<MyUiHandlers> implements
MyPresenter.MyView {
public interface Binder extends UiBinder<Widget, MyViewImpl> { }
private static Binder uiBinder = GWT.create(Binder.class);
/**
* The driver to link the proxy bean with the view.
*/
public interface EditorDriver extends SimpleBeanEditorDriver<MyModel, MyViewImpl> { }
private final Widget widget;
public MyViewImpl() {
widget = uiBinder.createAndBindUi(this);
}
#Override
public SimpleBeanEditorDriver<MyModel, ?> createEditorDriver() {
EditorDriver driver = GWT.create(EditorDriver.class);
driver.initialize(this);
return driver;
}
#Override
public Widget asWidget() {
return widget;
}
...
}
That's as close as I could get to MVP with GWT's Editor Framework. I couldn't find a way for the view implementation to NOT know the model but I don't think it's really necessary.
If anyone has any improvements on this, I'm glad to hear.
Found some additional comments on GWT Editors. It seems that it might just not be possible to completely separate the model. As Thomas Broyer puts it in his answer to another Editor question:
"MVP is not set in stone (it's not even defined; it was coined by Martin Fowler but he retired the term in favor of two more specific patterns), so you're only violating the rules you gave to yourself. Put differently, the Editor framework as a whole can be seen as violating MVP: each editor know the model, not necessarily the exact instance it's editing (as with ValueAwareEditor or LeafValue), but at least the kind of objects it's an editor of."
The issue is that the Driver.class passed to GWT.create
editorDriver = GWT.create(Driver.class);
must the concrete class that holds all sub-editors, i.e. all the uibinded widgets.
One solution is the following:
The view interface extends the editor interface for the Model object
public interface MyView extends View, ..., Editor<MyModel>
The view implementation MyViewImpl defines a driver type
interface MyDriverImpl extends SimpleBeanEditorDriver<MyModel,MyViewImpl>
The driver is instantiated in MyViewImpl by
SimpleBeanEditorDriver<MyModel,MyView> driver = GWT.create(MyDriverImpl.class);
The parent type
SimpleBeanEditorDriver<MyModel,MyView>
can be used to pass references of the driver to the presenter
MVP says that you use the presenter to completely seperate the model from the view. In addition I'd say that your approach puts logic inside the view...
I hope that there's another solution ;)