mcpField
C
custom-angular-component-creator

custom-angular-component-creator

github.com/datexDesign & UI55 installs
Repository

About custom-angular-component-creator

custom-angular-component-creator is a skill for authoring or editing custom Angular components in Datex Studio. For developers building bespoke interfaces, it uses dxs ng to materialize a local harness and preview screenshots before pushing components to Studio; typed datasources provide real app data while previews use mocks.

Key features

  1. 01Author standalone Angular components
  2. 02Create and pull components with dxs ng
  3. 03Preview a local harness with screenshots
  4. 04Embed existing app configuration components
  5. 05Use typed datasources with mocked preview data
  6. 06Push components to Datex Studio

Use cases

  1. 01Build bespoke charts, dashboards, or visualizations
  2. 02Embed generated controls in custom interfaces
  3. 03Use a custom component as container tab content

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/datex/skills --skill custom-angular-component-creator

After installing, type/custom-angular-component-creatorin Claude Code to confirm it loaded.

Bundled filesreferences/custom-angular-components.md

Details

From the project README

Custom Angular Component Creator

Author or edit a Datex Studio Custom Angular Component (CAC, configurationTypeId: 36) on a branch. A CAC is an author-written standalone Angular component — you write the actual component.ts / .html / .scss, not a declarative JSON config. It runs inside the generated app with the real platform context ($datasources, $flows, $shell, …) injected, so it's the escape hatch for UI the declarative components (grid / form / hub / selector / editor) can't express: bespoke charts, dashboards, visualizations, custom layouts.

A CAC can also embed an existing app configuration in its template — declare it as a component reference (selectors first; the contract is generic over component kind) and codegen imports the generated component into the CAC's scope, so a real generated control (e.g. a selector dropdown) renders inside your custom UI.

Clues for embedding: a selector generates in two variants — _single and _multi — so a referenced selector gives you both a single- and a multi-select control to choose from. An embedded control exposes the configuration's own properties as component inputs/outputs (e.g. a selector surfaces each of its datasource parameters as an input named by the parameter id, plus its selected value and a display-text output). To find the exact tag and the inputs/outputs a referenced config exposes, read its generated component in the materialized harness — the @Component selector is the tag to use, and the @Input/@Output members are what you bind — rather than guessing them.

This skill uses a different CLI surface from every other creator. There is no dxs configuration upsert customangularcomponent hand-authoring path here — CAC authoring goes through the dxs ng command family, which materializes a light harness (the one component + real typed context + stub services, generated by the server for the branch), lets you edit the two author regions locally, and renders a screenshot each iteration. Nothing is created in Studio until dxs ng push.

Data pattern (fixed default — never ask)

Whenever a CAC displays data, that data comes from a real, typed datasource — and how it is wired is not a question to put to the user. Do not ask whether to use real data, sample data, an @Input, or how to source it: the pattern is fixed for every component.

  • Authoring loop (CLI / dxs ng preview): the component reads through the typed this.$datasources.<ref> stub, but the preview is served mocked data — dxs ng data generate seeds mocks/harness-mocks.json and you fill it with representative values (from a real dxs query). The authoring preview never hits real data.
  • After push (generated Studio app): the same typed $datasources.<ref> calls hit the real datasource / real data in the running app. The component always depends on the datasource at runtime.
  • Datasource missing on the branch? Create one that matches the real schema with datasource-creator (explore with schema-explorer first) and upsert it before materializing the harness, so the harness types the stub. Never fall back to embedded sample rows in the body or a cast ($datasources as any).

In one line: real typed datasource + mocked data for authoring, real data after push — applied to every CAC without asking. Sample data lives only in mocks/harness-mocks.json (transient, never pushed); the body stays clean with real empty/loading/error states. See Phase 3 for the mechanics.

CLI-first — no workarounds (hard rule)

