İçeriğe geç

Methods reference

Bu içerik henüz dilinizde mevcut değil.

Methods are called directly on the element (const tg = document.getElementById('tg')).

Instant, served from the cached last selection_change. Return null / [] until the first event arrives.

Current configuration: { parts: [{ partId, partLabel, variantId, label, sku, modelName }], shareUrl }.

The SKUs of the current configuration, one per configurable part — the cart payload: ["OSLO-BODY-WALNUT", "OSLO-LEGS-CHROME"].

Deep-link URL that rebuilds the current configuration exactly. Persist it on the order line — it is the one field worth storing beyond the SKUs.

Async — each returns a Promise.

Enumerate every part + all its variants with select()-ready handles. Resolves to [{ partId, partKey, label, defaultVariantId, inPicker, link?, visibility?, variants: [{ variantId, handle, label, sku, swatch, swatchImage, modelName, isDefault, available, disabledReason, blockedBy }] }].

label is the panel’s customer-facing heading for that option. inPicker tells you whether the published panel offers the part to visitors — it is false for parts a link group drives, since the driver represents them. Filter on it and your UI matches the published panel without re-deriving the rule:

const options = (await tg.getOptions()).filter((o) => o.inPicker);

link ({ role: 'driver' | 'driven', groupId, … }) and visibility are there when you want to render the relationship yourself instead of hiding it.

Blocked options explain themselves. While a variant cannot be picked, available is false and disabledReason says why: 'rule' — the current configuration rules it out — or 'stock' — you reported it out of stock through setAvailability(). blockedBy lists the selections a rule blocks it on, as [{ partId, partKey, variantId, handle }], so your panel can say what to change:

for (const v of option.variants) {
if (v.disabledReason === 'rule' && v.blockedBy.length) {
hint(v, `Not available with ${v.blockedBy.map((b) => b.handle).join(' + ')}`);
} else if (v.disabledReason === 'stock') {
hint(v, 'Out of stock');
}
}

A rule outranks stock when both apply. blockedBy is empty for stock, and for a variant that is ruled out only because picking it would leave no valid configuration.

Options that move carry two more fields: a variant that opens or closes the model reports animation: { clipName, mode: 'toggle' }, and the option reports where the selected variant currently stands as animationState: { open }. Read them to draw your control in the right position on first paint, then drive it with setAnimationState().

Drive a selection from your own UI. Keys are the URL handles (select('body', 'walnut')). Resolves with { ok, selection } after the model swap settles; rejects on unknown handle or timeout. An option that cannot be picked right now — the ones getOptions() reports with available: false — resolves { ok: false, error: 'blocked', reason, blockedBy, selection } and changes nothing. When the option carries its own recorded camera angle, the camera eases there; an option sharing its part’s angle leaves the camera where it is.

Open or close an option that moves — an oven door, a drawer. Separate from select(): the choice and its state are two different things, so a host that only wants the door open does not re-pick the door.

await tg.setAnimationState('door', true);

Idempotent — setting a state the model already holds plays nothing. Resolves with { ok, open }; { ok: false } when the selected variant has no open/close animation, and rejects on an unknown handle. getOptions() reports which variants carry one and where the selected one stands.

Enumerate the product’s module slotsvisibility groups, where one whole part is shown at a time (Shelf 1 with its 22 alternative modules). getOptions() reports group membership but cannot change it; this is the read that lets your own panel offer the slots.

Resolves to [{ groupId, label, activePartId, members: [{ partId, partKey, label, thumb, isActive, available, disabledReason, blockedBy }] }].

  • partKey is the handle to pass to showPart()null when the part has no stable handle, in which case it cannot be driven from the host.
  • thumb is the module’s product shot, generated in the panel from the part’s own model and served from the CDN — see Module images. It is null when none has been generated; render the label instead.
  • available is false while a rule blocks that module; disabledReason is then 'rule' and blockedBy names the selections to change, exactly as on a variant.

Show one member of a module slot, e.g. showPart('shelf1-two-doors'). The group is resolved from the member itself and membership is re-checked, so an unknown handle resolves to { ok: false } and changes nothing. A module a rule blocks resolves { ok: false, error: 'blocked', reason: 'rule', blockedBy }.

const groups = await tg.getGroups();
await tg.showPart(groups[0].members[1].partKey);

A configurator can hold more than one product — a two-seater and a one-seater of the same sofa. Resolves to [{ productId, key, label, active }]: key is what ?product= carries and what selectProduct() takes, active marks the product on screen. Empty when the project has none.

Switch the product from your own panel, e.g. selectProduct('one-seater'). It is the same switch as the built-in panel’s product header: matching choices carry over, the camera moves to that product and the share link gains ?product=. Resolves with { ok, product, selection } after the new product has loaded, and getOptions() then lists its parts. An unknown key resolves { ok: false } and changes nothing.

