Skip to content

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.

See also