The dxs CLI is the only sanctioned surface for this skill — every read and write of Studio state, and every step of the authoring loop (create/pull, data generate, preview, push), goes through dxs. When the CLI falls short, report the gap as a CLI problem to fix, don't route around it — a workaround that "gets the task done" hides a defect every later session will hit again.

  • Derive facts from dxs, not side channels. Field shapes, config contents, schema, branch state all have dxs commands. Plain head/grep over command output is fine; scripted parsing of config JSON (jq/python) or reading raw platform artifacts is not.
  • Derive server contracts from CLI-observable evidence. A validate/upsert rejection names the expected server type, and an existing valid config on the branch (dxs configuration get) shows the accepted shape — that pair is sufficient. Do not open the platform source repositories to reverse-engineer contracts.
  • A needed workaround is a bug report, not a technique. If the task seems to require killing stray processes, scripting around a CLI output format, driving a tool the CLI already wraps (e.g. screenshotting outside dxs ng preview), or hand-editing generated JSON — stop, tell the user what the CLI gap/bug is, and agree on next steps.
  • Hangs and orphaned processes are reportable defects. A dxs command that blocks the terminal, leaves children holding file locks, or leaks temp state is a CLI lifecycle bug. Record the exact symptom chain (command, error code, leftover process/file) so it gets fixed; unblocking kills are done after reporting, never as an unremarked routine.

One CAC per screen (hard rule)

A design/screen is authored as one CAC — never decomposed into multiple CACs composed together. Composition happens through embedded platform components (component references: selectors first) and $shell dialog openers, not CAC-in-CAC. If a design feels too big for one component, trim the design, not the component count.

Using a CAC as tab content

A CAC can be a tab's content in a hub, editor, card, or dashboard — the same slot that hosts grids and forms. In the container's tab designer, pick Custom Angular Component as the tab content type, reference the pushed CAC, and bind its @Inputs through the tab's parameters (the CAC's inParams/outParams are what the tab wires to). The platform generates a tab-content contract onto every CAC, which reserves three member names — refresh, $refreshEvent, and outParamsChange. Never declare an outParam or @Output with any of these names, and never redeclare the two generated @Outputs ($refreshEvent, outParamsChange) in the body — a collision is a duplicate-member compile error. The one exception is a body refresh() method, the sanctioned override: to re-load the CAC when the container refreshes, implement refresh() in the body region and it replaces the generated default (which re-runs ngOnInit()).

References

Dependencies

  • requirements-gathering skill — invoked to produce a brief (or capture the target screenshot) if one isn't already in the conversation context. A CAC's whole point is bespoke UI, so a concrete visual target is worth more here than for any other component.
  • schema-explorer skill — invoked when the component reads Footprint data through $datasources, to confirm the entities/fields exist before wiring them.
  • datasource-creator skill — invoked when the component needs a datasource/flow that doesn't exist yet on the branch (author it first, then reference it from the component and mock it for preview).

Prerequisites (one-time, host-provided)

The dxs ng loop needs a reachable Datex API — confirm these before starting (see references/custom-angular-components.md → Prerequisites):

  1. A reachable Datex API for the branch you're targeting. The harness endpoints run codegen server-side and are not environment-gated — the deployed image installs Node and codegen/node_modules in its runtime stage precisely so the API can run codegen on demand — so create / pull work against whichever Datex Application API the CLI is configured for, dev, qa or prod alike. (Not to be confused with app publish, which genuinely is dev-gated — outside Development it is delegated to an Azure DevOps pipeline. That gate does not apply to the harness endpoints.)
  2. Authenticated — dxs auth status shows a signed-in identity.
  3. agent-browser (headless screenshots) — installed from the unscoped npm package: npm install -g agent-browser then agent-browser install. (It is not @anthropic-ai/agent-browser — that name 404s.)

CLI Lifecycle — the dxs ng family

