diff --git a/agents/framework/Web/UI/IAdapterControl.md b/agents/framework/Web/UI/IAdapterControl.md index 36ab8fae0..cc042c349 100644 --- a/agents/framework/Web/UI/IAdapterControl.md +++ b/agents/framework/Web/UI/IAdapterControl.md @@ -3,42 +3,39 @@ ### Directories [framework](../../INDEX.md) / [Web](../INDEX.md) / [UI](./INDEX.md) / **`IAdapterControl`** -## Class Info +## Interface Info **Location:** `framework/Web/UI/IAdapterControl.php` **Namespace:** `Prado\Web\UI` +**Since:** 4.3.3 ## Overview -Interface defining the contract for objects returned by `TControl::getAdapterControl()`. That method returns either the control itself or its attached `TControlAdapter`. Both `TControl` and `TControlAdapter` implement this interface so the framework can dispatch lifecycle calls through a single, uniform pointer without knowing whether an adapter is present. -All lifecycle methods, the render entry-point, and state hooks are called through this interface during the page request cycle. +Common contract for the object returned by `TControl::getAdapterControl()`. That method returns either the control itself or its `TControlAdapter` when one is set, so both classes implement this interface. Lifecycle methods, the render entry-point, and state hooks are all called through it, letting `TControl` dispatch to either the adapter or itself with a single typed call. -## Interface Methods +## Methods | Method | Description | |---|---| | `createChildControls()` | Creates child controls | -| `onInit($param)` | Called at the `OnInit` lifecycle stage | -| `onLoad($param)` | Called at the `OnLoad` lifecycle stage | -| `onPreRender($param)` | Called at the `OnPreRender` lifecycle stage | -| `onUnload($param)` | Called at the `OnUnload` lifecycle stage | -| `render(THtmlWriter $writer)` | Renders the control | +| `onInit($param)` | Invoked at the `OnInit` lifecycle stage | +| `onLoad($param)` | Invoked at the `OnLoad` lifecycle stage | +| `onPreRender($param)` | Invoked at the `OnPreRender` lifecycle stage | +| `onUnload($param)` | Invoked at the `OnUnload` lifecycle stage | +| `render($writer)` | Renders the control to `$writer` | | `loadState()` | Loads additional persistent control state | | `saveState()` | Saves additional persistent control state | ## Implementors -- **`TControl`** — implements it directly; `getAdapterControl()` returns `$this` when no adapter is set. -- **`TControlAdapter`** — base adapter class; each method delegates to the attached control by default. Subclasses override only what they need. +- **[`TControl`](TControl.md)** — satisfies all methods natively; `getAdapterControl()` returns `$this` when no adapter is set. +- **[`TControlAdapter`](TControlAdapter.md)** — provides pass-through implementations that delegate to the attached control; subclasses override only what they customise. -## Patterns & Gotchas +## Usage -- **Purpose of `getAdapterControl()`** — `TControl::initRecursive`, `loadRecursive`, `preRenderRecursive`, `unloadRecursive`, `renderControl`, and the state hooks all route through `getAdapterControl()` so that an adapter transparently intercepts any or all of these stages. -- **Attach via `TControl::setAdapter()`** — once an adapter is attached, `getAdapterControl()` returns it instead of the control itself. -- **Adding adapters post-init** — setting an adapter after the control has been initialized can result in lifecycle methods being called on the adapter for stages the control has already passed. +```php +// Inside TControl — getAdapterControl() is protected; returns IAdapterControl +$this->getAdapterControl()->onPreRender($param); +$this->getAdapterControl()->render($writer); +``` -## See Also - -- [TControlAdapter](./TControlAdapter.md) -- [TControl](./TControl.md) - -**@since 4.3.3** +Application code never calls `getAdapterControl()` directly; it is used exclusively inside `TControl`'s own lifecycle and rendering methods. diff --git a/agents/framework/Web/UI/IFilterRenderable.md b/agents/framework/Web/UI/IFilterRenderable.md index 31a073daa..0ab10e446 100644 --- a/agents/framework/Web/UI/IFilterRenderable.md +++ b/agents/framework/Web/UI/IFilterRenderable.md @@ -3,49 +3,46 @@ ### Directories [framework](../../INDEX.md) / [Web](../INDEX.md) / [UI](./INDEX.md) / **`IFilterRenderable`** -## Class Info +## Interface Info **Location:** `framework/Web/UI/IFilterRenderable.php` **Namespace:** `Prado\Web\UI` +**Extends:** `IRenderable` +**Since:** 4.3.3 ## Overview -Interface that marks a control as supporting render-output filtering via the `onRenderFilter` event. Extends `IRenderable`. -`TControl::renderControl` and `TControl::renderChildren` detect this interface and automatically handle the capture-and-restore lifecycle: when at least one `onRenderFilter` handler is registered, the writer's inner `ITextWriter` is swapped for a fresh buffer, `render()` runs into that buffer, and the captured HTML is then passed through `onRenderFilter` handlers before being written to the real output. +Marks a control as supporting render-output filtering via the `onRenderFilter` event. `TControl::renderControl` and `TControl::renderChildren` detect this interface and automatically wrap the render call in a capture-and-restore filter lifecycle (`preRenderFilter` / `processRenderFilter`). Implement using [`TFilterRenderableTrait`](Traits/TFilterRenderableTrait.md). -Implement using [TFilterRenderableTrait](./Traits/TFilterRenderableTrait.md). - -## Interface Methods +## Methods | Method | Description | |---|---| -| `hasEventHandler(string $name): bool` | Returns whether at least one handler is registered for the named event. Required so `TControl::preRenderFilter` can test `onRenderFilter` without assuming the object is a `TComponent`. | -| `onRenderFilter(string $renderedText): string` | Raises the `onRenderFilter` event via a [TRenderFilterParameter](./TRenderFilterParameter.md), passing captured HTML through all registered handlers, and returns the (possibly modified) string. | +| `hasEventHandler($name)` | Returns whether at least one handler is registered for the named event. Required so `TControl::preRenderFilter` can test the event without assuming a `TComponent` base. | +| `onRenderFilter($output)` | Raises the `onRenderFilter` event, passes HTML through handlers via a [`TRenderFilterParameter`](TRenderFilterParameter.md), returns the (possibly modified) HTML string. | -Inherits `render(THtmlWriter $writer)` from `IRenderable`. +## Implementation -## How to Implement +Implement the interface by using [`TFilterRenderableTrait`](Traits/TFilterRenderableTrait.md), which provides `onRenderFilter`. `hasEventHandler` is satisfied by `TComponent`, which all practical implementors extend: ```php -use Prado\Web\UI\IFilterRenderable; -use Prado\Web\UI\Traits\TFilterRenderableTrait; - -class MyControl extends TCompositeControl implements IFilterRenderable +class MyControl extends TComponent implements IFilterRenderable { use TFilterRenderableTrait; - // onRenderFilter() and hasEventHandler() are provided by the trait + TComponent + + public function render($writer): void + { + $writer->write('

