2011-04-18 14 views
8

Tenemos una aplicación web que necesita un tema diferente para cada cliente principal. El desarrollador original hizo esto mirando la URL en javascript y agregando una hoja de estilo para anular el tema predeterminado.ClientBundle para múltiples "temas"

Un problema con esto es que el sitio tiene el aspecto predeterminado durante unos segundos y cambia repentinamente al tema correcto. Otra es que parece desperdiciar mucho ancho de banda/tiempo.

Mi idea actual es crear un "predeterminado" ClientBundle con nuestro estilo predeterminado extender esa interfaz y anular cada entrada (según sea necesario) con las imágenes del cliente usando las diversas anotaciones como @ImageResouce y apuntando a una ubicación diferente.

¿Alguien ha tenido experiencia en esto? Uno de los problemas que prevengo es no poder usar las etiquetas de estilo de uibinder ya que apuntan estáticamente a un paquete de recursos específico.

¿Alguna idea?

+0

añade un ejemplo ... – helios

Respuesta

17

paquetes overriden

sí se puede.

He hecho la anulación con ClientBundles y funciona bien. Una cosa que DEBE hacer es heredar los tipos de las propiedades también. Por ejemplo:

BigBundle { 
    Nestedundle otherBundle(); 
    ImageResource otherImage(); 
    Styles css(); 
} 

Y entonces debe heredar de esta manera:

OtherBigBundle extends BigBundle { 
    OtherNestedBundle otherBundle(); // if you want to change it 
    ImageResource otherImage(); // of you want to change it 
    OtherStyles css(); // of you want to change it 
} 

y OtherNestedBundle extends NestedBundle y OtherStyles extends Styles

Al menos con CSS: si las propiedades se declaran no usar el niño Interfaz producirán estilos para el mismo nombre de clase de CSS y todo se mezclará. Así lo declarará estilos overriden con las interfaces niño :)

UIBinders flexibles

Puede ajustar desde fuera del haz de usar si se utiliza UiField(provided=true) anotación. De esta forma, primero configura el paquete y luego llama al uibindler. Utilizará el campo de recursos suponiendo que ya está creado.

la vinculación diferida

usted podría utilizar GWT.runAsync para la carga sólo el paquete correcto.

Algunos ejemplos

la interfaz de usuario.xml

<ui:with field='res' type='your.package.TheBundle'/> 

la clase correspondiente

@UiField(provided=true) TheBundle bundle; 

private void createTheThing() { 
    this.bundle = factory.createBundle(); 
    MyUiBindler binder = GWT.create(MyUiBindler.class); 
    this.panel = binder.createAndBindUi(this); 
    ... 
} 

Algunas interfaces de paquete

interface TheBundle extends ClientBundle { 
    @ImageResource("default.png") 
    ImageResource image1(); 

    @Source("default.css") 
    TheCss css(); 
} 

interface Theme1Bundle extends TheBundle { 
    @ImageResource("one.png") 
    ImageResource image1(); // type: imageresource is ok 

    @Source("one.css") 
    OneCss css(); // type: OneCss => use other compiled css class-names 

    interface OneCss extends TheCss { // inner-interface, just for fun 
    // don't need to declare each String method 
    } 
} 

Si no se sobreescribe algo que está bien

opciones para la fábrica paquete

1) justo por completo

if (...) { 
    return GWT.create(TheBundle.class); 
} else if (...) { 
    return GWT.create(Theme1Bundle.class); 
} 

2) runAsync (sólo tiene que cargar la parte necesaria ... pero después se ejecuta la parte inicial)

if (...) { 
    GWT.runAsync(new RunAsyncCallback() { 
     public void onSuccess() { 
     return GWT.create(TheBundle.class); 
     } 
     // please program the onFailure method 
    }); 
} else if (...) { 
    GWT.runAsync(new RunAsyncCallback() { 
     public void onSuccess() { 
     return GWT.create(Theme1Bundle.class); 
     } 
     // please program the onFailure method 
    }); 
} 

3) utilizan diferido de unión y generadores para fábrica de autogeneración en tiempo de compilación basada en paquetes anotados como @ThemeBundle("one")

Este ejemplo es del mundo real. Utilizo una DynamicEntryPointWidgetFactory (DEPWidgetFactory para abreviar) para crear un widget basado en una cadena de identificador. Cada widget es una pantalla de aplicación y cada ítem del menú principal tiene el widgetName que debe crear.

En su caso, el id será el tema para crear.

Importante: si utiliza runAsync no puede crear el paquete de recursos justo antes de crear la interfaz de usuario como en el código de ejemplo anterior. Debe solicitar el tema y cuando esté listo (en la devolución de llamada) páselo a su constructor de widgets y su widget puede asignarlo a su campo.

