--- name: javascript-actions description: "Create, call and drop JavaScript actions — client-side logic called from nanoflows, not microflows. Use when behaviour must run in the browser or on the device: DOM, fetch, geolocation, or wrapping a marketplace JS action." --- # Mendix JavaScript Actions Skill Guidance for creating, calling, and dropping **JavaScript actions** in Mendix projects with MDL. JavaScript actions hold custom **client-side** logic and are called from **nanoflows** (not microflows — those use Java actions). ## When to Use This Skill - Adding client-side behaviour (browser/native APIs, DOM, `fetch`, geolocation, …) - Logic that must run on the device rather than the server - Calling an existing JavaScript action from a nanoflow - Wrapping a marketplace/native capability exposed as a JS action For **server-side** custom code, use Java actions instead (see `java-actions`). ## Overview A JavaScript action is two things on disk: 1. The model unit (`JavaScriptActions$JavaScriptAction`). 2. A source file `javascriptsource//actions/.js` containing an exported `async function`. `CREATE JAVASCRIPT ACTION` writes **both**. The MDL body you provide becomes the function body (between the `BEGIN USER CODE` / `END USER CODE` markers), so it normally returns a `Promise`. ## Syntax ```sql CREATE [OR MODIFY] JAVASCRIPT ACTION Module.Name ( parameters ) RETURNS type [EXPOSED AS 'caption' IN 'category'] [PLATFORM Web | Native | Hybrid | All] -- default Web AS $$ $$; DROP JAVASCRIPT ACTION Module.Name; ``` Clause order is fixed: `returns`, then `exposed as`, then `platform`, then `as`. - **`AS $$ ... $$` is mandatory.** Omitting the body causes `no viable alternative at input '...'`. Use a stub: `AS $$ return Promise.resolve(false); $$`. - **`PLATFORM`** defaults to `Web`. Use `Native`, `Hybrid`, or `All` to widen. - **`OR MODIFY`** updates an existing action in place (UUID preserved); without it, a duplicate name is an error (`use create or modify to overwrite`). ### Parameter types Same type system as Java actions: - Primitives: `String`, `Integer`, `Long`, `Decimal`, `Boolean`, `DateTime` - Entity: `Module.EntityName` · List: `List of Module.EntityName` - Enumeration: `ENUM Module.EnumName` - Type parameter (generics): declare `EntityType: ENTITY `, then use the bare `pEntity` for instance parameters - Append `NOT NULL` to mark a parameter required. ## Examples Simple action (Web default): ```sql mdl 1; CREATE JAVASCRIPT ACTION MyFirstModule.JSA_IsOnline () RETURNS Boolean AS $$ return Promise.resolve(navigator.onLine); $$; ``` Parameters + return: ```sql mdl 1; CREATE JAVASCRIPT ACTION MyFirstModule.JSA_Add ( A: Integer NOT NULL, B: Integer NOT NULL ) RETURNS Integer AS $$ return Promise.resolve(A + B); $$; ``` Exposed toolbox action, native platform: ```sql mdl 1; CREATE JAVASCRIPT ACTION MyFirstModule.JSA_ShowToast ( Message: String NOT NULL, Duration: Integer ) RETURNS Boolean EXPOSED AS 'Show Toast' IN 'UI' PLATFORM Native AS $$ console.log(Message); return Promise.resolve(true); $$; ``` Idempotent update (UUID preserved): ```sql mdl 1; CREATE OR MODIFY JAVASCRIPT ACTION MyFirstModule.JSA_Add ( A: Integer NOT NULL, B: Integer NOT NULL, C: Integer ) RETURNS Integer AS $$ return Promise.resolve(A + B + (C || 0)); $$; ``` Drop (removes the unit and the `.js` file): ```sql mdl 1; DROP JAVASCRIPT ACTION MyFirstModule.JSA_Add; ``` ## Calling from a nanoflow JavaScript actions are invoked with `CALL JAVASCRIPT ACTION` inside a nanoflow body (arguments use `Name = value`): ```sql $Sum = call javascript action MyFirstModule.JSA_Add(A = 2, B = 3); ``` This is the nanoflow counterpart of `CALL JAVA ACTION` (microflows). Use it in a `CREATE NANOFLOW ... BEGIN ... END;` body. ## The generated .js file `CREATE` writes (and `OR MODIFY` rewrites) `javascriptsource//actions/.js`: ```javascript // This file was generated by Mendix Studio Pro. // ... // BEGIN EXTRA CODE // END EXTRA CODE /** * @param {number} A * @param {number} B * @returns {Promise.} */ export async function JSA_Add(A, B) { // BEGIN USER CODE return Promise.resolve(A + B); // END USER CODE } ``` Only the code between the `USER CODE` / `EXTRA CODE` markers is preserved when Studio Pro regenerates the file. The MDL `$$ body $$` lands inside `USER CODE`. ## Inspecting ```sql LIST JAVASCRIPT ACTIONS [IN Module]; DESCRIBE JAVASCRIPT ACTION Module.Name; -- re-executable MDL (signature + body) ``` `DESCRIBE` output round-trips: it re-parses as a `CREATE JAVASCRIPT ACTION`. ## Validation Checklist - [ ] Body present: `AS $$ ... $$;` — never omit it - [ ] Body returns a `Promise` (the function is `async`) - [ ] Clause order: `returns` → `exposed as` → `platform` → `as` - [ ] Required parameters marked `NOT NULL` - [ ] Use `CALL JAVASCRIPT ACTION` from **nanoflows**, `CALL JAVA ACTION` from microflows - [ ] Validate before applying: `mxcli check script.mdl` ## Common Errors | Symptom | Cause | Fix | |---|---|---| | `no viable alternative at input '...'` | Missing `AS $$ ... $$` body | Add a body (stub `return Promise.resolve(false);`) | | `already exists — use create or modify to overwrite` | Duplicate name without `OR MODIFY` | Add `OR MODIFY` | | Action runs nowhere / wrong client | `PLATFORM` too narrow | Set `PLATFORM All` (or the right target) | | `mismatched input 'platform'` | Clause out of order | Put `platform` after `exposed as`, before `as` | ## Related Documentation - `java-actions` — server-side custom code (microflows) - `write-nanoflows` — nanoflow syntax and restrictions - `mxcli syntax javascript-action` — quick syntax reference