I have been using a ClientBundle and CssResources for quite some time but have noticed that my styles is getting injected many times. The one thing I can point my finger too is that I use quite a few <ui:with field='resources' type='com.vf.client.resources.Resources' />
in my templates and my suspicion is that this is creating multiple copies of my clientBundle. One thing that may even cause this is that I use Ray Ryan's concept of caching my views in my clientfactory so some of the views are created before ever being attached to the DOM. In my base view I use the provided=true on the resources so as to hopefully not let UiBinder generate a new one for me. This may not be working though, I am suspecting and the ui:with is creating a new copy and ignoring the provided=true. I have used developer tools in Chrome and Firebug to see and in both cases the style is getting injected many times. Not sure on how to even resolve this problem without removing my Resources class from all my UiBinder templates and this would make for quite a bit of work. Any ideas are appreciated.
/**
*
* @author chris
*/
public abstract class ViewImpl extends Composite implements IView, RequiresResize {
@UiField(provided = true)
public final Resources resources;
}
public interface Resources extends ClientBundle, CellTable.Resources, CellList.Resources, DataGrid.Resources {
/**
* These are the obfuscated styles.
*
* @return
*/
@Source({ "default.css", "default-external.css"})
public Style style();
}
Update
I have been using a factory/singleton just to make sure it only creates one. I create this Resources ClientBundle in my ClientFactory implementation when the application starts. In my application start I call ensureEnjected on my style and from then on the ensureInjected is never called in my code.
This is my factory that simply gets my singleon request factory. I used to have the static initializer inside my interface but I moved to this a while back to hopefully clean up my multiple styles problem.
import com.google.gwt.core.client.GWT;
public class ResourcesFactory {
private static Resources instance = null;
private ResourcesFactory() {
}
public static final Resources getResources() {
if (instance == null) {
instance = GWT.create(Resources.class);
}
return instance;
}
}
My client bundle gets initialized and injected here only.
@Override
public void onModuleLoad() {
if (Window.Location.getParameterMap().containsKey("debug")) {
Window.alert("Remote Debugging will be enabled, see server log for debug information");
RemoteDebugService.enable();
}
try {
clientFactory = ClientFactory.INSTANCE;
masterLayoutPanel = clientFactory.getMasterLayoutPanel();
} catch (Exception e) {
logger.log(Level.SEVERE, "Unable to instantiate the ClientFactory", e);
Window.alert("SOMEBODY BROKE THE BUILD, add ?debug to the url to enable remote debugging" + e.getMessage());
}
RootLayoutPanel.get().add(masterLayoutPanel);
StyleInjector.inject(clientFactory.getResources().style().getText());
PlaceHistoryHandler historyHandler = new PlaceHistoryHandler(clientFactory.getPlaceHistoryMapper());
PlaceController placeController = clientFactory.getPlaceController();
// Start PlaceHistoryHandler with our PlaceHistoryMapper
historyHandler.register(placeController, clientFactory.getEventBus(), defaultPlace);
startApplication(clientFactory, historyHandler);
}
I have figured out atleast one problem I was doing wrong so far that was causing my style to be injected multiple times. The first is that I defined my CellTable and Datagrid styles to be inside my single style sheet but when injecting this style it gets injected multiple times. In the code below the default.css contains all my css definitions for the entire web application including the cell table and cell grid styles. I was injecting these at application start of course the the cellTableStyle() and cell dataGridStyle() inject the entire style sheet.
public interface Resources extends ClientBundle, CellTable.Resources, CellList.Resources, DataGrid.Resources {
...
/**
* {@link CellTable.Style} styles
*/
@Source("default.css")
Style cellTableStyle();
/**
* {@link DataGrid.Style} styles
*/
@Source("default.css")
Style dataGridStyle();
/**
* These are the obfuscated styles.
*
* @return
*/
@Source({ "default.css", "default-external.css" })
public Style style();
...
}
If declaring styles in this way the stylesheets should be broken up into separate style sheets and only contain the relevant styles or implement my main style to implement these interfaces and remove the extra dependent style implementations.
public interface Resources extends ClientBundle, CellTable.Resources, CellList.Resources, DataGrid.Resources {
...
/**
* {@link CellTable.Style} styles
*/
@Source("celltable.css")
Style cellTableStyle();
/**
* {@link DataGrid.Style} styles
*/
@Source("datagrid.css")
Style dataGridStyle();
/**
* These are the obfuscated styles.
*
* @return
*/
@Source({ "default.css", "default-external.css" })
public Style style();
...
}
This is the implementation where the style can be passed to a CellTable style and the styles will only be injected one time. Notice that the style implments all the needed table, grid and list styles.
public interface Resources extends ClientBundle, CellTable.Resources, CellList.Resources, DataGrid.Resources {
...
public interface Style extends CssResource, ProgressWidget.Style, CellTable.Style, CellList.Style, DataGrid.Style {
...
}
/**
* Single style that can be used for all my cell table resources
* and the default styles.
*
* @return
*/
@Source({ "default.css", "default-external.css" })
public Style style();
...
}