Interface ContentsEmbeddedComponents


public interface ContentsEmbeddedComponents

Embedded Components in Contents

Smart GWT's Layout and its subclasses provide a powerful layout engine, but some designs are more naturally expressed with CSS — for example, CSS flexbox with gap and flex-wrap, CSS grid with named grid-template-areas, or a page where Smart GWT widgets are interspersed with static HTML such as headings, paragraphs, and images.

Embedded components in contents bridge this gap: you provide an HTML template as Canvas.contents (a plain string, as it has always been), and mark slots for Smart GWT widgets using the scID attribute on placeholder elements. The CSS in the template controls all layout and sizing; the framework takes care of connecting widgets to the placeholders at draw time.

Basic Usage

Place one or more elements with an scID attribute in the Canvas.contents string. Each scID value identifies a child component by name — either a child already present in the Canvas.children array (matched by Canvas.name), or an AutoChild that will be created automatically if the corresponding *Defaults / *Constructor properties exist on the Canvas.

After the contents HTML is inserted into the DOM at draw time, the framework scans for scID placeholders, resolves each one to a child or AutoChild, and connects the widget to the placeholder element via Canvas.htmlElement and Canvas.matchElement. The widget renders inside the placeholder and sizes itself to match it, so the HTML's CSS rules control all layout and sizing.

Example: Mixing HTML and Smart GWT Widgets

The most common use case is a page section that mixes static HTML content (headings, text, images) with Smart GWT widgets:

  isc.Canvas.create({
      width: "100%", height: "100%",
      overflow: "auto",
      contents:
          '<h2>Employee Directory</h2>' +
          '<p>Select a row to see details.</p>' +
          '<div scID="mainGrid"' +
          '     style="height:400px"></div>' +
          '<footer style="margin-top:10px;' +
          '     color:#888">&copy; 2026 Acme</footer>',
      mainGridDefaults: {
          _constructor: "ListGrid",
          autoFetchData: true,
          dataSource: "employees"
      }
  });
  
The <h2>, <p>, and <footer> are ordinary HTML rendered by the browser. The mainGrid AutoChild fills the 400px-tall placeholder. Because the child is position:relative inside the content flow, the entire Canvas scrolls naturally when overflow is "auto".

As with all AutoChildren, the created component is accessible as an instance property on the parent — in this case, this.mainGrid. See AutoChildren for details.

Example: CSS Flexbox

  isc.Canvas.create({
      width: "100%", height: "100%",
      contents:
          '<div style="display:flex; gap:10px;' +
          '     height:100%">' +
              '<div scID="mainGrid"' +
              '     style="flex:1"></div>' +
              '<div scID="detail"' +
              '     style="width:300px"></div>' +
          '</div>',
      mainGridDefaults: {
          _constructor: "ListGrid",
          autoFetchData: true,
          dataSource: "employees"
      },
      detailDefaults: {
          _constructor: "DetailViewer",
          dataSource: "employees"
      }
  });
  
The CSS flex rules control the split between the grid and the detail viewer.

Example: CSS Grid with Named Areas

CSS Grid's grid-template-areas is a particularly natural fit for scID, since both use names to identify regions:

  isc.Canvas.create({
      width: "100%", height: "100%",
      contents:
          '<div style="display:grid; height:100%;' +
          '     grid-template-columns: 1fr 300px;' +
          '     grid-template-rows: auto 1fr auto;' +
          '     grid-template-areas:' +
          "     'header header'" +
          "     'grid   detail'" +
          "     'footer footer';" +
          '     gap: 8px">' +
          '<h2 style="grid-area:header;' +
          '     margin:0">Dashboard</h2>' +
          '<div scID="mainGrid"' +
          '     style="grid-area:grid"></div>' +
          '<div scID="detail"' +
          '     style="grid-area:detail"></div>' +
          '<div style="grid-area:footer;' +
          '     text-align:center;' +
          '     color:#888">Status bar</div>' +
          '</div>',
      mainGridDefaults: {
          _constructor: "ListGrid",
          autoFetchData: true,
          dataSource: "employees"
      },
      detailDefaults: {
          _constructor: "DetailViewer",
          dataSource: "employees"
      }
  });
  
The header and footer are plain HTML; the grid and detail areas are Smart GWT widgets sized by the CSS grid engine.

Example: Explicit Children

