Skip to content

Global Dialogs ​

A dialog in LaravelUi5 is not a fragment you keep in a controller. It is an artifact: a declared, named, authorized thing that the shell opens by namespace — from your own app, from another app, or from the command palette, without either side knowing the other's views.

js
LaravelUi5.dispatchIntent({
    name: 'ui5-artifact.open',
    parameters: { namespace: 'com.acme.sales.dialogs.create_customer' },
});

That is the whole call site. No fragment path, no Fragment.load, no lifecycle bookkeeping — the shell creates the view, checks the actor's permission on the server first, opens it, and destroys it after close.

What you write ​

Four files: a PHP artifact, a line in your module, and the view and controller pair.

The artifact ​

php
namespace App\Sales\Dialogs;

use App\Sales\SalesRole;
use LaravelUi5\Sdk\Security\Attributes\Access;
use LaravelUi5\Sdk\Ui5\AbstractUi5Dialog;

#[Access(ability: 'createCustomerDialog', role: SalesRole::Sales, note: 'Open the create-customer dialog.')]
class CreateCustomerDialog extends AbstractUi5Dialog
{
    public const NAMESPACE   = 'com.acme.sales.dialogs.create_customer';
    public const VERSION     = '1.0.0';
    public const TITLE       = 'Create Customer';
    public const DESCRIPTION = 'Creates a customer record.';
    public const VIEW        = 'com.acme.sales.view.dialog.CreateCustomer';
}

Five constants and nothing else. AbstractUi5Dialog has no abstract method to implement: it answers getType() with ArtifactType::Dialog and getViewName() with your VIEW, and the identity getters read the constants. A missing constant is a runtime Error on first use, not a compile error — so write all five.

VIEW is the UI5 view name in dotted notation, the same string you would hand XMLView.create.

Registering it ​

Dialogs are not discovered automatically. Declare them on your module:

php
public function getDialogs(): array
{
    return [
        new Dialogs\CreateCustomerDialog($this),
    ];
}

There is no ui5:dialog generator. Write the four files by hand — a dialog is small enough that a scaffold would not save much, and dialogs share that with value helps: neither has a stub.

The view ​

The view's single root control must be a sap.m.Dialog. The shell enforces that at open time and throws otherwise, and it also throws if the view has more than one root control.

xml
<mvc:View
    controllerName="com.acme.sales.controller.dialog.CreateCustomer"
    xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc" xmlns:f="sap.ui.layout.form">
    <Dialog title="{i18n>createCustomer.title}">
        <f:Form>
            <!-- … -->
        </f:Form>
        <beginButton>
            <Button text="{i18n>save}" type="Emphasized" press=".onSave"/>
        </beginButton>
        <endButton>
            <Button text="{i18n>cancel}" press=".onCancel"/>
        </endButton>
    </Dialog>
</mvc:View>

The controller comes from controllerName — the artifact names only the view.

The controller, and the one hook ​

Your controller is an ordinary one. If it defines a method named initDialog(dialog), the shell calls it once, on afterOpen, with the dialog instance. That is the place to reset a form model or focus a field.

js
initDialog(oDialog) {
    this.getView().setModel(new JSONModel({ name: '' }), 'form');
}

Closing destroys both the dialog and its view. Every open starts from a clean instance, so you never have to reset state on the way in — but also cannot keep it between opens.

Who may open it ​

#[Access] on the artifact class is the gate, and it is checked on the server before the dialog's view is ever created — the open intent's authorizer resolves the ability and asks the actor's grants.

SituationResult
the dialog declares no #[Access]anyone in the shell may open it
the actor holds the abilitythe dialog opens
the actor does not hold ita deny result; no view is built
the ability exists in code but ui5:sync has not runrefused — an unresolved ability is treated as no

The ability is catalogued under the module's namespace, like every other ability, and its role must be declared with #[Role] on the module (Permission Levels). Run ui5:sync after adding the attribute, then grant the role.

A dialog is a gate you can actually use

Unlike an app, a dialog is a small, sharply-scoped thing to put an ability on: may this actor open the create-customer form. The write behind Save carries its own #[Act], so the two decisions stay separate — hiding the door and locking the safe.

What happens on open ​

  1. Your app dispatches ui5-artifact.open with the dialog's namespace.
  2. The server authorizes, resolves the artifact, and answers with a dialog result carrying the owning app's namespace and the view name.
  3. The shell decides where to host it:
    • your own app — the view is created in your component's context, so your models, OData bindings and i18n are all there;
    • another app's dialog — the shell loads that app as a hidden component, opens the dialog from it, and destroys the component after close. The dialog is rendered with the owning app's models, which is the point: the dialog belongs to the module that knows the business object.

Opening a dialog from a foreign app needs nothing from you as the caller. You name a namespace.

If your app is the one being hosted

When another app opens your dialog, your component starts with componentData.mode === 'dialog'. Your Component.init must honour that and skip getRouter().initialize() — a router that starts in a hosted component navigates and tears the just-opened dialog down. The base component passes the flag but does not act on it; that check belongs to your app.

Saving ​

A dialog's Save is a mutation, and mutations are actions — OData stays read-only:

js
await LaravelUi5.call('com.acme.sales.actions.create_customer', {}, formData);

The action carries its own #[Act] ability, its own FormRequest and its own transaction. See Mutating Actions.

What it doesn't do ​

  • No URL. A dialog is not routable; there is no deep link to an open dialog. If a user must be able to link to it, it is a route in an app, not a dialog.
  • No shell without the SDK. On Core alone dispatchIntent does nothing, silently. Dialogs are a LeanShell capability.
  • No return value. A dialog is fire-and-forget. A dialog that hands a selection back to its caller is a value help — a different artifact type, with a promise.
  • No generator, no auto-discovery, as above.

See also ​