La interfaz de fábrica:

public interface DynamicEntryPointWidgetFactory 
{ 
    public void buildWidget(String widgetName, AsyncCallback<Widget> callback); 
} 

La anotación para los widgets para generar:

@Target(ElementType.TYPE) 
public @interface EntryPointWidget 
{ 
    /** 
    * The name wich will be used to identify this widget. 
    */ 
    String value(); 
} 

La configuración del módulo:

Se dice: la puesta en práctica de la fábrica se generará con esta clase (la otra opción es usar replace-with, pero en nuestro caso no tenemos opciones predefinidas para cada localidad o navegador, sino algo más dinámico).

<generate-with class="com.dia.nexdia.services.gwt.rebind.entrypoint.DynamicEntryPointFactoryGenerator"> 
    <when-type-assignable class="com.dia.nexdia.services.gwt.client.entrypoint.DynamicEntryPointWidgetFactory" /> 
</generate-with> 

El generador:

public class DynamicEntryPointFactoryGenerator extends Generator { 
    @Override 
    public String generate(TreeLogger logger, GeneratorContext context, 
      String typeName) throws UnableToCompleteException { 
     PrintWriter pw = context.tryCreate(logger, 
       "x.services.gwt.client.entrypoint", 
       "DynamicEntryPointWidgetFactoryImpl"); 

     if (pw != null) { 
      // write package, imports, whatever 
      pw.append("package x.services.gwt.client.entrypoint;"); 
      pw.append("import x.services.gwt.client.entrypoint.DynamicEntryPointWidgetFactory;"); 
      pw.append("import com.google.gwt.core.client.GWT;"); 
      pw.append("import com.google.gwt.core.client.RunAsyncCallback;"); 
      pw.append("import com.google.gwt.user.client.rpc.AsyncCallback;"); 
      pw.append("import com.google.gwt.user.client.ui.Widget;"); 

      // the class 
      pw.append("public class DynamicEntryPointWidgetFactoryImpl implements DynamicEntryPointWidgetFactory {"); 

      // buildWidget method 
      pw.append(" public void buildWidget(String widgetName, final AsyncCallback<Widget> callback) {"); 

      // iterates over all the classes to find those with EntryPointWidget annotation 
      TypeOracle oracle = context.getTypeOracle(); 
      JPackage[] packages = oracle.getPackages(); 
      for (JPackage pack : packages) 
      { 
       JClassType[] classes = pack.getTypes(); 
       for (JClassType classtype : classes) 
       { 
        EntryPointWidget annotation = classtype.getAnnotation(EntryPointWidget.class); 
        if (annotation != null) 
        { 
         String fullName = classtype.getQualifiedSourceName(); 
         logger.log(TreeLogger.INFO, "Entry-point widget found: " + fullName); 

         pw.append("if (\"" + annotation.value() + "\".equals(widgetName)) {"); 
         pw.append(" GWT.runAsync(" + fullName + ".class, new RunAsyncCallback() {"); 
         pw.append("  public void onFailure(Throwable t) {"); 
         pw.append("   callback.onFailure(t);"); 
         pw.append("  }"); 
         pw.append("  public void onSuccess() {"); 
         pw.append("   callback.onSuccess(new " + fullName + "());"); 
         pw.append("  }"); 
         pw.append(" });"); 
         pw.append(" return;"); 
         pw.append("}"); 
        } 
       } 
      } 
      pw.append("callback.onFailure(new IllegalArgumentException(\"Widget '\" + widgetName + \"' not recognized.\"));"); 

      pw.append(" }"); 
      pw.append("}"); 

      context.commit(logger, pw);   
     } 

     // return the name of the generated class 
     return "x.services.gwt.client.entrypoint.DynamicEntryPointWidgetFactoryImpl"; 
    } 
+0

¿Cómo sería la opción 3 el trabajo? ¿Registra un generador en el archivo de configuración GWT xml? – benstpierre

+1

Exactamente. Usted especifica cuál es la "clase generadora" para la interfaz BundleFactory. Esa clase de generador debe implementar un método que emita el código. Ese código escribe el "si". Puede utilizar algunas clases de utilidad GWT para encontrar interfaces de Bundle etiquetadas de alguna manera (como el ejemplo de @ThemeBundle). Mañana puedo publicar un código de muestra si lo desea. – helios

+0

Con el intercambio tradicional de CSS, ¿no cambiaría el archivo CSS que se carga de forma instantánea para la GUI completa? ¿Hay alguna manera de lograr este mismo efecto o tendré que "restablecer" cada estilo en el nuevo paquete? – benstpierre