GWT JsInterop - extending a Java interface in JavaScript - gwt

So, we are trying to use JsInterop so that in our GWT application, we can use some modules created externally in JavaScript.
We have an interface that have some contract, some methods that must be implemented by all views.
For example:
package com.foo;
import jsinterop.annotations.JsPackage;
import jsinterop.annotations.JsType;
#JsType(namespace = JsPackage.GLOBAL)
public interface MyView {
void doSomething();
void doSomethingElse();
Element getElement();
}
Now imagine that in the JS side, we have the implementation of that interface:
export class MyViewImplementation extends window.$wnd.MyView {
constructor() {
super();
}
doSometing() {
//Implementation goes here
}
doSomethingElse() {
//Implementation goes here
}
getElement() {
var div = document.createElement("div");
div.textContent = "External Component Test";
return div;
}
}
Can this work?
Currently, I have an error which says:
Class extends value undefined is not a constructor or null

Based on your feedback to my comment, I would remove #JsType from your interface and introduce a class with #JsType(isNative=true) that implements the interface.
public interface MyView {
void doSomething();
void doSomethingElse();
Element getElement();
}
public class JavaView implements MyView {
// . . .
}
#JsType(isNative = true, namespace = JsPackage.GLOBAL)
public class JsView implements MyView {
#Override
public native void doSomething();
#Override
public native void doSomethingElse();
#Override
public native Element getElement();
}
Then in JavaScript:
export class JsView {
// . . .
}

Related

Android How to add external dependency (context) to SubComponent builder in Dagger 2.1.0

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;
}
}

GWT's Editor Framework and GWTP

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

Working with WorkerStateEvent without casting?

I am currently dispatching my Business Logic via the Concurrency API JavaFX offers. But there is one part I stumble over which does not feel clean to me.
Basically if you create a Service which may look like this
public class FooCommand extends Service<Foo> {
#Override protected Task<Foo> createTask() {
return new Foo();
}
}
and I set the onSucceeded
FooCommand fooCommand = CommandProvider.get(FooCommand.class);
fooCommand.setOnSucceeded(new FooSucceededHandler());
fooCommand.start();
to an instance of this class
public class FooSucceededHandler implements EventHandler<WorkerStateEvent> {
#Override public void handle(WorkerStateEvent event) {
Foo f = (Foo) event.getSource().getValue();
}
}
But as you can see I need to cast the value of the Worker to (Foo). Is there some cleaner way to do it?
You could just make your own abstract class:
public abstract class EventCallback<T> implements EventHandler<WorkerStateEvent> {
#Override
public void handle(final WorkerStateEvent workerStateEvent) {
T returnType = (T) workerStateEvent.getSource().valueProperty().get();
this.handle(returnType);
}
public abstract void handle(T objectReturned);
}
And then using it:
final EventCallback<MonType> eventCallback = new EventCallback<MonType>() {
#Override
public void handle(final MonType objectReturned) {
// DO STUFF
}
};
As it is also an EventHandler, it is compatible with JavaFX concurrent API.

GWT Platform UiHandlers not working

