вторник, 18 октября 2011 г.

GWT & Mvp4G Event Filter

    В продолжение статьи об GWT + Mvp4G рассмотрим включение/отключение обработчиков с помощью Mvp4G Event Filter. Mvp4g позволяет фильтровать события и останавливать до того как они будут переданы обработчикам. Избавимся от условий if на каждое событие в группах презентеров. Сначала создадим PresenterHandler, который будет отвечать за инициализацию презентеров, добавление их в группы отправителей/получателей и активации списка презентеров для группы событий.
@EventHandler
@Singleton
public class PresenterHandler extends BaseEventHandler<AppEventBus> {

    private Map<Game, List<AbstractGroupPresenter<?, AppEventBus>>> presenterMap;

    public PresenterHandler() {
        presenterMap = new HashMap<Game, List<AbstractGroupPresenter<?, AppEventBus>>>();
    }

    public void onInit() {
        addWidget(SenderPresenter.class, Game.Action);
        addWidget(ReceiverPresenter.class, Game.Action);
        addWidget(ReceiverPresenter.class, Game.Action);
        addWidget(ReceiverPresenter.class, Game.Action);

        addWidget(SenderPresenter.class, Game.RPG);
        addWidget(ReceiverPresenter.class, Game.RPG);
        addWidget(ReceiverPresenter.class, Game.RPG);
        addWidget(ReceiverPresenter.class, Game.RPG);

        addWidget(SenderPresenter.class, Game.Strategy);
        addWidget(ReceiverPresenter.class, Game.Strategy);
        addWidget(ReceiverPresenter.class, Game.Strategy);
        addWidget(ReceiverPresenter.class, Game.Strategy);
    }

    private void addWidget(
            Class<? extends AbstractGroupPresenter<?, AppEventBus>> component, Game game) {
        AbstractGroupPresenter<?, AppEventBus> presenter = (AbstractGroupPresenter<?, AppEventBus>) eventBus
                .addHandler(component, false);
        presenter.setGame(game.toString());
        presenter.bind();
        if (!presenterMap.containsKey(game)) {
            presenterMap.put(game, new ArrayList<AbstractGroupPresenter<?, AppEventBus>>());
        }
        presenterMap.get(game).add(presenter);
    
        if (presenter instanceof SenderPresenter) {
            eventBus.showSenderWidget((Widget) presenter.getView());
        } else {
            eventBus.showReceiverWidget((Widget) presenter.getView());
        }
    }

    /**
     * activate the presenter list for an event group
     */
    public void activateGroup(Game groupToActivate) {
        for (Game game : presenterMap.keySet()) {
            for (AbstractGroupPresenter<?, AppEventBus> presenter : presenterMap.get(game)) {
                // when activating an empty group, activate all presenter
                if (groupToActivate == null || groupToActivate.equals("") || groupToActivate.equals(game)) {
                    presenter.setActivated(true);
                } else {
                    presenter.setActivated(false);
                }
            }
        }
    }
}


* This source code was highlighted with Source Code Highlighter.
     Далее создадим фильтр (AppPresentersEventFilter), который будет срабатывать на каждое событие вызванное в нашей шине событий (AppEventBus).
public class AppPresentersEventFilter implements EventFilter<AppEventBus> {

    @Inject
    private PresenterHandler presenterHandler;

    @Override
    public boolean filterEvent(String eventName, Object[] params, AppEventBus eventBus) {
        // deactivate presenter registered for a group when the event name ends in "$"
        if (eventName.endsWith("$")) {
            presenterHandler.activateGroup(Game.valueOf((String) params[0]));
        }
    
        // always return true to forward event to all active handlers
        return true;
    }
}


* This source code was highlighted with Source Code Highlighter.
     И подключим к шине событий:
@Filters(filterClasses = AppPresentersEventFilter.class)
@Events(startView = MainWidget.class)
public interface AppEventBus extends EventBus {

    @Start
    @Event(handlers = { PresenterHandler.class })                        
    public void init();

    @Event(handlers = { MainPresenter.class })
    public void showSenderWidget(IsWidget widget);

    @Event(handlers = { MainPresenter.class })
    public void showReceiverWidget(IsWidget widget);

