Search code examples
gwtdependency-injectionuibindergwt-gin

How to use injection via GIN with UiBinder and Widgets?


I'm using GWT 2.4 with gwt-platform 0.7 and gin 1.5.0.

I've built a library for dynamic (live) translation of my GWT application. So every widget will get notified when the LocaleChangeEvent gets fired and then ask my TranslationDictionary to get the new String to display.

The widgets actually look like this:

public class LocaleAwareLabel extends Label implements LocaleChangeEventHandler {
    TranslationDictionary dictionary;
    String translationToken;

    public LocaleAwareLabel(TranslationDictionary dictionary, EventBus eventBus, String translationToken) {
        this.dictionary = dictionary;
        this.translationToken = translationToken;
        eventBus.addHandler(LocaleChangeEvent.TYPE, this);
        getCurrentTranslationFromDictionary();
    }

    public void getCurrentTranslationFromDictionary() {
        this.setText(dictionary.getTranslation(translationToken));
    }

    @Override
    public void onLocaleChange(LocaleChangeEvent event) {
        getCurrentTranslationFromDictionary();
    }
}

As you can see: I can't easily use this widget with UiBinder, at the moment I inject EventBus and TranslationDictionary in my View and use @UiField(provided=true)like this:

@UiField(provided=true)
LocaleAwareLabel myLabel;

@Inject
public MyView(TranslationDictionary dictionary, EventBus eventBus) {
    widget = uiBinder.createAndBindUi(this);

    myLabel = new LocaleAwareLabel(dictionary, eventBus, "someTranslationToken");
}

What I'd like to have: Using my widgets without @UiField(provided=true), so I can simply put them inside a ui.xml like this:

<custom:LocaleAwareLabel ui:field="myLabel" translationToken="someTranslationToken" />

I know I can set the translationToken via UiBinder using:

public void setTranslationToken(String translationToken) {
    this.translationToken = translationToken;
}

But then I still have the problem that I can't use a zero-args constructor because of EventBus and TranslationDictionary. And additionaly I can't call the getCurrentTranslationFromDictionary() inside the constructor, because the value of translationToken of course gets set after the constructor.

Would be nice if someone can provide a solution, maybe with code examples.

And P.S. I'm a total injection-noob, but from my understanding gin may somehow solve my problem. But I don't know how.

Thank you!


Solution

  • This is not possible currently, because UiBinder won't ever call into GIN to instantiate widgets. For that you have to use @UiFactory methods, or provide already-built instances with @Uifield(provided=true).

    There's a request for enhancement already to better integrate the two. You can track it here: http://code.google.com/p/google-web-toolkit/issues/detail?id=6151

    As an alternative, you can requestStaticInjection to inject a Provider into a static field, and call the provider's get() from within your constructor:

    public class LocaleAwareLabel extends Label {
       @Inject Provider<EventBus> eventBusProvider;
       @Inject Provider<TranslationDictionary> dictionaryProvider;
    
       private final TranslationDictionary dictionary;
       private final EventBus eventBus;
       private final string translationToken;
    
       @UiConstructor
       public LocaleAwareLabel(String translationToken) {
           this(dictionaryProvider.get(), eventBusProvider.get(), translationToken);
       }
    
       // For cases where you can use injection, or inject params yourself
       @Inject
       public LocaleAwarelabel(TranslationDictionary dictionary, EventBus eventBus,
               String translationToken) {
           this.dictionary = dictionary;
           this.eventBus = eventBus;
           this.translationToken = translationToken;
       }
    

    You might also be interested into GIN's support for Assisted-Inject, to make it easier to write @UiFactory methods or initialize @UiField(provided=true) fields.