I have followed the GettingStarted on the GWTP tutorial
http://code.google.com/p/gwt-platform/wiki/GettingStarted
but unfortunately handlers not working, getUiHandlers() return null and exception stacktrace is same as in:
How to use UiHandlers of GWT Platform?
.
View Class
public class AView extends ViewWithUiHandlers<AUiHandlers> implements APresenter.Display {
#UiTemplate("AView.ui.xml")
interface AViewUiBinder extends UiBinder<Widget, AView> {}
private static AViewUiBinder uiBinder = GWT.create(AViewUiBinder.class);
#UiField Button saveBtn;
#UiField Button cancelBtn;
#UiField DivElement errorDiv;
private Widget widget;
#Inject
public AssetView() {
widget = uiBinder.createAndBindUi(this);
}
public Widget asWidget() {
return widget;
}
// Implementation: Presenter's Display methods
public void setErrorDivText(String msg) {
errorDiv.getStyle().setDisplay(Display.BLOCK);
errorDiv.setInnerText(msg);
}
// Handlers
#UiHandler("saveBtn")
void onSaveButtonClick(ClickEvent event) {
if(getUiHandlers() != null) {
getUiHandlers().onSaveButtonClick();
}
}
#UiHandler("cancelBtn")
void onCancelButtonClick(ClickEvent event) {
if(getUiHandlers() != null) {
getUiHandlers().onCancelButtonClick();
}
}
}
Handler Interface
public interface AUiHandlers extends UiHandlers {
void onSaveButtonClick();
void onCancelButtonClick();
}
Presenter
public class APresenter extends Presenter<APresenter.Display, APresenter.AssetProxy> implements AUiHandlers {
public interface Display extends View, HasUiHandlers<AUiHandlers> {
public void setErrorDivText(String msg);
}
#ProxyStandard
#NameToken(NameTokens.ASSET)
public interface AssetProxy extends ProxyPlace<AssetPresenter> {}
#Inject
public AssetPresenter(EventBus eventBus, Display view, AssetProxy proxy) {
super(eventBus, view, proxy);
getView().setUiHandlers(this);
}
#Override
protected void onBind() {
super.onBind();
}
#Override
protected void revealInParent() {
RevealRootContentEvent.fire( this, this );
}
public void onSaveButtonClick() {
getView().setErrorDivText("Save clicked.");
}
public void onCancelButtonClick() {
getView().setErrorDivText("Cancel clicked.");
}
}
Unable to understand where i am making mistake, implementation regarding UiHandlers is same as told in the above mentioned tutorial's link.
UiHandlers is not generic; it cannot be parameterized with arguments
As I see your handler interface, you have passed AUiHandlers type. I don't understand the package structure of UiHandlers . it should be com.gwtplatform.mvp.client.UiHandlers.
Please check import of it.
Update:
Remove private static AViewUiBinder uiBinder = GWT.create(AViewUiBinder.class);
and Pass as constructor argument
#Inject
public AssetView(AViewUiBinder uiBinder) {
widget = uiBinder.createAndBindUi(this);
}

Inject into anonymous inner class (GIN)

I have something like this:
request.findAllProjects().fire(new ExtReceiver<List<ProjectProxy>>() {
#Override
public void onSuccess(List<ProjectProxy> response) {
view.setProjects(response);
}
});
It is anonymous inner class of the abstract class ExtReceiver. The ExtReceiver is for handling the errors with an errorHandler which i want to provide.
public abstract class ExtReceiver<T> extends Receiver<T> {
private ErrorHandler errorHandler;
public ExtReceiver() {
}
#Inject
public void setErrorHandler(ErrorHandler errorHandler)
{
this.errorHandler = errorHandler;
}
#Override
public abstract void onSuccess(T response);
#Override
public void onFailure(ServerFailure error) {
errorHandler.exception(error);
}
#Override
public void onViolation(Set<Violation> errors) {
ValidationUtils.processViolation(errors);
}
}
I understand why this can't work, because i use the new Operator. But how could i do something like this. I want to have that anonymous class and not put it in an own file or something.
How could I inject that errorHandler? Thought about staticInjections, but it looked like this does not work too (Maybe because of the inheritance i create with doing an anonymous class)
In the opposite to normal Guice i don't know an injector.getInstance() call.
For information: That is a requestFactory call
Why don't you put the errorHandler parameter into the constructor of your abstract class instead creating a separate setErrorHandler setter, something like this:
public abstract class ExtReceiver<T> extends Receiver<T> {
private ErrorHandler errorHandler;
#Inject
public ExtReceiver(ErrorHandler errorHandler) {
this.errorHandler = errorHandler;
}
}
Declare the bindings:
public class MyClientModule extends AbstractGinModule {
protected void configure() {
bind(ErrorHandler.class).in(Singleton.class);
}
}
Declare a Ginjector for your ErrorHandler class annotating it with the Module:
#GinModules(MyClientModule.class)
public interface MyErrorHandlerInjector extends Ginjector {
ErrorHandler getErrorHandler();
}
and then use it like this:
MyErrorHandlerGinjector injector = GWT.create(MyErrorHandlerGinjector.class);
ErrorHandler errorHandler = injector.getErrorHandler();
request.findAllProjects().fire(new ExtReceiver<List<ProjectProxy>>(errorHandler) {
#Override
public void onSuccess(List<ProjectProxy> response) {
view.setProjects(response);
}
});
I think this should work.