CommandOnline?Touches Studio?What it does
dxs ng create <Name> -b <branch> [-d <dir>]yesnoBuild a starter config → server generates the light harness for the branch → materialize <name>/angularapp/… + manifest.json + mocks/ locally. Nothing is created in Studio.
dxs ng pull <name> -b <branch> [-d <dir>] [--force]yesnoSame materialization for an existing CAC (the edit-existing entry point). An existing folder is a structured DXS-NG-048 error; --force deletes the whole folder (stopping a running preview server itself first) and re-materializes from server truth.
dxs ng data generate <folder> -b <branch>yesnoSeed <folder>/mocks/harness-mocks.json with typed placeholders for the $datasources/$flows the component uses.
dxs ng preview <folder> [-o out.png] [--refresh] [--clean]no (local)noServe the harness locally and screenshot the component → <folder>/render.png. --refresh -b <branch> re-fetches the harness after a manifest/IO change; --clean resets a stuck server and tears down the agent-browser session (daemon, Chrome tree, stale state files).
dxs ng push <folder> -b <branch>yesyesExtract the two regions + manifest.json → upsert type-36. First push creates the component in Studio; the server validates on upsert (hard gate).
dxs ng stop <folder>no (local)noStop the folder's warm preview dev server + its agent-browser session (identity-checked lock kill, lock removed). Idempotent (stopped: false when nothing runs) — the disposal step when you're done; never read the lock or taskkill by hand.
dxs ng list -b <branch>yesnoList the branch's type-36 components.

The <Name> you pass to create is PascalCase (OutboundCommandCenter); the reference name becomes camelCase (outboundCommandCenter), the selector is kebab-case app-outbound-command-center (auto-derived — what create reports and manifest.json stores), and the files are app.outboundCommandCenter.component.{ts,html,scss}.

Workflow

[Phase 1: Setup + Requirements]
Follow branch-setup.md for branch selection (NEVER assume a branch ID)
Confirm prerequisites (API reachable, auth, agent-browser)
        |
[requirements brief / TARGET SCREENSHOT in context?]
  +-----+-----+
  |            |
 YES          NO -> invoke `requirements-gathering` (capture the visual target)
  +-----+------+
        |
[Phase 2: Materialize the harness]
dxs ng create <Name> -b <branch>        (new component)
   -- or --
dxs ng pull <name> -b <branch>          (edit an existing one)
        |
[Phase 3: Author the two regions]
Edit <name>/angularapp/src/app/app.<ref>.component.ts:
   __COMPONENT_TYPES__  region -> imports / interfaces / types
   __COMPONENT_BODY__   region -> fields / methods / getters / lifecycle
Edit app.<ref>.component.html and .scss.
NEVER touch the wrapper class line, the @Component decorator, the
constructor, or the //#region ... //#endregion sentinels.
        |
[real data?] -> dxs ng data generate <folder> -b <branch>   (seed mocks)
        |
[Phase 4: Preview loop (screenshot-driven)]
dxs ng preview <folder>        -> writes <folder>/render.png
Read render.png. Compare to the target. Edit regions. Re-preview.
Repeat until it matches. (First preview ~15-46s cold; each later ~10s.)
        |
[Phase 5: Push + verify]
dxs ng push <folder> -b <branch>     (creates in Studio; server validates)
dxs ng list -b <branch>              (confirm it landed)
dxs ng pull <name> -b <branch> -d ./roundtrip   (optional round-trip check)

Phase Details

Phase 1: Setup + Requirements
  1. Follow ../datex-studio-shared/branch-setup.md for branch selection. Never assume or reuse a branch ID — ask the user to confirm, even if one appeared earlier in the session. Confirm the prerequisites above.
  2. Check for a requirements brief in context. For a CAC, the highest-value input is a concrete visual target — a target screenshot or a precise description of the layout, data, and interactions. If none exists, invoke requirements-gathering. A target screenshot turns Phase 4 into an objective converge-to-target loop.
Phase 2: Materialize the harness

Run dxs ng create <Name> -b <branch> (new) or dxs ng pull <name> -b <branch> (existing). Pass -d components so the working copy lands under a shared components/<Name>/ directory (the convention for CAC working folders) instead of the current directory. This asks the server to generate the light harness for the branch and materializes it locally:

<name>/
  angularapp/…       # the runnable light harness (one component + real typed context + stub services)
  manifest.json      # identity + IO (inputs/outputs) + datasource/flow refs + displayModes
  mocks/             # fixtures the harness $datasources/$flows read during preview

Nothing is created in Studio at this step. See references/custom-angular-components.md → The light harness.

