Golden Layout binds to components and then controls their position, size and visibility (positioning) so that they fit within a layout. There are 4 ways Golden Layout can bind to a component:
Registering a component and specifying static positioning is the classic GoldenLayout approach to binding components. The components are registered with GoldenLayout and specify a constructor or callback function used to create a component whenever a new instance is needed in the layout. When the constructor or callback is invoked, it is passed a container object which includes a HTML element. The constructor or callback will create the object and make its top level HTML element a child of the container’s HTML element. The component is then part of the Golden Layout’s DOM hierarchy. Whenever the layout is re-arranged, the GoldenLayout DOM is adjusted to reflect the new layout hierarchy. Effectively this involves the ancestors of components’ root HTML elements being reparented when a layout is changed.
The following functions can be used to register components.
GoldenLayout.registerComponent()
GoldenLayout.registerComponentConstructor()
GoldenLayout.registerComponentFactoryFunction()
GoldenLayout.registerComponentFunction()
GoldenLayout.registerGetComponentConstructorCallback()
To give applications more control over the allocation of components, you can bind components with events instead of registration. If a handler is assigned to the event VirtualLayout.bindComponentEvent
it will be fired whenever a new component is needed. The handler should:
container.element
,BindableComponent
interface with virtual: false
.When a component is removed from Golden Layout it will be necessary to remove the component’s top level HTML elements as children of container.element
. Other component ‘tear-down’ actions may also be required. These actions can be carried out in either the VirtualLayout.unbindComponentEvent
event or the component container’s beforeComponentRelease
event (or both). Both these events will be fired (if handlers are assigned) when a component is no longer needed in Golden Layout.
With virtual components, Golden Layout knows nothing about components and does not include the component’s HTML elements in its own DOM hierarchy. Instead, whenever a component needs its position, size or visibility changed, Golden Layout will fire events which allow the application to change a component’s position, size or visibility. This is analogous to virtual grids where strings and other content to be displayed in a grid, are not included within the grid. Instead the grid fires events whenever it needs to display content. The application will return the required content.
Virtual Components has the following advantages:
With Virtual Components the following events need to be handled:
VirtualLayout.bindComponentEvent: (container, itemConfig) => ComponentContainer.BindableComponent
itemConfig
,absolute
position,container
as the key,virtualRectingRequiredEvent
and virtualVisibilityChangeRequiredEvent
events,BindableComponent
interface with virtual: true
.Example:
private handleBindComponentEvent(container: ComponentContainer, itemConfig: ResolvedComponentItemConfig) {
// Use ResolvedComponentItemConfig.resolveComponentTypeNamecan to resolve component types to a unique name
const componentTypeName = ResolvedComponentItemConfig.resolveComponentTypeName(itemConfig);
if (componentTypeName === undefined) {
throw new Error('handleBindComponentEvent: Undefined componentTypeName');
}
const component = this.createVirtualComponent(container, componentTypeName, itemConfig.componentState);
const componentRootElement = component.rootHtmlElement;
this._layoutElement.appendChild(componentRootElement);
this._boundComponentMap.set(container, component);
container.virtualRectingRequiredEvent = (container, width, height) => this.handleContainerVirtualRectingRequiredEvent(container, width, height);
container.virtualVisibilityChangeRequiredEvent = (container, visible) => this.handleContainerVisibilityChangeRequiredEvent(container, visible);
return {
component,
virtual: true,
};
}
VirtualLayout.unbindComponentEvent: (container) => void
container
as the key,Example:
private handleUnbindComponentEvent(container: ComponentContainer) {
const component = this._boundComponentMap.get(container);
if (component === undefined) {
throw new Error('handleUnbindComponentEvent: Component not found');
}
const componentRootElement = component.rootHtmlElement;
if (componentRootElement === undefined) {
throw new Error('handleUnbindComponentEvent: Component does not have a root HTML element');
}
this._layoutElement.removeChild(componentRootElement);
this._boundComponentMap.delete(container);
}
LayoutManager.beforeVirtualRectingEvent: () => void
This event does not need to be handled. However it can be used to optimise positioning of components. Whenever a layout is changed, it may be that several components need to be repositioned. This event will be fired whenever one or more components need to be positioned as the result of one layout change. Typically it is used to get the position of Golden Layout’s root HTML element, using getBoundingClientRect()
. This can then be cached for use when each component’s position needs to be calculated.
Example:
private handleBeforeVirtualRectingEvent(count: number) {
this._goldenLayoutBoundingClientRect = this._layoutElement.getBoundingClientRect();
}
ComponentContainer.virtualRectingRequiredEvent: (container, width, height) => void;
container
as the key,getBoundingClientRect()
, (Alternatively, it can used the position calculated by the handler for the virtualRectingRequiredEvent
event.)getBoundingClientRect()
,left
top
width
height
Example:
private handleContainerVirtualRectingRequiredEvent(container: ComponentContainer, width: number, height: number) {
const component = this._boundComponentMap.get(container);
if (component === undefined) {
throw new Error('handleContainerVirtualRectingRequiredEvent: Component not found');
}
const rootElement = component.rootHtmlElement;
if (rootElement === undefined) {
throw new Error('handleContainerVirtualRectingRequiredEvent: Component does not have a root HTML element');
}
const containerBoundingClientRect = container.element.getBoundingClientRect();
const left = containerBoundingClientRect.left - this._goldenLayoutBoundingClientRect.left;
rootElement.style.left = this.numberToPixels(left);
const top = containerBoundingClientRect.top - this._goldenLayoutBoundingClientRect.top;
rootElement.style.top = this.numberToPixels(top);
rootElement.style.width = this.numberToPixels(width);
rootElement.style.height = this.numberToPixels(height);
}
ComponentContainer.virtualVisibilityChangeRequiredEvent: (container, visible) => void;
container
as the key,display
property in the component’s top level HTML element.Example:
private handleContainerVisibilityChangeRequiredEvent(container: ComponentContainer, visible: boolean) {
const component = this._boundComponentMap.get(container);
if (component === undefined) {
throw new Error('handleContainerVisibilityChangeRequiredEvent: Component not found');
}
const componentRootElement = component.rootHtmlElement;
if (componentRootElement === undefined) {
throw new Error('handleContainerVisibilityChangeRequiredEvent: Component does not have a root HTML element');
}
if (visible) {
componentRootElement.style.display = '';
} else {
componentRootElement.style.display = 'none';
}
}
ComponentContainer.virtualZIndexChangeRequiredEvent: (container, logicalZIndex, defaultZIndex) => void
container
as the key,defaultZIndex
.Example:
private handleContainerVirtualZIndexChangeRequiredEvent(container: ComponentContainer, logicalZIndex: LogicalZIndex, defaultZIndex: string) {
const component = this._boundComponentMap.get(container);
if (component === undefined) {
throw new Error('handleContainerVirtualZIndexChangeRequiredEvent: Component not found');
}
const componentRootElement = component.rootHtmlElement;
if (componentRootElement === undefined) {
throw new Error('handleContainerVirtualZIndexChangeRequiredEvent: Component does not have a root HTML element');
}
componentRootElement.style.zIndex = defaultZIndex;
}
The apitest application demonstrates how virtual components are implemented.
When using virtual components, think of Golden Layout as more of an engine calculating position rather than actually positioning components. This binding method requires more work to set up than other binding methods. However it offers more flexibility and opens up more design opportunities. For example with virtual components, any HTML element could be the parent for your components (not just the Golden Layout container). You can even have different parents for different components. This allows, for example, some of your components have one parent, and the others a different parent. They could then inherit different CSS or handle event propagation differently.
These events give applications a lot of flexibility with positioning components in Golden Layout - but at the expense of more effort of integrating into Golden Layout. It is however, possible to get the same benefits of Virtual Components with just registering a component. In this case, a component will be registered as in classic approach to Golden Layout binding, however, within Golden Layout, the component will be handled like a virtual component. Golden Layout will internally handle the necessary events.
Existing applications using register functions in Golden Layout can easily be updated to use virtual binding by:
virtual
. By default, this is false
, specifying the classic binding in Golden Layout. Set this to true
to specify that components of that type should be implemented internally as virtual components.rootHtmlElement
which returns the component’s root HTML element. Components written in TypeScript should implement the GoldenLayout.VirtuableComponent
interface.rootHtmlElement
element need to have its overflow
CSS property set to hidden.static
).With these changes, applications can continue to use Golden Layout as they are now however Golden Layout will internally use virtual component binding.
Please note there will be a couple of minor behaviour changes:
absolute
.Also note that ‘virtual via registration’ binding is not supported by the GoldenLayout.registerGetComponentConstructorCallback()
registration function.
An application can use multiple methods of binding components for different component types. Whenever a component needs to be bound, Golden Layout will try to bind in the following order:
bindComponentEvent
handler. If so, this event will be used to bind it as a virtual component.getComponentEvent
handler. If so, this event will be used to bind the component statically within the Golden Layout DOM. This method is deprecated.If you use both ‘Virtual via Events’ and ‘Embedding via Events’, then the unbindComponentEvent
handler can use the ComponentContainer.virtual
field to determine which of these binding methods was used for a component.
The inheritance hierarchy for the Golden Layout class is: LayoutManager
-> VirtualLayout
-> GoldenLayout
.
The VirtualLayout
class implements all the Golden Layout functionality except for the register functions. If you only intend to use virtual components using the bindComponentEvent
, you can create an instance of VirtualLayout
instead of GoldenLayout
.
getComponentEvent