Maslosoft Framework Documentation
Flexible Application Foundation
Widget Actions A.K.A. Microcontroller Architecture
This is so-called microcontroller architecture, allowing to call widget method by especially crafted URL. So that widget act like controller with actions.
Use cases include, but are not limited to:
- Sorting data
- Searching
- Pagination
- Storing persitent state
Depending on situation, action will be called on client side widget instance or on server side.
Creating Server Side Microcontroller Widget
Evaluating Actions
To make actions available in widget, it need to have initialized actions
evaluator (Actions
). It will intercept request and call apropriate
methods if applicable. This should be placed in widget's init
method.
Actions
takes two arguments, first the widget isntance itself and the
boolean value indicating whether should call action now, or manually later.
Example:
Instant calling of action, this code will execute any method that is passed by request.
private $actions = null;
public function init()
{
$this->actions = new Actions($this);
}
Example of delayed calling:
Delayed calling might be usefull if some other tasks need to be done before evaluating actions.
private $actions = null;
public function init()
{
$this->actions = new Actions($this, false);
}
public function run()
{
$this->actions->call();
}
Widget Action Method
To create widget action, there first of all, the widget itself need to have
public method starting with action
prefix, for example actionSearch
. This
method will be called when URL containing widget ID will arrive to widget instance.
Additional parameters can be passed to widget action method.
This prefix is required to prevent calling arbitrary widget methods from JavaScript
Parameters
Parameters passed to widget, are - depending on their URL value - either string or hashed array.
Note that widget method should check if parameters are valid and react accordingly.
Auto-bound parameters
Widget can have many parameters passed by calling URL with
widget ID, action name and parameters. Note that if expected values might
contain colon :
, raw parameters can be used.
Example URL could be ?m18DDYa.page=
num:2
which will result in int value
of 2
bound to method argument named num
.
Example action method could be like following:
public function actionPage($num)
{
$page = $num;
...
}
Array parameters
URL containing widget ID, method name and value separated by colon :
, for
example URL m1YmTZj.sort=
fullName:desc
will call method actionSort
of widget with ID m1YmTZj
with array parameters if the method have one variadic
parameter list, denoted by ...
.
[
'fullName' => 'desc'
]
The method signature to match such params could be for example:
public function actionSort(...$sort)
{
...
}
Raw parameters
In some cases, it is desirable to obtain raw data from passed parameters, for
instance search query string. This can be retrieved by calling widget actions
evaluator instance (Actions
) with action name:
$params = $this->action->get('search')->rawArguments;
For example URL of ?top-search.search=
user
this will return string user
,
regardless of how it is formatted.
In case of raw arguments, action method itself does not even need a parameter:
public function actionSearch()
{
$params = $this->getActions()->get('search')->rawArguments;
}
Creating client side widget
Client side of widget should have class name same as server side one. It can
be initalized in widget's init
method. It should be located in js
sub folder
of current widget.
PHP widget inializing JS part
JavaScript part of widget have id
as a parameter:
class MyWidget
{
public function init()
{
new JsWidget($this, [JavaScript::encode($this->id)]);
}
}
JavaScript side widget
This example is written in CoffeeScript. In this example, dispatcher is used
smilarly to PHP's widget dispatcher Actions
. This allows both executing
actions on URL change, as well as calling remote action.
See also activities for more informations on dispatcher.
Dispatcher takes up to three parameters:
- Widget instance
- Instance of object holding actions
- Instance of object holding activities
The JavaScript widgets do not use action
prefix, as any action can
be called by client anyway. In this scenario, calling virtual URL
m1YmTZj.
sort
=fullName:desc
matching @id
passed
in constructor, will call sort
method.
class MyWidget constructor: (@id) -> @dispatcher = new Maslosoft.Widgets.Dispatcher @, @ sort: (columns, e) => # Call this widget's server side method @dispatcher.remote 'sort', columns
Complex parameters
The dispatcher's remote action can send arbitrary data to server, which will be decoded like if it was JSON data. The topmost named arguments will be auto wired to PHP function argument names.
class MyWidget constructor: (@id) -> @dispatcher = new Maslosoft.Widgets.Dispatcher @, @ sort: () => data = { direction: 1, field: 'name', parentIds: [1, 2, 3], filter: {published: true} } @dispatcher.remote 'sort', data
On the server side, the widget method actionSort
, when having proper
signature will receive JavaScript parameters as PHP values:
class MyWidget { ... public function actionSort($direction, $field, $parentIds, $filter) { var_dump($direction); // Integer `1` var_dump($field); // String `name` var_dump($parentIds); // Integer array `[1,2,3]` var_dump($filter); // stdClass object with property `published` having value `true` } ... }
Creating URL's and links
URL's in this example might look convoluted in some cases, especially
for generated widget ID's, like m18DDYa
. But no worries here, these
URL's are created by specialized classes of ActionUrl
and ActionLink
for creating whole link tag.
Creating URL
To create URL, echo
ActionUrl
with apropriate parameters, for example
to sort
with name
desc
, let's assume widget ID users
:
echo new ActionUrl($widget, 'sort', ['name' => 'dest']);
Will result in URL:
?user.sort=name:desc
Creating link
The class ActionLink
will create whole tag, with rel="virtual"
attribute,
so that it will call javascript widget method, which can call server
method via AJAX.
echo new ActionLink($widget, 'sort', 'Sort by name', ['name' => 'dest']);
Will result in link:
<a href="?user.sort=name:desc" rel="virtual">Sort by name</a>