Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 587
Benjamin M Avatar asked Jul 11 '12 13:07

Benjamin M


1 Answers

There's currently a little bit of a concept mismatch between Dependency Injection and UiBinder, but the way I currently use it is:

private final Provider<LocaleAwareLabel> labelProvider;

@Inject
public MyView(TranslationDictionary dictionary, 
              EventBus eventBus, 
              Provider<LocaleAwareLabel> labelProvider) {

  this.dictionary = dictionary;
  this.labelProvider = labelProvider;
  initWidget(uiBinder.createAndBindUi(this));
}

@UiFactory
public LocaleAwareLabel buildLocaleAwareLabel() {
  return labelProvider.get();
}

Then I can create as many labels as I want in my ui.xml:

<g:HTMLPanel>
  <custom:LocaleAwareLabel translationToken="abc"/>
  <custom:LocaleAwareLabel translationToken="xyz"/>
</g:HTMLPanel>

In the LocaleAwareLabel, I use a setter method for the translation token, and override onLoad():

private String translationToken;

@Inject
public LocaleAwareLabel(TranslationDictionary dictionary, EventBus eventBus) {
  this.dictionary = dictionary;
  initWidget(uiBinder.createAndBindUi(this));
}

@Override
protected void onLoad() {
  super.onLoad();
  doSomethingWithTheTranslationToken(); // do this here, not in the constructor!
}

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

Example for AssistedInject

MyView changes to:

private final LocaleAwareLabelFactory labelFactory;

@Inject
public MyView(TranslationDictionary dictionary, 
              EventBus eventBus, 
              LocaleAwareLabelFactory labelFactory) {

  this.dictionary = dictionary;
  this.labelFactory = labelFactory;

  initWidget(uiBinder.createAndBindUi(this));
}

@UiFactory
public LocaleAwareLabel buildLocaleAwareLabel(final String translationToken) {
  return labelFactory.create(translationToken);
}

LocaleAwareLabel:

public interface LocaleAwareLabelFactory {
  LocaleAwareLabel create(final String translationToken);
}

@Inject
public LocaleAwareLabel(TranslationDictionary dictionary, 
                        EventBus eventBus, 
                        @Assisted String translationToken) {

  this.dictionary = dictionary;
  initWidget(uiBinder.createAndBindUi(this));
  doSomethingWithTheTranslationToken(); // now you can do it in the constructor!
}

In your Gin module, add:

install(new GinFactoryModuleBuilder().build(LocaleAwareLabelFactory.class));

Make sure to add guice's assistedinject jar (e.g. guice-assistedinject-snapshot.jar) to your classpath.

like image 106
Chris Lercher Avatar answered Oct 05 '22 01:10

Chris Lercher