WoltLab Suite 6.2 Handbuch
Deutsch/PHP-Version: Die Website-Struktur, Navigation und Überschriften sind auf Deutsch vorbereitet. Code-Beispiele und technische Namen bleiben unverändert.

Ajax Requests - JavaScript API

Promise-based API for DatenbankObjectAction

WoltLab Suite 5.5 introduces a new API for Ajax requests that uses Promises to control the code flow.

It does not rely on references to existing objects and does not use arbitrary callbacks to handle the setup and handling of the request.

Usage

import * as Ajax from "./Ajax";

type ResponseGetLatestFoo = {
  template: string;
};

export class MyModule {
  private readonly bar: string;
  private readonly objectId: number;

  constructor(objectId: number, bar: string, buttonId: string) {
    this.bar = bar;
    this.objectId = objectId;

    const button = document.getElementById(buttonId);
    button?.addEventListener("click", (event) => void this.click(event));
  }

  async click(event: MouseEvent): Promise<void> {
    event.preventDefault();

    const button = event.currentTarget as HTMLElement;
    if (button.classList.contains("disabled")) {
      return;
    }
    button.classList.add("disabled");

    try {
      const response = (await Ajax.dboAction("getLatestFoo", "wcf\\data\\foo\\FooAction")
        .objectIds([this.objectId])
        .payload({ bar: this.bar })
        .dispatch()) as ResponseGetLatestFoo;

      document.getElementById("latestFoo")!.innerHTML = response.template;
    } finally {
      button.classList.remove("disabled");
    }
  }
}

export default MyModule;

The actual code to dispatch and evaluate a request is only four lines long and offers full IDE auto completion support.

This example uses a finally block to reset the button class once the request has finished, regardless of the result.

If you do not handle the errors (or chose not to handle _some_ errors), the global rejection handler will take care of this and show an dialog that informs about the failed request.

This mimics the behavior of the _ajaxFailure() callback in the legacy API.

Aborting in-flight requests

Sometimes new requests are dispatched against the same API before the response from the previous has arrived.

This applies to either long running requests or requests that are dispatched in rapid succession, for example, looking up values when the user is actively typing into a search field.

import * as Ajax from "./Ajax";

export class RapidRequests {
  private lastRequest: AbortController | undefined = undefined;

  constructor(inputId: string) {
    const input = document.getElementById(inputId) as HTMLInputElement;
    input.addEventListener("input", (event) => void this.input(event));
  }

  async input(event: Event): Promise<void> {
    event.preventDefault();

    const input = event.currentTarget as HTMLInputElement;
    const value = input.value.trim();

    if (this.lastRequest) {
      this.lastRequest.abort();
    }

    if (value) {
      const request = Ajax.dboAction("getSuggestions", "wcf\\data\\bar\\BarAction").payload({ value });
      this.lastRequest = request.getAbortController();

      const response = await request.dispatch();
      // Handle the response
    }
  }
}

export default RapidRequests;

Ajax inside Modules (Alte API)

The Ajax component was designed to be used from inside modules where an object

reference is used to delegate request callbacks. This is acomplished through

a set of magic methods that are automatically called when the request is created

or its state has changed.

_ajaxSetup()

The lazy initialization is performed upon the first invocation from the callee,

using the magic _ajaxSetup() method to retrieve the basic configuration for

this and any future requests.

The data returned by _ajaxSetup() is cached and the data will be used to

pre-populate the request data before sending it. The callee can overwrite any of

these properties. It is intended to reduce the overhead when issuing request

when these requests share the same properties, such as accessing the same endpoint.

// App/Foo.js
define(["Ajax"], function(Ajax) {
  "use strict";

  function Foo() {};
  Foo.prototype = {
    one: function() {
      // this will issue an ajax request with the parameter `value` set to `1`
      Ajax.api(this);
    },

    two: function() {
      // this request is almost identical to the one issued with `.one()`, but
      // the value is now set to `2` for this invocation only.
      Ajax.api(this, {
        parameters: {
          value: 2
        }
      });
    },

    _ajaxSetup: function() {
      return {
        data: {
          actionName: "makeSnafucated",
          className: "app\\data\\foo\\FooAction",
          parameters: {
            value: 1
          }
        }
      }
    }
  };

  return Foo;
});

Request Settings

The object returned by the aforementioned _ajaxSetup() callback can contain these

values:

data

_Defaults to {}._

A plain JavaScript object that contains the request data that represents the form

data of the request. The parameters key is recognized by the PHP Ajax API and

becomes accessible through $this->parameters.

contentType

