Existing-view injection

Use this pattern when the native screen already owns the correct window, model, navigation state, and input behavior. A small child view acts as a carrier that exposes the feature to the JavaScript bootstrap.

Python bridge

  1. Define a small ViewModel that registers the injected feature and its packaged CSS, classic scripts, or ES modules.
  2. Define a ViewComponent backed by the native R.mods... layout accessor on WG 2.4.1, or the validated dynamic accessor on the historical WG path.
  3. Validate the native accessor directly on WG 2.4.1. Wait for resource-map validation only on the historical WG path.
  4. After the native parent reaches its loaded lifecycle, attach the child with a unique child layout ID.
  5. Make attachment idempotent and let the parent lifecycle own destruction.
class InjectionModel(ViewModel):
    def _initialize(self):
        super(InjectionModel, self)._initialize()
        gf_mod_inject(
            self,
            'FeaturePanel',
            styles=['coui://gui/gameface/mods/vendor/FeaturePanel/FeaturePanel.css'],
            modules=['coui://gui/gameface/mods/vendor/FeaturePanel/FeaturePanel.js']
        )


from gui.impl.gen import R


class InjectionComponent(ViewComponent):
    viewLayoutID = R.mods.vendor.FeaturePanel.views.FeaturePanel

    def _createViewModel(self):
        return InjectionModel()


def attach_to_loaded_parent(parent):
    if parent is None or not parent_is_loaded(parent):
        return False
    if parent_has_child(parent, CHILD_LAYOUT_ID):
        return True
    parent.setChildView(CHILD_LAYOUT_ID, InjectionComponent())
    return True
Symbol Origin in this example Use
ViewModel, ViewComponent Wulf/client-side view classes; verify import path per build. Own the injected Python component and model.
gf_mod_inject, R.mods... OpenWG injection bridge plus the WG generated resource tree. Publish injected assets and bind the natively registered carrier layout.
parent_is_loaded, parent_has_child, CHILD_LAYOUT_ID Illustrative helper names. Keep attachment idempotent and tied to the loaded parent lifecycle.

Attach after load

Construction and loading are not the same boundary. Attaching during the parent's early loading phase can be unreliable; the proven pattern is to attach after the parent has completed its loaded lifecycle, while still checking that the child is not already present.

Reading Gameface models

Register model access after engine readiness, subscribe to changes, then render the current value once. Native collections may be proxy objects rather than ordinary JavaScript arrays, and generated field names or private methods can change between client builds.

Illustrative: this subscribes to the document root model; renderFromModel is a feature-owned renderer. An injected child needs its own verified model/resource identity, as described below.

let callbackId = null;
let model = null;
let alive = true;
const resourceId = 0; // Root model of this document, not an injected child.

function onDataChanged() {
  if (alive && model) renderFromModel(model);
}

export function dispose() {
  alive = false;
  engine.off('viewEnv.onDataChanged', onDataChanged);
  if (callbackId !== null) {
    viewEnv.removeDataChangedCallback(callbackId, resourceId);
  }
  callbackId = null;
  model = null;
  window.removeEventListener('unload', dispose);
}

window.addEventListener('unload', dispose);
engine.whenReady.then(() => {
  if (!alive) return;
  model = window.model;
  if (!model) return;
  callbackId = viewEnv.addDataChangedCallback('model', resourceId, true);
  engine.on('viewEnv.onDataChanged', onDataChanged);
  onDataChanged();
});
  • Store callback IDs together with the exact model or resource identity used to register them.
  • Perform an initial render because the first value may exist before the callback is installed.
  • Copy proxy collections into plain arrays only when needed; do not assume every array method exists.
  • Fail closed when a field or model is absent, and keep version-sensitive access in a narrow adapter.

Read by topic

Choose a focused chapter. Complete examples and reference details are preserved on their topic pages.