Editing an existing component (pull). pull is the edit-existing entry point, and it round-trips your two author regions + .html/.scss faithfully from Studio. On an existing folder it raises a structured DXS-NG-048 error naming the two sanctioned outs — pick the sync direction explicitly:

  • Keep my local work, refresh the harness → dxs ng preview <folder> --refresh -b <branch> — the only region-preserving sync (keeps authored regions/template/styles, manifest.json, mocks/, node_modules).
  • Discard local, take server truth → dxs ng pull <name> -b <branch> -d <dir> --force — deletes the whole folder (mirrors dxs report download --force) and re-materializes. The CLI stops a still-running preview dev server itself via the identity-checked serve lock — never kill processes by hand — and refuses (DXS-NG-049) if the target doesn't look like a CAC working copy.

Three things never round-trip through the server, so after a --force pull restore them: (1) manifest.datasources comes back empty (the persisted config stores only identity + inParams/outParams) — re-add entries as objects { "name": "<ref>", "ref": "<ref>" }, never bare strings (strings fail manifest validation on the next dxs ng command); (2) mocks/ comes back empty — re-seed with dxs ng data generate + fill (or restore your prior file); (3) node_modules is gone — the next preview re-runs npm install (minutes). The first preview after re-adding manifest entries reports refetched: true (the manifest edit changes the IO signature, triggering an auto-rematerialize) — expected, not a bug.

Phase 3: Author the two regions

You edit only the two author regions inside <name>/angularapp/src/app/app.<ref>.component.ts, plus the sibling .html and .scss:

//#region __COMPONENT_TYPES__
//   imports, interfaces, type declarations  (spliced verbatim on push)
//#endregion __COMPONENT_TYPES__

// ...generated @Component + class wrapper + constructor (DO NOT EDIT)...

  //#region __COMPONENT_BODY__
  //   fields, methods, getters, lifecycle body  (spliced verbatim on push)
  //#endregion __COMPONENT_BODY__

Never change the wrapper class line, the @Component decorator, the constructor, or the //#region … //#endregion sentinel lines — push extracts your work from between the sentinels, so damaging them breaks extraction. The constructor already injects the real context ($datasources, $flows, $shell, $utils, $settings, $reports, $localization, $operations, $userSettings, $frontendFlows) with real branch types, so body code uses this.$datasources.… etc. — never raw HttpClient. SharedModule is imported, so Angular directives (*ngFor, *ngIf, [ngClass], [style.*]) and Material/AG-Grid/ApexCharts are available in the template.

Style to the Datex design system — a CAC gets no styling for free, so it looks foreign unless you apply it. The non-negotiables: mirror, don't invent (copy the closest existing component's markup + classes); use var(--…) theme tokens, never a hard-coded hex (a hex breaks dark mode and the token the filled-control system pivots on); compose the real datex-* class names (datex-button primary, field-container, grid-table-*, card datex-card, widget-container, …) rather than bespoke CSS; Fluent icons only on <i>; sentence case, one primary button on screen. For the token variables, the full component class list, and the compiled-CSS traps, read ../datex-studio-shared/design-system/ (start with 02-tokens, 03-components, 06-traps).

If the component reads real data, run dxs ng data generate <folder> -b <branch> to seed mocks/harness-mocks.json, then fill in realistic values so the preview renders with representative data. Invoke schema-explorer first if you're unsure the datasource/entity exists; invoke datasource-creator if it needs to be authored.

A manifest IO change (new @Input/@Output) needs codegen wiring — edit manifest.json, then re-preview with --refresh -b <branch>; the regenerated harness types this.inParams.<name> and emits through this.<output>. Region/template/style edits stay fully local (no --refresh).

Lifecycle hooks go in the body. Implement ngOnInit / ngOnChanges / ngOnDestroy directly in __COMPONENT_BODY__ (e.g. async ngOnInit() that loads data). Codegen emits its own stub only for the hooks you don't implement, so a refetch reconciles them — but a plain preview with no IO change won't refetch, leaving your hook duplicating the still-present generated stub (a compile error). So add the hook together with the IO change that triggers a refetch, or run preview --refresh -b <branch> once after adding it. For in-component interactive state that isn't an @Input (e.g. a mode toggle), keep a plain field seeded from the input in ngOnInit — no manifest change, so the preview stays warm.