_Defaults to application/x-www-form-urlencoded; charset=UTF-8._

The request content type, sets the Content-Type HTTP header if it is not empty.

responseType

_Defaults to application/json._

The server must respond with the Content-Type HTTP header set to this value,

otherwise the request will be treated as failed. Requests for application/json

will have the return body attempted to be evaluated as JSON.

Other content types will only be validated based on the HTTP header, but no

additional transformation is performed. For example, setting the responseType

to application/xml will check the HTTP header, but will not transform the

data parameter, you'll still receive a string in _ajaxSuccess!

type

_Defaults to POST._

The HTTP Verb used for this request.

url

_Defaults to an empty string._

Manual override for the request endpoint, it will be automatically set to the

Core API endpoint if left empty. If the Core API endpoint is used, the options

includeRequestedWith and withCredentials will be force-set to true.

withCredentials

!!! warning "Enabling this parameter for any domain other than the current will trigger a CORS preflight request."

_Defaults to false._

Include cookies with this requested, is always true when url is (implicitly)

set to the Core API endpoint.

autoAbort

_Defaults to false._

When set to true, any pending responses to earlier requests will be silently

discarded when issuing a new request. This only makes sense if the new request

is meant to completely replace the result of the previous one, regardless of its

reponse body.

Typical use-cases include input field with suggestions, where possible values

are requested from the server, but the input changed faster than the server was

able to reply. In this particular case the client is not interested in the result

for an earlier value, auto-aborting these requests avoids implementing this logic

in the requesting code.

ignoreError

_Defaults to false._

Any failing request will invoke the failure-callback to check if an error

message should be displayed. Enabling this option will suppress the general

error overlay that reports a failed request.

You can achieve the same result by returning false in the failure-callback.

silent

_Defaults to false._

Enabling this option will suppress the loading indicator overlay for this request,

other non-"silent" requests will still trigger the loading indicator.

includeRequestedWith

!!! warning "Enabling this parameter for any domain other than the current will trigger a CORS preflight request."

_Defaults to true._

Sets the custom HTTP header X-Requested-With: XMLHttpRequest for the request,

it is automatically set to true when url is pointing at the WSC API endpoint.

failure

_Defaults to null._

Optional callback function that will be invoked for requests that have failed

for one of these reasons:

1. The request timed out.

2. The HTTP status is not 2xx or 304.

3. A responseType was set, but the response HTTP header Content-Type did not match the expected value.

4. The responseType was set to application/json, but the response body was not valid JSON.

The callback function receives the parameter xhr (the XMLHttpRequest object)

and options (deep clone of the request parameters). If the callback returns

false, the general error overlay for failed requests will be suppressed.

There will be no error overlay if ignoreError is set to true or if the

request failed while attempting to evaluate the response body as JSON.

finalize

_Defaults to null._

Optional callback function that will be invoked once the request has completed,

regardless if it succeeded or failed. The only parameter it receives is

options (the request parameters object), but it does not receive the request's

XMLHttpRequest.

success

_Defaults to null._

This semi-optional callback function will always be set to _ajaxSuccess() when

invoking Ajax.api(). It receives four parameters:

1. data - The request's response body as a string, or a JavaScript object if

contentType was set to application/json.

2. responseText - The unmodified response body, it equals the value for data

for non-JSON requests.

3. xhr - The underlying XMLHttpRequest object.

4. requestData - The request parameters that were supplied when the request

was issued.

_ajaxSuccess()

This callback method is automatically called for successful AJAX requests, it

receives four parameters, with the first one containing either the response body

as a string, or a JavaScript object for JSON requests.

_ajaxFailure()

Optional callback function that is invoked for failed requests, it will be

automatically called if the callee implements it, otherwise the global error

handler will be executed.

Single Requests Without a Module (Alte API)

The Ajax.api() method expects an object that is used to extract the request

configuration as well as providing the callback functions when the request state

changes.

You can issue a simple Ajax request without object binding through Ajax.apiOnce()

that will destroy the instance after the request was finalized. This method is

significantly more expensive for repeated requests and does not offer deriving

modules from altering the behavior. It is strongly recommended to always use

Ajax.api() for requests to the WSC API endpoint.

<script data-relocate="true">
  require(["Ajax"], function(Ajax) {
    Ajax.apiOnce({
      data: {
        actionName: "makeSnafucated",
        className: "app\\data\\foo\\FooAction",
        parameters: {
          value: 3
        }
      },
      success: function(data) {
        elBySel(".some-element").textContent = data.bar;
      }
    })
  });
</script>