Skip to content

05 Push WebResources

RemyDuijkeren edited this page Jul 18, 2026 · 4 revisions

Push WebResources

flowline push does more than copy files to Dataverse: it reads // flowline:... comment annotations straight out of your built dist/ files and uses them to auto-wire Dataverse metadata that would otherwise mean a Maker Portal visit — Form Event Handlers, Form Library entries, and web resource dependencies — then syncs dist/ to Dataverse to match exactly.

See 08-WebResources-Project for how to scaffold the project and write the scripts themselves, or 04-Push-Plugins-and-Custom-APIs for the plugin-side equivalent of this page.


Form event registration

Normally you'd wire a script up to a form by hand in the maker portal's Configure Event dialog. Flowline lets you declare it from your source instead, with a single-line comment above the handler or on top of the file — no maker portal visit required:

// flowline:onload account "Account Main"
// flowline:onsave account "Account Main"
// flowline:onload account "Account Main" onLoadAccount
// flowline:onload account "Account Main" onLoadAccount(param1,param2)

export function onLoad(executionContext) { ... }
export function onSave(executionContext) { ... }
export function onLoadAccount(executionContext) { ... }

flowline push reads these and adds, updates, or removes the corresponding Form Event Handler and Form Library entries on the form automatically, keeping them in sync with your source on every push.

Syntax

// flowline:onload <entity> <form> [order:N][bulkEdit] [Function[(params)]]
// flowline:onsave <entity> <form> [order:N] [Function[(params)]]
// flowline:onchange <entity> <form> <attribute> [order:N] [Function[(params)]]
// flowline:tabstatechange <entity> <form> <tabname> [order:N] [Function[(params)]]
// flowline:onreadystatecomplete <entity> <form> <controlname> [order:N] [Function[(params)]]

onchange, tabstatechange, and onreadystatecomplete each wire up one extra thing — a field, a tab, or an IFRAME control — with one extra token identifying it:

// flowline:onchange account "Account Main" creditlimit
export function onCreditLimitChange(executionContext) { ... }

// flowline:tabstatechange account "Account Main" Summary
export function onSummaryTabStateChange(executionContext) { ... }

// flowline:onreadystatecomplete account "Account Main" myFrame
export function onMyFrameReadyStateComplete(executionContext) { ... }
  • <entity> — the table's logical name (e.g. account).
  • <form> — the form's display name, exactly as it appears in the form editor. Quote it if it contains spaces: "Account Main".
  • <attribute>onchange only. The field's logical name (e.g. creditlimit).
  • <tabname>tabstatechange only. The tab's control's Name as it appears in the form editor's control properties.
  • <controlname>onreadystatecomplete only. The IFRAME control's Name as shown in the form editor's control properties. The form editor always renders IFRAME_ as a fixed prefix there, so you can write just the suffix (myFrame) or the full id (IFRAME_myFrame) — both resolve to the same control.
  • Function — optional. Defaults to onLoad/onSave, on<Attribute>Change, on<TabName>TabStateChange, or on<ControlName>ReadyStateComplete. Name it explicitly to use a different exported function.
  • (param1,param2) — optional comma-separated parameter list passed to the handler, same as the Dataverse form editor's own "Comma separated list of parameters" field.
  • Only Main and Quick Create forms are supported.
  • Multiple annotations per file are allowed, anywhere in the file.
  • Also recognized: //! flowline:onload ... and /*! flowline:onload ... */ (same for the other directives) — the ! forms survive minification, same as flowline:depends.

For the rare case that needs more control, add modifiers between the scope token and the function name: [bulkEdit] (onload only — also runs the handler during bulk edit form) or [order:N] (forces run order when multiple handlers share an event, lower N first).

If a form gets renamed

Renaming a form in the maker portal doesn't touch your annotation, so the next push fails with form '<form>' not found for entity '<entity>'. Update the annotation to the new form name and push again. files are deduplicated automatically.


Web resource dependencies

Dataverse has a built-in web resource dependencies feature: associate one web resource with another so the platform loads them together — a shared utility, a library from another solution, or localization strings a script needs — without you registering each one separately wherever the script is used.

Flowline lets you declare these dependencies easily from your source, instead of clicking through the web resource form in the maker portal. Declare it anywhere in the file, but preferably at the top of the file:

// flowline:depends av_ext/shared-library.js

export function onLoad(executionContext) { ... }

flowline push reads these annotations and registers the dependency in Dataverse. During flowline deploy, any resource listed in a // flowline:depends annotation is protected from orphan cleanup — it will not be deleted even if it is absent from your local solution.

Syntax

  • One logical name per line: // flowline:depends <webresource-logical-name>.
  • The logical name can include path separators: av_ext/lib.js, av_ext/strings.1033.resx.
  • Multiple annotations per file are allowed, anywhere in the file; duplicates across files are deduplicated automatically.
  • Also recognized: //! flowline:depends <name> and /*! flowline:depends <name> */ to help with survival during minification. The last form is the best way to ensure annotations survive minification.

RESX localization files

RESX files in your project that share a base name with a script are automatically registered as dependencies of that script — no annotation required:

Local file Auto-matched to
dist/av/AccountForm.1033.resx dist/av/AccountForm.js
dist/av/AccountForm.1041.resx dist/av/AccountForm.js

This covers the common pattern where a form script uses localized strings — the RESX loads alongside the script so the localized values are available to it. Auto-detection works on exact base-name matches regardless of folder depth:

  • One match found — RESX registered as a dependency automatically.
  • No match — Flowline warns and skips. Use an explicit // flowline:depends annotation instead.
  • Multiple matches — Flowline warns and skips. Use an explicit annotation to name the target.

If you reference a bare RESX name without an LCID suffix (e.g., // flowline:depends av_ext/strings.resx), Flowline expands it to all matching *.1033.resx, *.1041.resx, etc. variants present in the combined local and Dataverse resource set.


File type detection

Flowline determines a web resource's Dataverse type from its file extension — .js, .html, .css, and so on. Most projects never need to think about this.

For legacy files with no extension (or one Dataverse doesn't recognize), Flowline tries to work it out automatically and warns you when it does, recommending you rename the file with a proper extension. If it can't work it out, the push fails with an error.


Push

flowline push will build the project, then sync the contents of dist/ to Dataverse. It adds, updates, or removes web resources to match the local dist/ folder exactly, and registers any form event handlers or web resource dependencies declared in the source.

npm run build
flowline push --scope webresources

Or push everything (plugins + web resources) in one step:

flowline push

Building your project means calling dotnet build which runs npm run build automatically. If you already built and tested, --no-build skips the build and pushes the existing dist/.

See 03-Command-Reference#push for all push options including --dry-run and --no-delete.

Clone this wiki locally