AI blog · DotApp PHP Framework 2.0
How to use $dotapp() JavaScript in DotApp PHP Framework
Every Shop page that posts a form, calls Bridge, or talks to PHP with load() must include /assets/dotapp/dotapp.js.
That URL is generated by the framework — not a static file you copy, and not something you edit under app/parts/js/.
This article is the boot contract, the selector traps, load() + parseReply(), preloaders, halt(), file uploads, and a complete page.js.
Common mistakes
| Wrong | Right |
|---|---|
Serve a copied dotapp.js from the module assets folder. |
Always <script src="/assets/dotapp/dotapp.js"> from the framework route. |
Register a plugin on the dotapp event. |
Libraries call $dotapp().fn(...) on dotapp-register. Page logic waits for dotapp. |
Chain a getter: $dotapp('#x').val().addClass('ok'). |
Read into a variable, then act. Many getters return the value, not the instance. |
Bind row clicks with .on() before the list is patched. |
Use .live(event, selector, fn) for dynamic DOM. |
Treat the raw load() text as JSON, or location.reload() after success. |
parseReply() (base64 JSON from ajaxReply), then patch the DOM. |
Put a file in FormData on load() / <fo-rm>. |
$dotapp().uploadFile(file, url, progress), then parseReply on the text. |
Mandatory script
<script src="/assets/dotapp/dotapp.js"></script>
<script src="/assets/dotapp/dotapp.template.js"></script>
<script src="/assets/dotapp/dotapp.reactive.js"></script>
<script src="/assets/modules/Shop/js/page.js"></script>
Optional add-ons come after dotapp.js, module scripts last.
The generated script injects per-session key material. Without a usable HTTP_REFERER the route answers 404.
Without this script, <fo-rm> is never converted and every secure endpoint rejects the request.
How the channel works: How DotApp PHP Framework protects the browser-to-PHP channel.
Do not edit app/parts/js/. Put Shop code in app/modules/Shop/assets/js/.
Boot
(function () {
var runMe = function ($dotapp) { /* page logic */ };
if (window.$dotapp) runMe(window.$dotapp);
else window.addEventListener("dotapp", function () { runMe(window.$dotapp); }, { once: true });
})();
| Event | Use |
|---|---|
dotapp-register |
Register plugins via $dotapp().fn(...) (fires first). |
dotapp |
Application / page logic. |
dotapp-template-ready / dotapp-reactive-ready |
Official add-on loaded. |
$dotapp(selector) is an instance for the matched elements.
$dotapp() with no arguments is the singleton used for forms, load, Bridge, variables, and plugins.
Selector traps
| Trap | Reality |
|---|---|
| Getters chain | Many getters return the value, not this. |
.html() as getter |
Sets an internal last-result and returns this — read the DOM instead. |
.last() / .nth() |
Buggy in shipped builds — use .get(i) / .all(). |
.on() for rows that get replaced |
Use .live(event, selector, fn). |
| Empty selection | Getters may return null/false; html() can throw. |
var email = $dotapp('#login [name="email"]').val();
$dotapp('#login [name="email"]').addClass('ok');
Network: load() and parseReply()
$dotapp().load() adds CSRF fields, computes the integrity value, sets the dotapp: load header, and posts { data, crc }.
PHP must still crcCheck(). Raw fetch to a DotApp endpoint fails that check.
parseReply(text) is required because PHP ajaxReply() returns base64-encoded JSON.
Error callback codes at high level: 400, 403, 404, 429.
Forms vs load() vs Bridge: Secure backend-frontend communication in DotApp PHP Framework
and How to create secure forms in DotApp PHP Framework.
$dotapp().load(url, "POST", { id: $dotapp(el).attr("data-id") },
function (response) {
var reply = $dotapp().parseReply(response);
if (!reply || reply.status != 1) { return; }
},
function (code) { /* 400 / 403 / 404 / 429 */ }
);
There is no full-page reload. If the success callback does not update the DOM, the user sees nothing — that is a bug.
redirectTo plus window.location is only for leaving the page (login, wizard).
Overlay the region you mutate; patch a child node. Lists: How to build AJAX lists with pagination in DotApp PHP Framework.
Preloaders: loading / loader
You build region overlays in the module. On submit buttons, the script also understands loading and loader attributes.
Show them before the request; remove them on success and error.
$dotapp("#saveBtn").attr("loading", "true").attr("loader", "dots");
$dotapp("#saveBtn").removeAttr("loading").removeAttr("loader");
halt()
Return $dotapp().halt() from a form .before() hook to stop the submit (double-submit guard).
.before(function (data, form) {
if ($dotapp(form).attr("blocked") == 1) return $dotapp().halt();
$dotapp(form).attr("blocked", "1");
})
.after(function (data, response, form) {
$dotapp(form).attr("blocked", "0");
});
Files: uploadFile
load() and <fo-rm> post JSON { data, crc }. A file cannot ride that payload.
Use $dotapp().uploadFile(file, url, progressCallback) — a Promise of response text, then parseReply(text).
Drop zone: $dotapp().dragAndDropFile(dropZone, fileInput, callback, parallel) (it calls uploadFile).
PHP uses $request->upload() and must not crcCheck() on that endpoint. Reject executable uploads; frontend accept= is UX only.
Overlay the drop zone until the Promise settles. Return HTTP 200 with status 0|1 so the Promise resolves and you can toast reply.message.
Complete page.js for a Shop page
File: app/modules/Shop/assets/js/page.js.
Pair with a page that includes dotapp.js, a <fo-rm id="saveForm">, a list wrapper, and optional Bridge.
Bridge details: How to call PHP from JavaScript with DotBridge.
Polling UI: Reactivity in DotApp PHP Framework.
(function () {
var runMe = function ($dotapp) {
var listBusy = false;
function listDone() {
listBusy = false;
$dotapp("#listWrap").removeClass("shop_busy");
$dotapp("#saveBtn").removeAttr("loading").removeAttr("loader");
}
$dotapp("#saveForm")
.before(function (data, form) {
if ($dotapp(form).attr("blocked") == 1) return $dotapp().halt();
$dotapp(form).attr("blocked", "1");
$dotapp("#saveBtn").attr("loading", "true").attr("loader", "dots");
})
.after(function (data, response, form) {
var reply = $dotapp().parseReply(response);
if (reply && reply.status == 1) {
if (reply.html) $dotapp("#listInner").html(reply.html);
if (reply.message) $dotapp("#shopStatus").attr("hide", "false").text(reply.message);
} else if (reply && reply.message) {
$dotapp("#shopStatus").attr("hide", "false").text(reply.message);
}
$dotapp(form).attr("blocked", "0");
$dotapp("#saveBtn").removeAttr("loading").removeAttr("loader");
});
$dotapp().live("click", ".js-shop-page", function (e) {
if (listBusy) return;
listBusy = true;
$dotapp("#listWrap").addClass("shop_busy");
var page = parseInt($dotapp(e.currentTarget).attr("data-page"), 10) || 1;
$dotapp().load("/shop/items/list", "POST", { page: page, q: "" },
function (raw) {
var reply = $dotapp().parseReply(raw);
if (reply && reply.status == 1 && reply.html) $dotapp("#listInner").html(reply.html);
listDone();
},
function () { listDone(); }
);
});
$dotapp().live("change", "#shopPhoto", function (e) {
var file = e.currentTarget.files && e.currentTarget.files[0];
if (!file) return;
$dotapp("#shopDrop").addClass("shop_busy");
$dotapp().uploadFile(file, "/shop/photo", function (name, pct) {})
.then(function (text) {
var reply = $dotapp().parseReply(text);
if (reply && reply.message) $dotapp("#shopStatus").attr("hide", "false").text(reply.message);
$dotapp("#shopDrop").removeClass("shop_busy");
})
.catch(function () { $dotapp("#shopDrop").removeClass("shop_busy"); });
});
};
if (window.$dotapp) runMe(window.$dotapp);
else window.addEventListener("dotapp", function () { runMe(window.$dotapp); }, { once: true });
})();
FAQ
Is $dotapp('#x') the same as $dotapp()?
No. A selector returns a set of elements. The empty call is the singleton for load, bridge, parseReply, fn, and halt.
Where do I register $dotapp().fn('shopHighlight', ...)?
On dotapp-register, with an isRegistered guard. Duplicate fn('sameName') throws. Page code still listens to dotapp.
How do I read inner HTML?
Do not rely on .html() as a getter. Read el.innerHTML from .get(0), or keep values in variables you already know.
What is sendInput?
$dotapp().sendInput(groupName, url) posts an Input::group form. Ordinary Shop forms still prefer <fo-rm> plus formName.
How do I wrap the current node in a handler?
$dotapp(e.currentTarget) or $dotapp(this) when this is a DOM node — then read .attr / .val into a variable.