Datasources — create first, reference with typed access, no fixtures. A datasource is a real branch config: author it with datasource-creator (dxs datasource generate → validate → dxs configuration upsert datasource) before you materialize the harness, so the generated harness types the this.$datasources.<ref> stub. manifest.datasources only declares the dependency — it never creates the datasource. Read it with typed access (await this.$datasources.<ref>.getList({…})), never a cast. If the datasource is missing, that typed reference is a compile error — and that fail-fast is exactly what you want: fix the wiring (create/upsert it, then re-materialize), do not cast around it ($datasources as any) or fall back to embedded sample data. Added the datasource after materializing and the stub hasn't appeared? Re-materialize fresh (dxs ng pull / preview --refresh -b <branch>) and confirm it's listed in angularapp/src/app/app.datasource.index.ts before relying on it. Sample/preview data belongs only in mocks/harness-mocks.json (transient, never pushed) — never in the component body, which is spliced verbatim into the Studio config. To learn the datasource's field shape for your row mapping, use the CLI — never hand-parse the config JSON: since you upsert before materializing anyway, read the authoritative shape with dxs report datasource-fields <ref> -b <branch> (result type + params + flat field paths + collections), and validate the local file with dxs datasource validate <file> -b <branch> (it exits 1 when it finds errors — that is validation reporting findings, not a broken CLI: read validation_errors, fix, re-run); don't jq/python fields out of outParams/queryOptionsObjectTypeDef, and remember this.$datasources.<ref> is already typed after dxs ng create. See references/custom-angular-components.md → Reading real data.

Phase 4: Preview loop (screenshot-driven)
dxs ng preview <folder>          # -> <folder>/render.png

Read render.png, compare it to the target, edit the regions/template/styles, and re-run preview. This is the core loop and where an agent earns its keep: read the PNG, diff it against the target screenshot, adjust, repeat until it matches. Type/template errors surface here too (the harness compiles the real component). Timings (local): first preview after create is a one-time npm install (minutes) + a ~15-46s compile; every preview after that is ~10s, and an edit hot-reloads in ~4s. The cold-compile wait is handled internally (no timeout knob); use --clean if the server or browser session wedges — it resets both, including stale agent-browser session state from crashed runs.

preview captures the component's default rendered state — it doesn't click. For a mode/variant switched by in-component UI (rather than an @Input), temporarily set that default (or drive it from an @Input/mock) to screenshot each variant. For genuine interaction states (click-to-select, toggles, panels), drive agent-browser against the served harness instead: read the port from <folder>/.dxs-serve.lock, then agent-browser open http://127.0.0.1:<port> → wait '<css>' → click '<css>' → screenshot out.png — this verifies the real handler wiring, not a simulated default.

Phase 5: Push + verify
dxs ng push <folder> -b <branch>     # first push CREATES it in Studio; server validates on upsert
dxs ng list -b <branch>              # confirm it appears
dxs ng pull <name> -b <branch> -d ./roundtrip   # optional: confirm the regions round-trip
dxs ng stop <folder>                 # done for now? dispose of the warm dev server + browser session

push is the only command that writes to Studio. The server runs the configuration validator on upsert (a hard gate — it recognizes CAC configs); a component that references a datasource that doesn't exist on the branch, or otherwise fails validation, is rejected. Fix what it reports and push again. push also fails locally (before any network call) if a region sentinel is damaged — that's the "don't touch the sentinels" rule biting.

