Статьи

GXT MVC Framework

Последние пару месяцев я разрабатывал GWT-приложение, используя смесь простых старых GWT и GXT- виджетов. Когда я только начинал его разрабатывать, я не знал, как лучше организовать свой код и отделить логику. Решение, которое я придумал, состояло в том, чтобы принять какую-то инфраструктуру MVC. Поскольку я уже использовал GXT, я выбрал облегченную реализацию MVC GXT .

Как упоминалось в разделе «Тестирование приложений GWT» , в MVC GXT не так много документации . Лучшей справочной документацией, кажется, является Кристиан Начало работы с Ext-GWT: Справочное приложение Mail .

Переход на страницу с помощью Dispatcher
После работы с GXT MVC в течение пары месяцев я все еще не уверен, что полностью понимаю, как работает навигация и диспетчеризация событий. Самая большая путаница для меня заключается в том, как лучше всего использовать класс GXT Dispatcher .

Проблема с Dispatcher состоит в том, что у него есть два метода, которые, кажется, делают то же самое.

  • forwardEvent (4 варианта)
  • dispatch (3 варианта)

В дополнение к этим методам в Dispatcher fireEventв классе View GXT есть два метода . Согласно моим расчетам, это означает, что есть 9 различных вариантов перехода от одного вида к другому. Какой из них лучше всего использовать?

Из того, что я узнал, я думаю, что лучше всего использовать fireEventв представлениях и forwardEventв контроллерах и других виджетах. IMO, dispatcherникогда не должен использоваться, кроме как в onHistoryChangedметоде реализации вашего HistoryListener . Важно понимать, что этот метод должен работать только в том случае, если для этого события зарегистрирован контроллер представления.

  protected void fireEvent(AppEvent event) {
    Controller c = controller;
    while (c != null) {
      if (c.canHandle(event)) {
        c.handleEvent(event);
      }
      c = c.parent;
    }
  }

Тем не менее, fireEventкажется, работает, даже если контроллер представления не зарегистрирован для этого события. Это потому, onHistoryChangedчто вызывается в EntryPoint. Для опытных пользователей GXT MVC эта навигация сочетается с вашими результатами?

Для успешной работы навигации самое важное — включить поддержку истории. В следующем разделе рассказывается о том, как сделать это эффективно.

Включение поддержки истории
Чтобы лучше объяснить, я создал простое приложение GWT MVC Example и использовал Maven для создания архетипа с ним. Вы можете создать проект из архетипа, используя следующую команду:

mvn archetype:create -DarchetypeGroupId=org.appfuse.archetypes \
-DarchetypeArtifactId=gwt-mvc -DarchetypeVersion=1.0-SNAPSHOT \
-DgroupId=com.mycompany.app -DartifactId=myproject \
-DremoteRepositories=http://oss.sonatype.org/content/repositories/appfuse-snapshots

Чтобы включить поддержку истории в этом приложении, я осуществил HistoryListener в моем EntryPoint (Application.java) и добавил следующую логику для инициализации:

// If the application starts with no history token, redirect to 'home' state
String initToken = History.getToken();
if (initToken.length() == 0) {
    History.newItem(HistoryTokens.HOME);
}

// Add history listener
History.addHistoryListener(this);

// Now that we've setup our listener, fire the initial history state.
History.fireCurrentHistoryState();

В этом примере HistoryTokens — это класс, который содержит все URL-адреса «представлений» в приложении.

public class HistoryTokens {
    public static final String HOME = "home";
    public static final String CALENDAR = "calendar";
    public static final String NOTES = "notes";
    public static final String SEARCH = "search";
}

Чтобы URL-адреса, такие как http: // localhost: 8080 / # calendar, переходили в представление календаря, в onHistoryChangedметоде существует следующая логика .

        Dispatcher dispatcher = Dispatcher.get();

        if (historyToken != null) {
            if (historyToken.equals(HistoryTokens.HOME)) {
                dispatcher.dispatch(AppEvents.GoHome);
            } else if (historyToken.equals(HistoryTokens.CALENDAR)) {
                dispatcher.dispatch(AppEvents.Calendar);
            } else if (historyToken.equals(HistoryTokens.NOTES)) {
                dispatcher.dispatch(AppEvents.Notes);
            } else if (historyToken.equals(HistoryTokens.SEARCH)) {
                dispatcher.dispatch(AppEvents.Search);
            } else {
                GWT.log("HistoryToken '" + historyToken + "' not found!", null);
            }
        }

Контроллеры регистрируются в EntryPoint следующим образом:

        final Dispatcher dispatcher = Dispatcher.get();
        dispatcher.addController(new CalendarController());
        dispatcher.addController(new HomeController());
        dispatcher.addController(new NotesController());
        dispatcher.addController(new SearchController());

Контроллеры реагируют на события, для которых они зарегистрированы. Это делается в их конструкторе:

    public CalendarController() {
        registerEventTypes(AppEvents.Calendar);
    }

Чтобы навигация работала, вам нужно создать ссылки с токенами истории 1 . Например, вот ссылка из HomeViewкласса:

	Hyperlink notesLink = new Hyperlink("Notes", HistoryTokens.NOTES);
	notesLink.addClickListener(new ClickListener() {
	    public void onClick(Widget widget) {
	        Dispatcher.get().fireEvent(AppEvents.Notes);
	    }
	});

Вы заметите, что в этом примере я использую fireEventметод Dispatcher . Если я хотел передать некоторые данные с вашего мероприятия, вам нужно будет использовать forwardEvent. Вот пример из CalendarView:

    Button submit = new Button("Submit");

    submit.addSelectionListener(new SelectionListener<ButtonEvent>() {
        public void componentSelected(ButtonEvent ce) {
            AppEvent<Date> event = 
                new AppEvent<Date>(AppEvents.GoHome, date.getValue(), HistoryTokens.HOME);
            Dispatcher.forwardEvent(event);
        }
    });

В этом примере вы также можете использовать Dispatcher.dispatcher(), но я считаю, что это приведет к тому, что переход произойдет дважды, потому что onHistoryChangedметод тоже вызывается . В большинстве случаев это не имеет значения, за исключением случаев, когда вы начинаете использовать DispatcherListener s.

Надеюсь, эта статья помогла вам понять, как работает GXT MVC Framework. Мне интересно узнать, как работают другие фреймворки GWT MVC. Если вы использовали один, я хотел бы услышать о вашем опыте.

1. Если вы используете конструктор по умолчанию в Hyperlink и используете setText(), обязательно вызовите setTargetHistoryToken()тоже. Если вы этого не сделаете, будет использован пустой маркер истории, и # заставит браузер прокрутить страницу вверх, прежде чем произойдет переход страницы.