Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the proper way to use gwt-bootstrap modal with gwt-platform?

I am constructiong an webapp with Google Web Toolkit using GWT-Platform and GWT-Bootstrap frameworks. Mostly it has been almost flawless until I tried to implement a popup. These frameworks' undestanding of popups seems to be quite different.

GWT-Platform expects a popup widget itself to be an instance of com.google.gwt.user.client.ui.PopupPanel when using the GWTP's RevealRootPopupContentEvent.fire(source, content) or a presenter's addToPopupSlot(child) method.

GWT-Bootstrap's Modal is used like any other widget that is added to the underlying panel but my goal is it to have a separate presenter and view and to possibly fetch it asynchrously with AsyncProvider.

I have tried to make it as a PresenterWidget and using addToSlot(slot, content) to reveal it but it doesn't look quite right. Not all of the styles are applied this way and the close icon (×), doesn't work for example.

I think I am not the first one trying to do something like that so maybe someone has figured out a proper way to make it work.

Thanks!

like image 252
Raidok Avatar asked May 21 '12 18:05

Raidok


3 Answers

You have to create a view:

public class MyPopupView extends PopupViewImpl implements MyView {

    protected Widget widget;

    public interface MyPopupViewUiBinder extends
            UiBinder<Widget, MyPopupView> {
    }

    @UiField(provided = true)
    Modal dialogBox;

    private MyPresenter presenter;

    @Inject
    public MyPopupView(final MyPopupViewUiBinder uiBinder,
            final EventBus eventBus) {
        super(eventBus);
        setUpDialog(); // Provides UiField => Before initWidgets
        initWidget(uiBinder.createAndBindUi(this));
    }

    // DialogBox must be overridden to let the presenter handle changes onUnload
    private void setUpDialog() {
        dialogBox = new Modal() {

            @Override
            protected void onUnload() {
                MyPopupView.this.hide();
            }
        };

        dialogBox.setTitle("Some title");
    }

    @Override
    public void setPresenter(final MyPresenter presenter) {
        this.presenter = presenter;
    }

    @Override
    public final void hide() {
        dialogBox.hide();
        presenter.hide();
    }

    @Override
    public void setAutoHideOnNavigationEventEnabled(final boolean autoHide) {
        // TODO Auto-generated method stub
    }

    @Override
    public void setCloseHandler(
            final PopupViewCloseHandler popupViewCloseHandler) {
        // TODO Auto-generated method stub
    }

    @Override
    public void setPosition(final int left, final int top) {
        // TODO Auto-generated method stub
    }

    @Override
    public void show() {
        dialogBox.show();
    }

    @Override
    public void center() {
        dialogBox.show();
    }

    @Override
    public Widget asWidget() {
        return widget;
    }

    protected final void initWidget(final Widget widget) {
        this.widget = widget;
    }

}

And a UIBinder file:

<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
    xmlns:g='urn:import:com.google.gwt.user.client.ui'
    xmlns:b='urn:import:com.github.gwtbootstrap.client.ui'>

    <b:Modal title="Some Title" ui:field="dialogBox">
        <!-- Your content -->
    </b:Modal>
</ui:UiBinder>
like image 71
Dominik Avatar answered Oct 16 '22 15:10

Dominik


Your gwtp popup presenter has a view that extends PopUpViewImpl which implements PopupView, and uses a lot of the methods of that interface for displaying the popup (asPopupPanel(), show(), center(), etc).

I'm just starting to get to know gwt-bootstrap (looks great +caalos0), but it seems that Modal doesn't implement PopupView, and therefore cannot be passed to addToPopupSlot in a way it would be displayed automatically by gwtp.

as for the addToSlot() issue, are you using RootLayoutPanel or RootPanel? it could be the reason for addToSlot not working properly, since the gwt-bootstrap Modal widget is attached to the RootPanel on initialization, this can cause weird layout behavior along with an application using RootLayoutPanel as base.

I would try to extend the Modal component, let it implement PopUpView, add it as a field on the PopUpViewImpl attached to your popup presenter, and override the PopUpViewImpl asPopupPanel() function to return the new extended Modal.

like image 26
shukshuk Avatar answered Oct 16 '22 14:10

shukshuk


Based on the answer by @dominik I did some improvements, see my Gist. It contains some abstract base classes that can be used for any Modal/PopupView implementation. It's a bit more complex but also cleaner because we don't pass the whole Presenter to the View. The interface for the View to interact with the Presenter when the modal is closed is HasModalUnbind.

You would use these classes as follows. Example presenter:

public class ErrorModalPresenter extends ModalPopupPresenter<ErrorModalPresenter.MyView> {

    public interface MyView extends ModalPopupView {
        DivElement getErrorMessage();
    }

    private final ErrorEvent error;

    @Inject
    public ErrorModalPresenter(final EventBus eventBus,
                               final MyView view,
                               @Assisted final ErrorEvent error) {

        super(eventBus, view);
        this.error = error;
    }

    @Override
    public void unbindModal() {
        ErrorDismissEvent.fire(this, this);
    }

    @Override
    protected void onBind() {
        super.onBind();

        //noinspection ThrowableResultOfMethodCallIgnored
        getView().getErrorMessage().setInnerText(error.getCause().getMessage());
    }
}

Example view:

public class ErrorModalView extends ModalPopupViewImpl implements ErrorModalPresenter.MyView {

    @UiField(provided = true)
    Modal errorModal;

    @UiField
    DivElement errorMessage;

    interface Binder extends UiBinder<Widget, ErrorModalView> {}

    @Inject
    public ErrorModalView(final EventBus eventBus,
                          final Binder uiBinder) {

        super(eventBus);

        errorModal = initModal();
        initWidget(uiBinder.createAndBindUi(this));
    }

    @Override
    public DivElement getErrorMessage() {
        return errorMessage;
    }
}

And the UiBinder XML just for the record:

<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
             xmlns:g='urn:import:com.google.gwt.user.client.ui'
             xmlns:b='urn:import:com.github.gwtbootstrap.client.ui'>

    <b:Modal ui:field='errorModal' title='Error'>
        <g:HTML>
            <div ui:field='errorMessage'/>
        </g:HTML>

        <b:ModalFooter>
            <b:Button text='Close' dismiss='MODAL'/>
        </b:ModalFooter>
    </b:Modal>

</ui:UiBinder>

In unbindModal() of ErrorModalPresenter I fire an event which is caught by the parent presenter of ErrorModalPresenter. There the modal presenter is removed from a container and then unbind() is called on the presenter. Of course any other solution is possible in unbindModal().

The base classes assume that modals are one-shot modals that will be removed once they're hidden. This behaviour can be changed in initModal() of ModalPopupViewImpl.

like image 25
Sven Jacobs Avatar answered Oct 16 '22 14:10

Sven Jacobs