Datasource connection preflight (DXS-NG-047). Before upserting, push verifies every own-branch datasource the CAC uses — the union of manifest.datasources and the unqualified $datasources.<ref> calls in the component body (so a stale manifest can't blind it) — exists on the branch and names an API connection setting the branch actually defines. This matters because the harness mocks never touch a connection: preview renders green even when Studio would flag "Missing API Connection setting <name>" on the datasource, and a green push must not imply the real codegen build works. On DXS-NG-047: check dxs source branch settings <branch>; regenerate the datasource against this branch with dxs datasource generate (it auto-resolves the setting); if the app has no API connection at all, wire one in Studio first (the CLI never creates connections). --skip-connection-check bypasses the gate for deliberate mock-only development on an app with no connections yet. Package datasources (Module/ref, $datasources.<Module>.<ref>) are exempt — their settings resolve via the reference remap.

Pre-Flight Checklist

  1. Branch confirmed with the user (never assumed); prerequisites up (API reachable, auth, agent-browser).
  2. Only the two regions + .html + .scss were edited — the wrapper class line, @Component, constructor, and //#region sentinels are untouched.
  3. No raw HttpClient / direct backend calls — data comes through this.$datasources / this.$flows; UI actions through $shell / $flows (see calling-conventions).
  4. Mocks seeded (dxs ng data generate) and filled with representative values if the component reads $datasources/$flows, so the preview renders real-looking data.
  5. manifest.json IO changes are followed by preview --refresh -b <branch> (new @Input/@Output needs codegen re-wiring). A referenced datasource must already exist on the branch (create it with datasource-creator first, ideally before materializing) and is read with typed $datasources access — a missing datasource is a compile error to fix, never something to cast around; sample data stays in mocks/. See the datasource note in Phase 3.
  6. The preview PNG matches the target — the loop converged, and no compile/template errors remain in the render.
  7. displayName ≤ 100 chars — it becomes the config title/description (there is no separate manifest description field).
  8. Verified after push — dxs ng list -b <branch> shows it; optionally dxs ng pull … -d ./roundtrip round-trips the regions.

Cleanup — working copies are heavy

A materialized working folder weighs ~700 MB, ~85% of it angularapp/node_modules. The folder is not the deliverable — after push, the config in Studio is.

After a successful push (verified via dxs ng list -b <branch>), ask the user whether to clean up — never silently. First stop the warm dev server and its agent-browser session with dxs ng stop <folder> — the identity-checked teardown (PID + image verified, never a blind kill), including the browser session/daemon and the lock file; idempotent, so call it unconditionally. Never read .dxs-serve.lock or taskkill by hand — on CLIs that predate stop, that manual dance was the workaround, not the method. (dxs ng preview <folder> --clean is the OTHER lifecycle verb: a reset, not a stop — same teardown, then a fresh server; use it for a wedged preview, stop for disposal.) Then offer the two tiers below. Reclaim disk in two tiers:

  • Prune (default between sessions): delete <folder>/angularapp/node_modules (~600 MB), <folder>/angularapp/.angular (build cache, ~100 MB), and the <folder>/.dxs-install-ok sentinel (it pairs with node_modules — preview reinstalls unless both are present). Everything authored stays resumable at ~16 MB; the next preview re-runs npm install (minutes) and a cold compile. Zero data-loss risk.
  • Full delete (done with the component): only after confirming the config is in Studio (dxs ng list -b <branch>). If the goal is a fresh working copy, don't delete by hand at all — dxs ng pull <name> -b <branch> -d <dir> --force does the whole thing (stops the running dev server itself, deletes, re-materializes); a residual locked-file failure surfaces as a structured DXS-NG-049. Deleting without re-pulling (final disposal): dxs ng stop <folder> first, then delete the folder. Rare residual case: a previously crashed server can leave orphaned children (node.exe, esbuild.exe) the lock no longer tracks, so if the delete still fails with locked files, sweep processes whose command line references the folder — and report it as a CLI lifecycle bug. dxs ng pull re-materializes the two regions + .html/.scss faithfully, but mocks/harness-mocks.json, captured target/notes files, and manifest.datasources/componentRefs do not round-trip — re-seed/re-add them after a re-pull.

Never full-delete an unpushed working copy — the authored regions exist nowhere else.

Common Mistakes

MistakeFix
Asking the user how the component should get its data (real vs sample, @Input vs datasource)Don't ask — the data pattern is fixed. Real typed datasource + mocks/ data for the authoring preview; real data after push. See Data pattern (fixed default — never ask).
Hand-authoring a JSON body and dxs configuration upsert customangularcomponentCACs are authored through dxs ng (regions + harness + preview), not a JSON round-trip. Use dxs ng create / preview / push.
Editing outside the two regions (touching the wrapper class, @Component, constructor, or the //#region sentinels)push extracts your code from between the sentinels. Edits elsewhere are lost on push or break extraction. Keep to __COMPONENT_TYPES__, __COMPONENT_BODY__, .html, .scss.
Installing @anthropic-ai/agent-browser (404)The package is the unscoped agent-browser: npm i -g agent-browser && agent-browser install.
preview reports ng serve did not become ready (DXS-NG-042)Usually the first cold compile running long — extend with DXS_NG_SERVE_TIMEOUT (seconds), re-run, --clean to reset a stuck server. On CLI ≤0.4.13 a relative folder argument also caused this (the dev server died instantly on a mis-resolved script path; fixed since — pass an absolute path as the workaround there). If it persists on a current CLI, it IS a real compile problem — inspect the ng build.
preview fails with DXS-NG-053 (agent-browser 'wait' timed out) — assuming the component is brokenA screenshot timeout ≠ a render failure: the component usually compiled + served fine (contrast DXS-NG-042 ng serve did not become ready, which is the compile failure). A warm serve can also be stale. Re-run with --clean, then verify the real render by opening the .dxs-serve.lock port with agent-browser and checking app-<ref> has content — don't chase phantom code bugs.
Expecting to preview/open OTHER components (or $shell dialogs to them)The harness makes only YOUR candidate a real component; $shell.open<X>Dialog(...) to other components are compilable stubs that won't open in preview. $datasources/$flows (real branch) and your own UI are fully live.
Expecting the component to appear in Studio after create / previewcreate/pull/data generate/preview are all transient. Only push writes to Studio (first push creates).
Reaching for HttpClient or fetch in the bodyUse the injected $datasources / $flows; they're real, typed, and mockable for preview. Raw HTTP won't have the branch's auth/context.
Component uses a datasource but preview renders emptyRun dxs ng data generate and fill mocks/harness-mocks.json — the harness $datasources/$flows read those fixtures during preview (no backend).
Added an @Input/@Output but preview doesn't reflect itIO changes need codegen re-wiring: update manifest.json, then dxs ng preview <folder> --refresh -b <branch>.
Expecting manifest.datasources to create or wire a datasourceIt does neither. Create the datasource first with datasource-creator (dxs datasource generate/validate/configuration upsert datasource), ideally before materializing so the harness types the $datasources.<ref> stub; manifest.datasources only declares the dependency.
Casting around a missing datasource ($datasources as any) or fixture-fallbackAnti-pattern: it ships fake data to Studio and hides the wiring problem. A missing datasource should be a compile error — fix it by creating/upserting the datasource and re-materializing (pull/--refresh), then use typed this.$datasources.<ref>.
Embedding sample/fixture rows (or "replaced by the real datasource later" comments) in the bodyThe body is spliced verbatim into the pushed type-36 config, so fixtures + WIP comments become production. Keep the body clean — typed datasource read + real empty/loading/error states; sample data lives only in mocks/harness-mocks.json.
Hand-parsing the datasource config JSON (jq/python, or eyeballing outParams/queryOptionsObjectTypeDef) to learn its field shapeUse the CLI. Upsert first (required before materializing anyway), then dxs report datasource-fields <ref> -b <branch> for the field list; dxs datasource validate/context <file> -b <branch> for the local file. After dxs ng create, this.$datasources.<ref> is typed — you rarely need a hand-written row type. Never script the JSON.
--refresh render looks clean, but a later preview shows a compile errorA --refresh restart can screenshot before the recompile error overlay renders. Confirm with a second plain preview, and grep the harness app.datasource.index.ts to see which datasources are actually wired.
push rejected by the serverIt ran the validator (hard gate). Fix what it reports (commonly a datasource ref missing on the branch — invoke schema-explorer/datasource-creator) and push again.
push fails with DXS-NG-047 (datasource connection preflight)The CAC uses a datasource that's missing on the branch or whose apiSettingName names an API connection setting the branch doesn't define — Studio would flag it even though preview rendered fine (mocks never touch connections). Regenerate the datasource against this branch (dxs datasource generate auto-resolves the setting), or wire a connection in Studio first; --skip-connection-check only for deliberate mock-only development. See Phase 5.
Trusting a green preview as proof the datasource wiring works in StudioPreview data comes from mocks/ — the harness never touches an API connection, so broken apiSettingName wiring is invisible until Studio (or the push preflight) flags it. The DXS-NG-047 gate on push is the honest check; don't bypass it casually.
push fails locally before any network callA region sentinel was damaged. Restore the //#region __COMPONENT_TYPES__/__COMPONENT_BODY__ markers exactly, or re-pull and re-apply your edits.
pull came back with an empty mocks/ and no manifest.datasources (editing an existing CAC)Expected — three things never round-trip: manifest.datasources, mocks/, node_modules. Re-add datasources (objects, not strings), re-seed mocks/ (data generate + fill), let preview reinstall. The two regions + .html/.scss round-trip fine. See Phase 2.
Hand-deleting the working folder (or killing dev-server processes) to get past pull's existing-folder errorUse the flags, not manual cleanup: preview --refresh keeps your local work; pull --force discards it — and stops the running dev server itself (identity-checked lock kill). A residual locked-file failure is a structured DXS-NG-049, not a raw WinError.
Reading .dxs-serve.lock and taskkill-ing the dev server by hand when done previewingThat was the pre-stop workaround. dxs ng stop <folder> is the disposal verb: identity-checked tree-kill + agent-browser session teardown + lock removal, idempotent (stopped: false when nothing runs). The full lifecycle is CLI-owned: pull --force disposes on replace, preview --clean resets, stop disposes.
Writing manifest.datasources entries as bare ref stringsEntries are ManifestDatasourceRef objects: { "name": "<ref>", "ref": "<ref>" }. Bare strings fail manifest validation on the next dxs ng command (data generate, preview, push).
push fails with DXS-API-TIMEOUT on .../config/<ref>/lock (~600s)Not necessarily a hang or stale lock: a dev running the API under a debugger may be paused on a breakpoint on the lock/upsert path, or the token lapsed mid-call. Continue past the breakpoint (or re-dxs auth login), then retry push.
dxs ng data generate leaves a $frontendFlows ref or an embedded componentRef selector's datasource with no mock keyThe generator doesn't seed every key: $frontendFlows refs aren't seeded at all, and it silently skips a componentRef selector's backing datasource when branch resolution fails during generation (no error). After generating, open mocks/harness-mocks.json and add any missing keys by hand.
Seeding a mock value for a $frontendFlows ref and expecting the preview to use it$frontendFlows run as real, computed client-side code in the harness — never mocked. The preview always shows the flow's actual computed result; a mock entry for a frontendFlow key is inert.
A full-page/dashboard CAC's render.png looks cut off partway downThe platform shell forces html, body { overflow: hidden }, so preview's screenshot is capped to one viewport (commonly ~569px) — it can't see past your own inner overflow-y: auto container no matter how tall the content is. This is a platform-shell constraint, not a bug in your layout. To verify content beyond the first viewport, drive agent-browser directly against the harness's served port with a taller viewport instead of relying on render.png alone.
Using dxs source explore configs/trace to confirm a CAC exists or to trace its referencesNeither command indexes type-36 components — there's no CAC/customangularcomponent category in their type filters or output. Verify presence with dxs ng list -b <branch>, and trace declared datasource/flow/componentRefs via dxs configuration get customangularcomponent <id> -b <branch> or by reading the manifest/harness source directly.
Declaring an outParam or @Output named refresh / $refreshEvent / outParamsChange, or redeclaring the two generated @Outputs in the bodyReserved — the platform generates these on every CAC (the tab-content contract that lets a CAC be a hub/editor/card/dashboard tab); a collision is a duplicate-member compile error. The exception is a body refresh() method — the sanctioned override; it replaces the generated default (which re-runs ngOnInit).

A CAC's value is bespoke visuals — treat the preview screenshot as the acceptance test, and converge it to the target before pushing.

Comments

Markdown supported