content

'); + } } ``` -## Patterns & Gotchas - -- **No-op when no handlers** — `preRenderFilter` calls `hasEventHandler('onRenderFilter')` and returns `null` (no buffer swap) when the result is false. Zero overhead for controls without handlers. -- **`TControl` itself implements this** — `TControl` implements `IFilterRenderable` via `TFilterRenderableTrait`, so all controls can receive `onRenderFilter` handlers out of the box. -- **Non-`TControl` implementors** — `renderChildren` also checks `IFilterRenderable` on non-`TControl` children (plain `IRenderable` objects), applying the same lifecycle when they implement this interface. +`TControl` already implements `IFilterRenderable` — no extra work is needed for controls that extend it. -## See Also +## Filter lifecycle -- [TFilterRenderableTrait](./Traits/TFilterRenderableTrait.md) -- [TRenderFilterParameter](./TRenderFilterParameter.md) -- [TControl](./TControl.md) +When `TControl::renderChildren` encounters a non-`TControl` child that implements `IFilterRenderable`: +1. `preRenderFilter($writer, $child)` — checks `$child->hasEventHandler('onRenderFilter')`. If true, swaps the writer's inner buffer and saves the original. +2. `$child->render($writer)` — renders into the capture buffer. +3. `processRenderFilter($writer, $oldWriter, $child)` — flushes the buffer, calls `$child->onRenderFilter($output)`, writes the result to the original writer, and restores it. -**@since 4.3.3** +If no handler is registered, all three steps are no-ops and output goes directly to the writer. diff --git a/agents/framework/Web/UI/TPage.md b/agents/framework/Web/UI/TPage.md index c6c63f723..5f5e31177 100644 --- a/agents/framework/Web/UI/TPage.md +++ b/agents/framework/Web/UI/TPage.md @@ -24,86 +24,72 @@ TPage is the base class for all web pages in PRADO framework. It extends [TTempl - `Head` ([THead](./WebControls/THead.md)): Page header element - `Validators` ([TList](../../Collections/TList.md)): Collection of registered validators - `Theme` ([TTheme](./TTheme.md)): Page theme for styling -- `StyleSheet` ([TTheme](./TTheme.md)): Page stylesheet theme +- `StyleSheetTheme` ([TTheme](./TTheme.md)): Page stylesheet theme - `ClientScript` ([TClientScriptManager](./TClientScriptManager.md)): Manages client-side scripts +- `Title` (string): Page title, held until a THead is set on the page - `PagePath` (string): Path to the current page +- `IsPostBack` (bool): Whether the request is a postback (read-only) +- `IsCallback` (bool): Whether the request is a callback (read-only) +- `StatePersisterClass` / `StatePersister` ([IPageStatePersister](./IPageStatePersister.md)): Where page state is stored - `EnableStateValidation` (bool): Whether page state should be HMAC validated -- `EnableStateEncryption` (bool): Whether page state should be encrypted +- `EnableStateEncryption` (bool): Whether page state should be encrypted - `EnableStateCompression` (bool): Whether page state should be compressed -- `EnableJavaScript` (bool): Whether client supports JavaScript -- `Focus` (string|[TControl](./TControl.md)): Control or element to be focused on page load +- `EnableStateIGBinary` (bool): Whether page state uses the igbinary serializer when available +- `ClientSupportsJavaScript` (bool): Whether client supports JavaScript +- `Focus` (string|[TControl](./TControl.md)): Control or element to be focused on page load (write-only) +- `CallbackClient` ([TCallbackClientScript](./ActiveControls/TCallbackClientScript.md)): Client-side commands for a callback response ## Core Methods ### Page Lifecycle -- `initRecursive()`: Initializes page and child controls -- `loadRecursive()`: Loads page and child controls -- `preRenderRecursive()`: Pre-renders page and child controls -- `unloadRecursive()`: Unloads page and child controls -- `saveState()`: Saves page state to persister +- `run($writer)`: Entry point called by [TPageService](../Services/TPageService.md) +- `processNormalRequest($writer)`, `processPostBackRequest($writer)`, `processCallbackRequest($writer)`: The three life cycles +- `onPreInit()`, `onInitComplete()`, `onPreLoad()`, `onLoadComplete()`, `onPreRenderComplete()`, `onSaveStateComplete()`: Page-only life cycle events +- `flushWriter()`: Flushes the content rendered so far to the response ### Form Management -- `getForm()`: Gets form instance -- `setForm()`: Sets form instance -- `setFocus($control)`: Sets focus to a control -- `getFocus()`: Gets focus control or element ID -- `renderForm()`: Renders HTML form +- `getForm()` / `setForm($form)`: The single [TForm](./TForm.md) of the page +- `getHead()` / `setHead($head)`: The single [THead](./WebControls/THead.md) of the page +- `setFocus($control)`: Sets focus to a control or element ID +- `ensureRenderInForm($control)`: Throws when a control renders outside the form +- `getInFormRender()`, `beginFormRender($writer)`, `endFormRender($writer)`: Form render state, invoked by TForm ### Validation -- `getValidators()`: Gets list of registered validators -- `registerValidator($validator)`: Registers a validator -- `unregisterValidator($validator)`: Unregisters a validator -- `validate()`: Performs page validation -- `getIsValid()`: Checks if page is valid +- `getValidators($validationGroup = null)`: Gets the [TList](../../Collections/TList.md) of registered validators; validators add and remove themselves +- `validate($validationGroup = null)`: Performs page validation +- `getIsValid()`: Whether the input is valid; throws when `validate()` has not run ### State Management -- `getPageStatePersister()`: Gets page state persister instance -- `loadPageState()`: Loads page state from request -- `savePageState()`: Saves page state to response -- `getPageState()`: Gets page state data -- `getControlState()`: Gets control state data for page -- `setStateValidation()`: Sets page state validation -- `getStateValidation()`: Gets page state validation -- `setViewState()`: Sets viewstate data for page -- `getViewState()`: Gets viewstate data for page - -### Event Handling -- `raisePostBackEvent($sender, $param)`: Raises postback event -- `onLoadPostData()`: Handles loading postback data -- `onLoad()`: Raises OnLoad event -- `onPreRender()`: Raises OnPreRender event -- `onUnload()`: Raises OnUnload event +- `getStatePersister()`: Gets page state persister instance +- `loadPageState()` / `savePageState()`: Reads and writes the page state through the persister +- `saveState()` / `loadState()`: Adds the controls requiring post data to the page's own state +- `getClientState()` / `setClientState($state)`: State to be written to the client +- `getRequestClientState()`: State posted back from the client + +### Postback & Data Handling +- `getIsPostBack()`, `getIsCallback()`: Request type +- `processPostData($postData, $beforeLoad)`: Dispatches post data to the controls +- `registerRequiresPostData($control)`: Registers a control to load post data on the next postback +- `getIsLoadingPostData()`: Whether post data is being loaded +- `getPostBackEventTarget()` / `setPostBackEventTarget($control)`: Control raising the postback event +- `getPostBackEventParameter()` / `setPostBackEventParameter($value)`: Postback event parameter +- `raiseChangedEvents()`: Raises OnPostDataChanged for the controls whose data changed +- `isSystemPostField($field)`: Whether a post field is one of the framework `FIELD_*` fields + +### Callbacks +- `getCallbackClient()`: Client-side script handler for the callback response +- `getCallbackEventTarget()` / `setCallbackEventTarget($control)`: Control raising the callback event +- `getCallbackEventParameter()` / `setCallbackEventParameter($value)`: JSON decoded callback parameter ### Theme Management -- `getTheme()`: Gets page theme -- `setTheme($value)`: Sets page theme -- `getStyleSheet()`: Gets page stylesheet -- `setStyleSheet($value)`: Sets page stylesheet -- `applyControlSkin()`: Applies skin to controls - -### Client Script Management -- `getClientScript()`: Gets client script manager -- `registerClientScript()`: Registers client script -- `registerStyleSheet()`: Registers CSS stylesheet -- `addStyleSheet()`: Adds CSS stylesheet -- `getJavaScript()`: Gets JavaScript object +- `getTheme()` / `setTheme($value)`: Gets and sets the page theme +- `getStyleSheetTheme()` / `setStyleSheetTheme($value)`: Gets and sets the page stylesheet theme +- `applyControlSkin($control)` / `applyControlStyleSheet($control)`: Applies a skin to a control -### Postback & Data Handling -- `getPostBackEventTarget()`: Gets control that raised postback event -- `setPostBackEventTarget()`: Sets control that raised postback event -- `getPostBackEventParameter()`: Gets postback event parameter -- `setPostBackEventParameter()`: Sets postback event parameter -- `getPostBackData()`: Gets postback data -- `setPostBackData()`: Sets postback data -- `getHasChanged()`: Checks if page data has changed - -### Rendering -- `render()`: Renders page to HTML output -- `renderChildren()`: Renders page child controls -- `renderFormContent()`: Renders form content -- `renderHead()`: Renders page head element -- `renderClientScript()`: Renders client-side scripts -- `renderPostData()`: Renders postback data fields +### Client Script and Caching +- `getClientScript()`: Gets the client script manager, which registers scripts, stylesheets and hidden fields +- `getCachingStack()`: Stack of the active [TOutputCache](./WebControls/TOutputCache.md) controls +- `registerCachingAction($context, $funcName, $funcParams)`: Records an action to replay when cached content is served ## Constants - `FIELD_POSTBACK_TARGET`: System postback target field name @@ -114,35 +100,32 @@ TPage is the base class for all web pages in PRADO framework. It extends [TTempl - `FIELD_CALLBACK_PARAMETER`: System callback parameter field name ## Page Lifecycle Stages -1. **Init**: Page and control initialization -2. **Load**: Loading of page state and postback data -3. **PreRender**: Page preparation for rendering -4. **Render**: Actual HTML output generation -5. **Unload**: Page cleanup +`onPreInit` -> `initRecursive` -> `onInitComplete` -> `loadPageState` *(POST/Callback)* -> `processPostData` *(POST/Callback)* -> `onPreLoad` -> `loadRecursive` -> `processPostData` *(POST/Callback)* -> `raiseChangedEvents` *(POST/Callback)* -> `raisePostBackEvent` *(POST-only)* -> `processCallbackEvent` *(Callback-only)* -> `onLoadComplete` -> `preRenderRecursive` -> `onPreRenderComplete` -> `savePageState` -> `onSaveStateComplete` -> `renderControl` *(GET/POST)* / `renderCallbackResponse` *(Callback-only)* -> `unloadRecursive` ## Validation Flow -1. Validators are registered during Init lifecycle -2. Validation is triggered in PreRender lifecycle -3. Page is validated in Validate method -4. Validation results are stored in IsValid property +1. A validator adds itself to `Validators` when it is initialized, and removes itself when it is unloaded +2. A button with `CausesValidation` calls `validate($validationGroup)` from its `raisePostBackEvent()` +3. `raisePostBackEvent()` on the page validates when no control is registered as the event target +4. `getIsValid()` reports the outcome and throws when `validate()` has not run ## Usage Example ```php -// Create page with form -$page = new TPage(); -$form = new TForm(); -$page->setForm($form); - -// Register validator -$validator = new TRequiredFieldValidator(); -$page->registerValidator($validator); - -// Process page -$page->initRecursive(); -$page->loadRecursive(); -if ($page->validate()) { - $page->preRenderRecursive(); - $page->render($writer); +class HomePage extends TPage +{ + public function onLoad($param) + { + parent::onLoad($param); + if (!$this->getIsPostBack()) { + $this->setTitle('Home'); + } + } + + public function buttonClicked($sender, $param) + { + if ($this->getIsValid()) { + // handle the click + } + } } ``` diff --git a/agents/framework/Web/UI/TRenderFilterParameter.md b/agents/framework/Web/UI/TRenderFilterParameter.md index 23a7c7df5..f9f7c2014 100644 --- a/agents/framework/Web/UI/TRenderFilterParameter.md +++ b/agents/framework/Web/UI/TRenderFilterParameter.md @@ -8,91 +8,105 @@ **Namespace:** `Prado\Web\UI` **Extends:** `TEventParameter` **Implements:** `IEventCycleParameter` +**Since:** 4.3.3 ## Overview -Event parameter for the `onRenderFilter` event raised by `TControl::renderControl`. It carries the captured rendered HTML and exposes two representations that can be switched between transparently: -- **HTML string** — raw rendered markup, via `getFilterText()` / `setFilterText()` or array-access key `'html'` (`RENDER_FILTER_TEXT`). -- **DOMDocument** — a parsed DOM tree, via `getFilterDOM()` / `setFilterDOM()` or array-access key `'dom'` (`RENDER_FILTER_DOM`). - -The parameter tracks which representation is *current* (authoritative) and lazily syncs between them. +Event parameter for the `onRenderFilter` event raised by `TControl::renderControl`. Carries the rendered HTML and exposes two transparently-switchable representations — an HTML string and a `DOMDocument` — plus the libxml parse error list. All three are stored in the parent `TEventParameter` array under reserved keys. ## Constants | Constant | Value | Description | |---|---|---| -| `RENDER_FILTER_TEXT` | `'html'` | Array-access key for the HTML string | -| `RENDER_FILTER_DOM` | `'dom'` | Array-access key for the DOMDocument | -| `RENDER_FILTER_ERRORS` | `'errors'` | Array-access key for libxml parse errors (`LibXMLError[]` or `null`) | +| `RENDER_FILTER_TEXT` | `'html'` | Array key for the HTML string | +| `RENDER_FILTER_DOM` | `'dom'` | Array key for the DOMDocument | +| `RENDER_FILTER_ERRORS` | `'errors'` | Array key for the libxml error list | -## Resource Switching +## Resource switching -| Action | Effect | -|---|---| -| `getFilterDOM()` | Parses HTML → DOM (if needed); makes DOM the current resource | -| `getFilterText()` | Serialises DOM → HTML (if DOM is current); makes string the current resource | -| `setFilterText($html)` | Sets string; discards DOM cache and parse errors; string becomes current | -| `setFilterDOM($dom)` | Sets DOM; DOM becomes current | +The parameter tracks which representation is *current*: +- `getFilterDOM()` (or `$param['dom']`) → parses HTML into DOM, makes DOM current. +- `getFilterText()` (or `$param['html']`) while DOM is current → serialises DOM back to HTML, makes string current. +- `setFilterText()` / `setFilterDOM()` makes the set representation current and discards the other. +- `postRaiseEvent` automatically serialises DOM → HTML after all handlers run, so `processRenderFilter` always receives a valid string. ## Key Methods -- `getFilterText(): string` — Current HTML string (syncs from DOM first if needed). -- `setFilterText(string $html): void` — Replaces the HTML string; discards any cached DOM. -- `getFilterDOM(): DOMDocument|false` — Lazily parsed DOM; `false` on fatal libxml parse failure. Makes DOM current. -- `setFilterDOM(DOMDocument $dom): void` — Replaces the DOM; makes DOM current. -- `getFilterErrors(): ?array` — `LibXMLError[]` from the last parse, or `null` when no errors occurred. -- `getHasFilterError(): bool` — `true` when the last parse captured at least one libxml error. -- `walkElements(callable $callback, ?DOMNode $node = null, bool $recursive = true): void` — Depth-first traversal of every `DOMElement` in the document (or a subtree). Callback signature: `(DOMElement $el, TRenderFilterParameter $p, int $depth): void`. The visit list is snapshotted before the first callback fires, so DOM mutations during the walk do not affect which elements are visited. -- `postRaiseEvent(...)` — `IEventCycleParameter` hook. Serialises DOM → HTML after all handlers run so `TControl::processRenderFilter` always receives a valid string. +### HTML accessor + +| Method | Description | +|---|---| +| `getFilterText(): string` | Current HTML (serialises from DOM first if DOM is current) | +| `setFilterText(string $html): void` | Replace HTML; discard DOM and errors | + +### DOM accessor + +| Method | Description | +|---|---| +| `getFilterDOM(): DOMDocument\|false` | Parsed DOM (lazy parse on first call); `false` on fatal libxml failure | +| `setFilterDOM(DOMDocument $dom): void` | Replace DOM; clear errors | + +### Error accessors + +| Method | Description | +|---|---| +| `getFilterErrors(): ?array` | `LibXMLError[]` from the most recent parse, or `null` when no errors | +| `getHasFilterError(): bool` | `true` when at least one libxml error was captured | + +### DOM walker + +```php +$param->walkElements(function (\DOMElement $el, $param, int $depth) { + if ($el->tagName === 'img' && !$el->hasAttribute('alt')) { + $el->setAttribute('alt', ''); + } +}); +``` + +`walkElements(callable, ?DOMNode $node = null, bool $recursive = true)` — depth-first traversal of every `DOMElement`. The visit list is snapshotted before the first callback, so DOM mutations during the walk do not affect which elements are visited. + +## Array-access -## Array-Access Behaviour +All three reserved keys are proxied through the getters/setters: ```php -$param[TRenderFilterParameter::RENDER_FILTER_TEXT] // proxies getFilterText() -$param[TRenderFilterParameter::RENDER_FILTER_DOM] // proxies getFilterDOM() -$param[TRenderFilterParameter::RENDER_FILTER_ERRORS] // proxies getFilterErrors() - -$param['html'] = '

new

'; // setFilterText() -$param['dom'] = $domDocument; // setFilterDOM() -unset($param['html']); // clears string to '' -unset($param['dom']); // commits DOM→HTML then discards DOM -unset($param['errors']); // clears stored parse errors +$param[TRenderFilterParameter::RENDER_FILTER_TEXT] // → getFilterText() +$param[TRenderFilterParameter::RENDER_FILTER_TEXT] = $html; // → setFilterText() +$param[TRenderFilterParameter::RENDER_FILTER_DOM] // → getFilterDOM() +$param[TRenderFilterParameter::RENDER_FILTER_DOM] = $dom; // → setFilterDOM() (must be DOMDocument) +$param[TRenderFilterParameter::RENDER_FILTER_ERRORS] // → getFilterErrors() (null or array) +$param[TRenderFilterParameter::RENDER_FILTER_ERRORS] = $v; // no-op — errors are read-only +unset($param[TRenderFilterParameter::RENDER_FILTER_ERRORS]); // clears errors → null ``` -## Usage Examples +Extra keys (not one of the three reserved ones) pass through to the parent `TEventParameter` array and can be used for handler-to-handler state passing. + +## Error semantics + +`RENDER_FILTER_ERRORS` stores `null` when no errors have been captured (fresh instance, clean parse, or after `setFilterText`/`setFilterDOM`). A non-null value means the last parse produced at least one libxml error. Use `getHasFilterError()` as the canonical check. + +## Subclassing + +Override `htmlToDom(string $html): DOMDocument|false` to substitute a custom parser. Call `$this->storeErrors($errors)` inside the override to keep the errors slot consistent (it bypasses the public no-op on `offsetSet('errors', ...)`). + +## Typical handler ```php -// String-based handler $control->onRenderFilter[] = function ($sender, TRenderFilterParameter $param) { + // String API $param->setFilterText(strtoupper($param->getFilterText())); -}; -// DOM-based handler — add missing alt attributes to all elements -$control->onRenderFilter[] = function ($sender, TRenderFilterParameter $param) { + // DOM API $dom = $param->getFilterDOM(); // DOMDocument|false - if ($dom === false) { - return; // libxml could not parse the fragment + if ($dom !== false) { + $param->walkElements(function (\DOMElement $el, $p) { + if ($el->tagName === 'img' && !$el->hasAttribute('alt')) { + $el->setAttribute('alt', ''); + } + }); } - $param->walkElements(function (\DOMElement $el, TRenderFilterParameter $p) { - if ($el->tagName === 'img' && !$el->hasAttribute('alt')) { - $el->setAttribute('alt', ''); - } - }); - // DOM → HTML serialisation is automatic via postRaiseEvent + + // Extra state for a downstream handler + $param['processed-by'] = 'my-filter'; }; ``` - -## Patterns & Gotchas - -- **DOM parsed with `LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD`** — no `//` wrappers are added. The processing instruction `` is injected for correct encoding and then removed after parsing. -- **`false` return from `getFilterDOM()`** — means libxml reported a fatal parse failure. The HTML string remains current and unmodified. Check `getFilterErrors()` for details. -- **Parse errors are always retained** — even when parsing succeeded, libxml warnings/notices are captured. Only `null` means "no errors at all". -- **`postRaiseEvent` serialises automatically** — handlers that work exclusively through the DOM API do not need to call `getFilterText()` themselves. - -## See Also - -- [IFilterRenderable](./IFilterRenderable.md) -- [TFilterRenderableTrait](./Traits/TFilterRenderableTrait.md) -- [TControl](./TControl.md) - -**@since 4.3.3** diff --git a/agents/framework/Web/UI/WebControls/TButton.md b/agents/framework/Web/UI/WebControls/TButton.md index d478576c3..4a0e2a9c0 100644 --- a/agents/framework/Web/UI/WebControls/TButton.md +++ b/agents/framework/Web/UI/WebControls/TButton.md @@ -19,7 +19,7 @@ TButton creates a clickable button control on the page. It is primarily used for - **Command Parameters**: Supports command name and parameter for distinguishing multiple buttons ## Core Properties -- `ButtonTag` (TButtonTag): Tag name of button (Input, Button, or Reset) +- `ButtonTag` (TButtonTag): Tag name of button (Input or Button) - `ButtonType` (TButtonType): Type of button (Submit, Button, or Reset) - `CommandName` (string): Command name for command events - `CommandParameter` (string): Command parameter for command events @@ -27,7 +27,7 @@ TButton creates a clickable button control on the page. It is primarily used for - `ValidationGroup` (string): Validation group for restricting validation - `Text` (string): Button caption text - `EnableClientScript` (bool): Whether JavaScript is rendered for button -- `UseSubmitBehavior` (bool): Whether to use submit behavior for postback +- `IsDefaultButton` (bool): Set by a [TPanel](./TPanel.md) to make this the panel's default button ## Core Events - `OnClick`: Raised when button is clicked @@ -39,28 +39,27 @@ TButton creates a clickable button control on the page. It is primarily used for - **Reset**: Resets form fields when clicked (clears input values) ## Button Tags -- **Input**: Renders as `` (default) -- **Button**: Renders as `