const products = await tg.getProducts();
if (products.length > 1) await tg.selectProduct(products[1].key);

Back to the published default. Resolves with { ok, selection }.

Push live stock: setAvailability({ body: { walnut: false } }) — keys are the same handles getOptions() returns; false = out of stock. Out-of-stock swatches dim, strike through and become unselectable in the built-in picker; getOptions() then reports each variant’s available. Resolves with { ok, applied }.

Push price display from your catalogue:

tg.setPrices({ currency: 'EUR', locale: 'de-DE', items: { 'SKU-1': 129.9 }, total: 259.8 });

Keys are the same SKUs the events report. Numbers are Intl-formatted with currency/locale; strings are shown verbatim; total is optional (auto-summed when every shown price is numeric). Prices appear in the viewer’s summary. Pass null to clear. Resolves with { ok, applied }.

With controls="off" — named after <video controls> — the viewer draws none of its own controls: no AR button, no snapshot button, no ruler icon, no reset-view button. Your page draws them instead. One switch covers the whole surface, so a control added to the viewer later never needs a new attribute here.

What the viewer’s controls would be offering right now:

const { ar, photo, dimensions, resetView } = tg.getControls() ?? {};

ar is whether native AR can be launched for the current configuration. dimensions is { enabled, on }enabled follows the project’s WebAR settings → Dimensions switch, on is whether the overlay is currently drawn. resetView is true while the camera has left its opening framing. Returns null before the viewer reports.

The togenar:controls event fires on every change, with the same shape in detail — bind your buttons to it and they track the viewer exactly:

tg.addEventListener('togenar:controls', (e) => {
arBtn.hidden = !e.detail.ar;
measureBtn.hidden = !e.detail.dimensions.enabled;
measureBtn.classList.toggle('is-on', e.detail.dimensions.on);
resetBtn.hidden = !e.detail.resetView;
});

Show or hide the product’s measurement overlay. Omit the argument to flip it.

tg.setDimensions(true);
const { on } = await tg.toggleDimensions();
measureBtn.classList.toggle('is-on', on === true);

Resolves with { ok, on } — render on on your own button. Measurements must be enabled for the project (WebAR settings → Dimensions); otherwise it resolves with { ok: false, error: 'dimensions disabled' }. Inside an AR session it resolves with { ok: false, error: 'unavailable in AR' }.

Add controls="off" to the embed to take the viewer’s own controls down and draw your own:

<togenar-embed project="" picker="off" controls="off"></togenar-embed>

Ease the camera back to its opening framing. Resolves with { ok }. Show your own reset button while getControls().resetView is true — that is exactly when the viewer would have shown its own.

Apply the camera angle recorded for a part. Call it when your own panel changes section, not when a swatch is picked. Pass null for a section that is not a part to ease back to the opening framing.

tg.focusPart('door');
tg.focusPart(null);

A part with no recorded angle eases back to the opening framing. Resolves with { ok, moved, partId }; moved is false when the project does not move the camera on part change. An unknown handle resolves with { ok: false, error: 'unknown part handle' }.

Launch AR (iOS Quick Look / Android Scene Viewer / WebXR). Call from your own click handler — WebXR may require an in-page user gesture. Resolves with { ok }.

Whether native AR can be launched for the current configuration — synchronous, returns a boolean. Use it to decide whether to render your own AR button at all, so the button never appears on a configuration or a plan that cannot enter AR.

Tell us your cart call succeeded. Call it from your own “Add to cart” handler, right after your store confirms the add. Fire-and-forget; safe to call on every add, AR or not.

This is what makes AR measurable on Android, where no button of ours can be drawn inside the AR session. Without this call, an AR-driven Android sale is not attributed.

addBtn.addEventListener('click', async () => {
await addToCart(tg.getSkus());
tg.addedToCart({ value: 1249.5, currency: 'EUR', quantity: 2 });
});

Pass value (major units) and currency (ISO 4217) to have the add counted in the Revenue block of Analytics. quantity and sku are optional.

See AR Add to Cart and Revenue tracking.

Tell us the order was placed. Call it on your order-confirmation page, with the embed present.

tg.purchased({ value: 1249.5, currency: 'EUR', orderId: '1001' });

value, currency and orderId are required. orderId deduplicates: a refreshed confirmation page counts once.

See Revenue tracking.

PNG data-URL of the currently-configured model (the summary hero image). Resolves { ok, url } — best-effort, ok: false when capture is unavailable.

Short device-adaptive share link (…/s/{code}) for the current configuration — the same link the built-in summary’s Share box and QR encode. Resolves { ok, url } (falls back to the full launcher URL).

QR PNG data-URL for the desktop “scan to view in AR” flow. Defaults to the short share link; pass { text, size } to encode something else. Resolves { ok, url }.

Async methods can be called before the viewer is ready — requests queue and flush once the viewer’s bridge is up. Default timeout: 30 s.