Instead of AutoChildren, pre-created children can be supplied in the Canvas.children array. Each child's Canvas.name must match the corresponding scID:

  isc.Canvas.create({
      width: "100%", height: "100%",
      contents:
          '<div style="display:flex; gap:10px;' +
          '     height:100%">' +
              '<div scID="gridArea"' +
              '     style="flex:1"></div>' +
              '<div scID="chartArea"' +
              '     style="flex:1"></div>' +
          '</div>',
      children: [
          isc.ListGrid.create({
              name: "gridArea",
              autoDraw: false,
              dataSource: "employees",
              autoFetchData: true
          }),
          isc.FacetChart.create({
              name: "chartArea",
              autoDraw: false,
              facets: [{ id: "region" }],
              dataSource: "employees",
              autoFetchData: true
          })
      ]
  });
  

How It Works

The framework processes scID placeholders during Canvas.draw(), between HTML insertion and drawChildren(). For each scID found in the DOM:

  1. The name is resolved to an existing child (by Canvas.name) or an AutoChild (via Canvas.addAutoChild()). If neither resolves, a warning is logged and the placeholder is left empty.
  2. The child is connected to the placeholder element with Canvas.htmlElement set to the placeholder, Canvas.htmlPosition set to "afterBegin", and Canvas.matchElement set to true. This causes the widget to render inside the placeholder and size itself to match.
  3. The child draws in the normal drawChildren() pass.
Because the child is rendered position:relative inside the placeholder, it participates in normal document flow. The placeholder's CSS (width, height, flex, grid-area, etc.) controls the widget's size and position, and Canvas.persistentMatchElement ensures the widget tracks the placeholder through subsequent page reflows.

Scrolling and Responsive Behavior

Because embedded children are position:relative within the contents, they scroll naturally with the rest of the content when overflow is set to "auto" or "scroll". This is a key difference from Smart GWT's Layout system, which uses absolute positioning internally.

CSS media queries, flex-wrap, and other responsive techniques work as expected — when the browser reflows the template (for example, due to a viewport resize), Canvas.persistentMatchElement detects the placeholder's new size and automatically resizes the embedded widget to match.

Dynamic Contents

Dynamic contents (${} expressions) can be combined with scID placeholders. The ${} expressions are evaluated first, producing the final HTML string, and then the framework scans the resulting DOM for scID attributes. This allows the template itself to be data-driven while still embedding Smart GWT components.

Visibility

When an embedded child is hidden (via Canvas.hide(), Canvas.visibleWhen, etc.), the child's DOM handle becomes invisible but the scID placeholder element is unaffected — it retains whatever size and position the CSS template gives it. This follows the existing Canvas.matchElement contract: the framework controls the widget; the CSS template controls the placeholder.

If a design requires the placeholder to collapse when the child is hidden (for example, so that flex siblings expand to fill the vacated space), this can be achieved by toggling the placeholder's own CSS from application code — for instance, setting display:none on the placeholder in a Canvas.visibilityChanged() override.

Redraw Behavior

When a Canvas with scID placeholders is redrawn (e.g., via Canvas.setContents() or Canvas.markForRedraw()), the framework preserves embedded children using a detach-and-reattach strategy:

  1. Each embedded child's DOM handle is detached from its placeholder (without destroying the widget).
  2. The contents HTML is replaced in the DOM.
  3. The DOM is re-scanned for scID placeholders.
  4. Children already connected to an scID are reattached to the corresponding new placeholder element without re-resolving.
If a placeholder that previously existed is absent after redraw, the corresponding child is cleared but not destroyed, allowing it to be reconnected if the placeholder reappears in a subsequent redraw. Conversely, if a new scID appears that was not in the previous contents, Canvas.resolveContentsComponent() is called to resolve it, including AutoChild creation if applicable.

For Canvas.htmlPosition variants where the child is absolutely positioned over (rather than inside) the placeholder, a clear() / draw() cycle is used instead of detach/reattach, because the child's position depends on coordinates that must be recalculated.

Layout Interaction

When scID placeholders are used in a Layout or its subclasses (VLayout, HLayout, etc.), embedded children are positioned by the CSS template, not by the Layout engine. These children are part of the Canvas.children array but are not added as Layout.members, so Layout.layoutChildren() does not attempt to size or position them.

Restrictions

  • scID resolution is local to the owning Canvas. A placeholder cannot reference a widget that is not a child or AutoChild of the Canvas whose contents contains the placeholder.
  • A given scID value must be unique within a single Canvas's contents.
  • The placeholder element should be a block-level element (typically a <div>). Using inline elements may produce unexpected sizing behavior.
  • Embedded children should not also be added as Layout.members of the same Canvas.
  • Embedded children are normal Canvas.children and follow standard child lifecycle — they are destroyed when the parent is destroyed.
See Also: