jueves, 11 de agosto de 2011

Activities and Places

Activities and Places es la implementación de google del patrón MVP. Si bien es posible desarrollar páginas web estáticas utilizando Activities and Places, en la versión GWT 2.3 no hay suficiente información sobre su integración con el servidor si se puede utilizar RPC o RequestFactory. Este último requiere la clase com.google.web.bindery.event.shared.EventBus pero el ejemplo a continuación sólo funciona con com.google.gwt.event.shared.EventBus. En un desarrollo real se recomienda utilizar RequestFactory y el paquete com.google.web.bindery.event.shared ya que funciona con Aplicaciones GWT y Android.
De acuerdo al Google I/O 2011 recomiendan utilizar GWTP, RequestFactory y Objectify para el desarrollo de aplicaciones.

El ejemplo a continuación es solo para dar una idea del funcionamiento del MVP. El código fuente lo puede descargar aqui.
La estructura de la pagina web sería la siguiente:


Dentro del paquete client crear la interfaz ClientFactory y su implementación

package com.paginaweb.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.Window;

public class PaginaWeb implements EntryPoint {
 
 public void onModuleLoad() {
  Window.alert("Hola Mundo");
 }
}

Su implementación

package com.paginaweb.client;

import com.google.gwt.event.shared.EventBus;
import com.google.gwt.event.shared.SimpleEventBus;
import com.google.gwt.place.shared.PlaceController;

public class ClientFactoryImpl implements ClientFactory {
 private static final EventBus eventBus = new SimpleEventBus();
 private static final PlaceController placeController = new PlaceController(eventBus); 
 @Override
 public EventBus getEventBus() {
   return eventBus;
 }
 @Override
 public PlaceController getPlaceController() {
   return placeController;
 }
}

Agrego unas lineas al EntryPoint

package com.paginaweb.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.place.shared.PlaceController;
import com.google.gwt.user.client.Window;

public class PaginaWeb implements EntryPoint {
 
 public void onModuleLoad() {
  ClientFactory clientFactory = GWT.create(ClientFactory.class);
     EventBus eventBus = clientFactory.getEventBus();
     PlaceController placeController = clientFactory.getPlaceController();
 }
}

Dentro del paquete mvp se crean 2 archivos AppActivityMapper.java y AppPlaceHistoryMapper.java

package com.paginaweb.client.mvp;

import com.google.gwt.activity.shared.Activity;
import com.google.gwt.activity.shared.ActivityMapper;
import com.google.gwt.place.shared.Place;
import com.paginaweb.client.ClientFactory;

public class AppActivityMapper implements ActivityMapper {
 private ClientFactory clientFactory; 
 public AppActivityMapper(ClientFactory clientFactory) {
   this.clientFactory = clientFactory;
 } 
 @Override
 public Activity getActivity(Place place) {
   // TODO Auto-generated method stub
   return null;
 }
}

la interfaz AppPlaceHistoryMapper
package com.paginaweb.client.mvp;

import com.google.gwt.place.shared.PlaceHistoryMapper;

public interface AppPlaceHistoryMapper extends PlaceHistoryMapper {
}

Modificar el archivo del modulo PaginaWeb.gwt.xml

<?xml version="1.0" encoding="UTF-8"?>
<module rename-to='paginaweb'>  
  <inherits name='com.google.gwt.user.User'/>
  <inherits name="com.google.gwt.place.Place"/>
  <inherits name="com.google.gwt.activity.Activity"/>
  <entry-point class='com.paginaweb.client.PaginaWeb'/>  
  <replace-with class="com.paginaweb.client.ClientFactoryImpl">
    <when-type-is class="com.paginaweb.client.ClientFactory"/>
  </replace-with>
  <source path='client'/>
  <source path='shared'/>
</module>

En el paquete client.ui crear la interfaz IndexView.java para la vista principal

package com.paginaweb.client.ui;

import com.google.gwt.place.shared.Place;
import com.google.gwt.user.client.ui.IsWidget;

public interface IndexView extends IsWidget {
 
 void setName(String name);
 void setPresenter(Presenter listener);
 
 public interface Presenter {
  
  void goTo(Place place);
 }

}

Click derecho en el paquete client.ui ir a New->UiBinder


El asistente creara los archivos UiBinder IndexViewImpl.ui.xml y su clase manejadora IndexViewImpl.java

Luego modifico la clase IndexViewImpl.java

package com.paginaweb.client.ui;

import com.google.gwt.core.client.GWT;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Widget;

public class IndexViewImpl extends Composite implements IndexView {

 private static Binder uiBinder = GWT
   .create(Binder.class);

 interface Binder extends UiBinder<Widget, IndexViewImpl> {
 }
 
 private Presenter listener;
 private String name;

 public IndexViewImpl() {
  initWidget(uiBinder.createAndBindUi(this));
 }

 @Override
 public void setPresenter(Presenter listener) {
  this.listener = listener;  
 }

 @Override
 public void setName(String name) {  
  this.name = name;
 }

}

El archivo uibinder solo mostrara un mensaje

<!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">
 <ui:style>
  
 </ui:style>
 <g:HTMLPanel>
 Hello Activities and Places
 </g:HTMLPanel>
</ui:UiBinder>

Activities and Places

De acuerdo a la página de google  un activity representa una acción del usuario. Un Activity no contiene Widgets ni código UiBinder. Se encarga de inicializar y cargar la correspondiente vista.
Un place representa un estado particular de la interfaz.

Activities son como controllers y un Place corresponde a una URL asociada a una vista.
En GWT solo se utiliza un archivo HTML para un modulo, las vistas se diferencian por un #token que se añade al final de la URL.

Crear la clase IndexPlace.java en el paquete client.place
agregando el código necesario luce así:

package com.paginaweb.client.place;

import com.google.gwt.place.shared.Place;
import com.google.gwt.place.shared.PlaceTokenizer;

public class IndexPlace extends Place {

 private String name;
 
 public IndexPlace(String token){
  this.name = token;
 }

 public String getName() {
  return name;
 }
 
 public static class Tokenizer implements PlaceTokenizer<IndexPlace>{

  @Override
  public IndexPlace getPlace(String token) {
   return new IndexPlace(token);
  }

  @Override
  public String getToken(IndexPlace place) {
   return place.getName();
  }
  
 }
}

En la interfaz ClientFactory añado un metodo para devolver la vista IndexView

package com.paginaweb.client;

import com.google.gwt.event.shared.EventBus;
import com.google.gwt.place.shared.PlaceController;
import com.paginaweb.client.ui.IndexView;

public interface ClientFactory {
   EventBus getEventBus();
   PlaceController getPlaceController();
   IndexView getIndexView();
}

En la clase ClientFactoryImpl se implementa el metodo añadido:

package com.paginaweb.client;

import com.google.gwt.event.shared.EventBus;
import com.google.gwt.event.shared.SimpleEventBus;
import com.google.gwt.place.shared.PlaceController;
import com.paginaweb.client.ui.IndexView;
import com.paginaweb.client.ui.IndexViewImpl;

public class ClientFactoryImpl implements ClientFactory {
 private static final EventBus eventBus = new SimpleEventBus();
 private static final PlaceController placeController = new PlaceController(eventBus);
 private static final IndexView indexview = new IndexViewImpl();
 @Override
 public EventBus getEventBus() {
   return eventBus;
 }
 @Override
 public PlaceController getPlaceController() {
   return placeController;
 }
 @Override
 public IndexView getIndexView() {
  return indexview;
 }
}

Creo la clase IndexActivity.java en el paquete client.activity

package com.paginaweb.client.activity;

import com.google.gwt.activity.shared.AbstractActivity;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.place.shared.Place;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.paginaweb.client.ClientFactory;
import com.paginaweb.client.place.IndexPlace;
import com.paginaweb.client.ui.IndexView;

public class IndexActivity extends AbstractActivity implements IndexView.Presenter {
 private ClientFactory clientFactory;
 private String name;
 
 public IndexActivity(IndexPlace place, ClientFactory clientFactory){
  this.name = place.getName();
  this.clientFactory = clientFactory;
 }

 @Override
 public void start(AcceptsOneWidget panel, EventBus eventBus) {  
  IndexView view = clientFactory.getIndexView();
  view.setName(name);
  view.setPresenter(this);
  panel.setWidget(view.asWidget());
 }

 @Override
 public void goTo(Place place) {
  clientFactory.getPlaceController().goTo(place);  
 }

}

modifico la interfaz AppPlaceHistoryMapper.java para agregarle el IndexPlace

package com.paginaweb.client.mvp;

import com.google.gwt.place.shared.PlaceHistoryMapper;
import com.google.gwt.place.shared.WithTokenizers;
import com.paginaweb.client.place.IndexPlace;

@WithTokenizers({IndexPlace.Tokenizer.class})
public interface AppPlaceHistoryMapper extends PlaceHistoryMapper {
}

luego agrego el código al metodo getActivity de la clase AppActivityMapper

package com.paginaweb.client.mvp;

import com.google.gwt.activity.shared.Activity;
import com.google.gwt.activity.shared.ActivityMapper;
import com.google.gwt.place.shared.Place;
import com.paginaweb.client.ClientFactory;
import com.paginaweb.client.activity.IndexActivity;
import com.paginaweb.client.place.IndexPlace;

public class AppActivityMapper implements ActivityMapper {
 private ClientFactory clientFactory; 
 public AppActivityMapper(ClientFactory clientFactory) {
   this.clientFactory = clientFactory;
 } 
 @Override
 public Activity getActivity(Place place) {
  if(place instanceof IndexPlace)
   return new IndexActivity((IndexPlace)place, clientFactory);
  return null;
 }
}

por último modifico mi clase entrypoint PaginaWeb

package com.paginaweb.client;

import com.google.gwt.activity.shared.ActivityManager;
import com.google.gwt.activity.shared.ActivityMapper;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.place.shared.Place;
import com.google.gwt.place.shared.PlaceController;
import com.google.gwt.place.shared.PlaceHistoryHandler;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.paginaweb.client.mvp.AppActivityMapper;
import com.paginaweb.client.mvp.AppPlaceHistoryMapper;
import com.paginaweb.client.place.IndexPlace;

public class PaginaWeb implements EntryPoint {
 
 private Place defaultPlace = new IndexPlace("index");
 private SimplePanel appWidget = new SimplePanel();
 
 public void onModuleLoad() {
  ClientFactory clientFactory = GWT.create(ClientFactory.class);
     EventBus eventBus = clientFactory.getEventBus();
     PlaceController placeController = clientFactory.getPlaceController();
     
     ActivityMapper activityMapper = new AppActivityMapper(clientFactory);
  ActivityManager activityManager = new ActivityManager(activityMapper, eventBus);
  activityManager.setDisplay(appWidget);
  
  AppPlaceHistoryMapper historyMapper = GWT .create(AppPlaceHistoryMapper.class);
  PlaceHistoryHandler historyHandler = new PlaceHistoryHandler(historyMapper);
  historyHandler.register(placeController, eventBus, defaultPlace);
  
  RootPanel.get().add(appWidget);
  
  historyHandler.handleCurrentHistory();
 }
}

Ahora ejecuto click derecho en el proyecto Run As->Web Application

La página muestra el mensaje

Hello Activities and Places

No hay comentarios:

Publicar un comentario