    @Event(handlers = {ReceiverPresenter.class})
    public void setSelectedItem$(String genre, String value);
}


* This source code was highlighted with Source Code Highlighter.
     Также можно динамически добавлять/удалять фильтр с помощью методов addEventFilter и removeEventFilter в шине событий.
AppPresentersEventFilter filter = new AppPresentersEventFilter();
eventBus.addEventFilter(filter);
eventFilter.removeEventFilter(filter);


* This source code was highlighted with Source Code Highlighter.
     В шине используется метод setSelectedItem$(String genre, String value) с особым символом для фильтрации событий. Когда происходит событие и срабатывает фильтр, мы делаем проверку окончания имени события. Если имя заканчивается на символ “$”, то отключаем зарегистрированную группу презентеров. В фильтре происходит инжект PresenterHandler, через который вызываем метод activateGroup(Game groupToActivate) для включения/отключения списка презентеров для группы событий.
     Презентеры (Main, Receiver and Sender) после изменений выглядят вот так:
@Presenter(view = MainWidget.class)
public class MainPresenter extends BasePresenter<IMainWidget, AppEventBus> {

    public interface IMainWidget {
        public void addSenderWidget(IsWidget widget);
        public void addReceiverWidget(IsWidget widget);
    }

    public void onShowSenderWidget(IsWidget widget) {
        view.addSenderWidget(widget);
    }

    public void onShowReceiverWidget(IsWidget widget) {
        view.addReceiverWidget(widget);
    }
}


* This source code was highlighted with Source Code Highlighter.
@Presenter(view = ReceiverWidget.class, multiple = true)
public class ReceiverPresenter extends AbstractGroupPresenter<IReceiverWidget, AppEventBus> {

    public interface IReceiverWidget {
        void showGroup(String group);
        void setReceivedValue(String value);
    }

    @Override
    public void bind() {
        view.showGroup(getGame());
    }

    public void onSetSelectedItem$(String genre, String value) {
        List<String> games = Genre.valueOf(value).getGames();
        view.setReceivedValue(games.get(Random.nextInt(games.size())));
    }
}


* This source code was highlighted with Source Code Highlighter.
@Presenter(view = SenderWidget.class, multiple = true)
public class SenderPresenter extends AbstractGroupPresenter<ISenderWidget, AppEventBus> {
    public interface ISenderWidget {
        void showGroup(String group);
        HasChangeHandlers getSelectableComponent();
        String getSelectedValue();
        void displayValues(List<Genre> asList);
    }

    public void bind() {
        view.showGroup(getGame());
        view.getSelectableComponent().addChangeHandler(new ChangeHandler() {
            @Override
            public void onChange(ChangeEvent event) {
                eventBus.setSelectedItem$(getGame(), view.getSelectedValue());
            }
        });
        view.displayValues(Game.valueOf(getGame()).getGenre());
    }
}


* This source code was highlighted with Source Code Highlighter.
     Вот и все. Eclipse проект можно скачать по следующей ссылке.

3 комментария:

  1. Прочитал все статьи про GWT, одобряю! :)

    Не совсем понял цель группировки презентеров и зачем каждому небольшому виджету свой собственный презентер, если их можно обернуть в композит и сделать единственным вью с единственным презентером: а предложенным образом (ручным созданием презентеров), мне кажется, нарушается event flow mvp4g: EventBus перестаёт быть единой точкой управления загрузкой презентеров в модуле.

    С другой стороны, если виджеты сложные, у каждого отдельная модель данных и хочется у каждого иметь свой eventBus (который, кстати, можно инжектить, это я уже сейчас научился :) ), то возможно это решение оправдано.

    Возможно просто в начале статьи нужно описать зачем понадобилась группировка презентеров.

    ОтветитьУдалить
  2. Cпасибо за комментарий.

    Обязательно добавлю более детальное описание в начало статьи.

    ОтветитьУдалить
  3. Best bets for soccer today - Sports Toto
    Today, 토토 사이트 도메인 we're going febcasino to tell you a few bet365 key to checking into soccer betting apps. kadangpintar of the most popular soccer betting options and which ones 바카라 사이트 will

    ОтветитьУдалить