ClientBundle для нескольких «тем» - PullRequest
8 голосов
/ 19 апреля 2011

У нас есть веб-приложение, которому нужна отдельная тема для каждого основного клиента.Первоначальный разработчик сделал это, посмотрев URL в javascript и добавив таблицу стилей, чтобы переопределить тему по умолчанию.

Одна из проблем заключается в том, что сайт выглядит по умолчанию в течение нескольких секунд, а затем внезапно переключается на правильную тему.Другая причина в том, что кажется, что она тратит много пропускной способности / времени.

Моя текущая идея - создать «по умолчанию» ClientBundle с нашим внешним видом и интерфейсом, расширяющим этот интерфейс и заменяющим каждую запись(при необходимости) с изображениями клиента, используя различные аннотации, такие как @ImageResouce и указывающие на другое местоположение.

У кого-нибудь был опыт в этом?Одна проблема, которую я вижу, заключается в невозможности использовать теги стиля uibinder, так как они статически указывают на определенный пакет ресурсов.

Есть идеи?

1 Ответ

17 голосов
/ 19 апреля 2011

Переопределенные связки

Да, вы можете.

Я сделал переопределение с ClientBundles и работает нормально. Одна вещь, которую вы ДОЛЖНЫ сделать, это наследовать типы свойств Например:

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

И тогда вы должны унаследовать этот путь:

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
}

и OtherNestedBundle extends NestedBundle и OtherStyles extends Styles

По крайней мере с помощью css: , если свойства объявлены НЕ ИСПОЛЬЗУЯ дочерний интерфейс, они будут создавать стили для одного и того же имени класса CSS, и все будут смешаны. Поэтому объявляйте переопределенные стили с дочерними интерфейсами:)

Гибкие UIBinders

Можно использовать извне комплекта для использования, если вы используете UiField(provided=true) аннотацию. Таким образом, вы сначала устанавливаете пакет, а затем вызываете uibindler. Он будет использовать поле ресурса, если оно уже создано.

Отсроченное связывание

Вы можете использовать GWT.runAsync для загрузки только правильного пакета.

Пример

The ui.xml

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

соответствующего класса

@UiField(provided=true) TheBundle bundle;

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

Некоторые пакеты интерфейсов

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
  }
}

Если что-то не переопределить, все в порядке

Варианты комплектации завода

1) всего

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

2) runAsync (просто загрузите нужную часть ... но после выполнения начальной части)

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) использовать deferred-binding и генераторы для автогенерации фабрики во время компиляции на основе аннотированных пакетов, таких как @ThemeBundle("one")

Этот пример из реального мира. Я использую DynamicEntryPointWidgetFactory (для краткости DEPWidgetFactory) для создания виджета на основе строки идентификатора. Каждый виджет - это экран приложения, и у каждого главного меню есть виджет, который он должен создать.

В вашем случае идентификатором будет тема для создания.

Важно: если вы используете runAsync, вы не можете создать пакет ресурсов непосредственно перед созданием пользовательского интерфейса, как в предыдущем примере кода. Вы должны запросить тему и, когда она будет готова (в обратном вызове), передать ее конструктору виджетов, и ваш виджет сможет назначить ее своему полю.

Заводской интерфейс:

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

Аннотация для создания виджетов:

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

Конфигурация модуля:

Там написано: реализация для Factory будет сгенерирована с помощью этого класса (другой вариант - использовать replace-with, но в нашем случае у нас нет предопределенных опций для каждой локали или браузера, но есть что-то более динамичное) .

<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>

Генератор:

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";
    }
...