Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unimplement JsExpressionable from JsCallback #1955

Merged
merged 7 commits into from
Dec 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions demos/_unit-test/sse.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@
$sse->setUrlTrigger('see_test');

$v->js(true, $sse->set(function () use ($sse) {
$sse->send(new JsExpression('console.log(\'test\')'));
$sse->send(new JsExpression('console.log(\'test\')'));
$sse->send(new JsExpression('console.log(\'test\')'));
$sse->send(new JsExpression('console.log(\'test\')'));
for ($i = 0; $i < 4; ++$i) {
$sse->send(new JsExpression('console.log([])', ['test ' . $i]));
}

// non-SSE way
return new JsToast('SSE sent, see browser console log');
}));
})->jsExecute());
2 changes: 1 addition & 1 deletion demos/form-control/multiline.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
$inventory = new MultilineItem($app->db);
$inventory->getField($inventory->fieldName()->item)->ui['multiline'] = [Form\Control\Multiline::TABLE_CELL => ['width' => 2]];
$inventory->getField($inventory->fieldName()->inv_date)->ui['multiline'] = [Form\Control\Multiline::TABLE_CELL => ['width' => 2]];
$inventory->getField($inventory->fieldName()->inv_date)->ui['multiline'] = [Form\Control\Multiline::TABLE_CELL => ['width' => 2]];
$inventory->getField($inventory->fieldName()->inv_time)->ui['multiline'] = [Form\Control\Multiline::TABLE_CELL => ['width' => 2]];
$inventory->getField($inventory->fieldName()->country_id)->ui['multiline'] = [Form\Control\Multiline::TABLE_CELL => ['width' => 3]];
$inventory->getField($inventory->fieldName()->qty)->ui['multiline'] = [Form\Control\Multiline::TABLE_CELL => ['width' => 2]];
$inventory->getField($inventory->fieldName()->box)->ui['multiline'] = [Form\Control\Multiline::TABLE_CELL => ['width' => 2]];
Expand Down
2 changes: 1 addition & 1 deletion docs/callbacks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ When you trigger callback, you'll see the output::
{"success": true, "message": "Success", "eval": "alert(\"ok\")"}

This is how JsCallback renders actions and sends them back to the browser. In order to retrieve and execute actions,
you'll need a JavaScript routine. Luckily JsCallback also implements JsExpressionable, so it, in itself is an action.
you'll need a JavaScript routine. Luckily JsCallback can be passed to :php:meth:`View::on()` as a JS action.

Let me try this again. JsCallback is an :ref:`js_action` which will execute request towards a callback-URL that will
execute PHP method returning one or more :ref:`js_action` which will be received and executed by the original action.
Expand Down
9 changes: 5 additions & 4 deletions src/CardDeck.php
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,9 @@ protected function initActionExecutor(Model\UserAction $action): ExecutorInterfa
* Return proper js statement for afterExecute hook on action executor
* depending on return type, model loaded and action scope.
*
* @param string|array|JsExpressionable|Model|null $return
* @param string|JsExpressionable|array<int, JsExpressionable>|Model|null $return
*
* @return array|object
* @return JsExpressionable|array<int, JsExpressionable>
*/
protected function jsExecute($return, Model\UserAction $action)
{
Expand All @@ -242,10 +242,9 @@ protected function jsExecute($return, Model\UserAction $action)
}

/**
* Return jsNotifier object.
* Override this method for setting notifier based on action or model value.
*/
protected function getNotifier(Model\UserAction $action, string $msg = null): object
protected function getNotifier(Model\UserAction $action, string $msg = null): JsExpressionable
{
$notifier = Factory::factory($this->notifyDefault);
if ($msg) {
Expand All @@ -257,6 +256,8 @@ protected function getNotifier(Model\UserAction $action, string $msg = null): ob

/**
* Js expression return when action afterHook executor return a Model.
*
* @return array<int, JsExpressionable>
*/
protected function jsModelReturn(Model\UserAction $action, string $msg = 'Done!'): array
{
Expand Down
9 changes: 2 additions & 7 deletions src/Console.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,9 @@ public function set($fx = null, $event = null)
return $this;
}

/**
* Return JavaScript expression to execute console.
*
* @return JsExpressionable
*/
public function jsExecute()
public function jsExecute(): JsExpressionable
{
return $this->sse;
return $this->sse->jsExecute();
}

private function escapeOutputHtml(string $message): string
Expand Down
2 changes: 2 additions & 0 deletions src/Crud.php
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ protected function initActionExecutor(Model\UserAction $action)
* depending on return type, model loaded and action scope.
*
* @param string|null $return
*
* @return array<int, JsExpressionable>
*/
protected function jsExecute($return, Model\UserAction $action): array
{
Expand Down
6 changes: 3 additions & 3 deletions src/JsCallback.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Atk4\Ui;

class JsCallback extends Callback implements JsExpressionable
class JsCallback extends Callback
{
/** @var array Holds information about arguments passed in to the callback. */
public $args = [];
Expand Down Expand Up @@ -47,7 +47,7 @@ protected function flattenArray(array $response): array
return $res;
}

public function jsRender(): string
public function jsExecute(): JsExpression
{
$this->getApp(); // assert has App

Expand All @@ -57,7 +57,7 @@ public function jsRender(): string
'confirm' => $this->confirm,
'apiConfig' => $this->apiConfig,
'storeName' => $this->storeName,
])->jsRender();
]);
}

/**
Expand Down
17 changes: 6 additions & 11 deletions src/JsExpression.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public function jsRender(): string
$namelessCount = 0;
$res = preg_replace_callback(
'~\[[\w]*\]|{[\w]*}~',
function ($matches) use (&$namelessCount) {
function ($matches) use (&$namelessCount): string {
$identifier = substr($matches[0], 1, -1);

// Allow template to contain []
Expand All @@ -51,7 +51,7 @@ function ($matches) use (&$namelessCount) {
$value = $this->args[$identifier];

// no escaping for "{}"
if ($matches[0][0] === '{') {
if ($matches[0][0] === '{' && is_string($value)) {
return $value;
}

Expand All @@ -74,15 +74,10 @@ function ($matches) use (&$namelessCount) {
*/
protected function _jsEncode($arg): string
{
if (is_object($arg)) {
if ($arg instanceof JsExpressionable) {
$result = $arg->jsRender();
if (is_object($arg) && $arg instanceof JsExpressionable) {
$result = $arg->jsRender();

return $result;
}

throw (new Exception('Not sure how to represent this object in JSON'))
->addMoreInfo('obj', $arg);
return $result;
} elseif (is_array($arg)) {
$array = [];
$assoc = !array_is_list($arg);
Expand Down Expand Up @@ -115,7 +110,7 @@ protected function _jsEncode($arg): string
} elseif ($arg === null) {
$string = 'null';
} else {
throw (new Exception('Unsupported argument type'))
throw (new Exception('Argument is not renderable to JS'))
->addMoreInfo('arg', $arg);
}

Expand Down
22 changes: 10 additions & 12 deletions src/JsFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class JsFunction implements JsExpressionable
/** @var array */
public $fxArgs;

/** @var array */
/** @var array<int, JsExpressionable> */
public $fxStatements = [];

/** @var bool add preventDefault(event) to generated method */
Expand All @@ -28,12 +28,19 @@ class JsFunction implements JsExpressionable
/** @var string Indent of target code (not one indent level) */
public $indent = ' ';

/**
* @param array<int, JsExpressionable|null>|array<string, mixed> $statements
*/
public function __construct(array $args, array $statements)
{
$this->fxArgs = $args;

foreach ($statements as $key => $value) {
if (is_int($key)) {
if ($value === null) { // TODO this should be not needed
continue;
}

$this->fxStatements[] = $value;
} else {
$this->{$key} = $value;
Expand All @@ -56,18 +63,9 @@ public function jsRender(): string
$output = 'function (' . implode(', ', $this->fxArgs) . ') {'
. $pre;
foreach ($this->fxStatements as $statement) {
if ($statement === null) { // TODO this should be not needed
continue;
}

if ($statement instanceof JsExpressionable) {
$statement = $statement->jsRender();
} else {
throw (new Exception('Incorrect statement for JsFunction'))
->addMoreInfo('statement', $statement);
}
$js = $statement->jsRender();

$output .= "\n" . $this->indent . ' ' . $statement . (!preg_match('~[;}]\s*$~', $statement) ? ';' : '');
$output .= "\n" . $this->indent . ' ' . $js . (!preg_match('~[;}]\s*$~', $js) ? ';' : '');
}

$output .= "\n" . $this->indent . '}';
Expand Down
4 changes: 2 additions & 2 deletions src/JsSse.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ protected function init(): void
}
}

public function jsRender(): string
public function jsExecute(): JsExpression
{
$this->getApp(); // assert has App

Expand All @@ -53,7 +53,7 @@ public function jsRender(): string
$options['closeBeforeUnload'] = $this->closeBeforeUnload;
}

return (new Jquery($this->getOwner() /* TODO element and loader element should be passed explicitly */))->atkServerEvent($options)->jsRender();
return (new Jquery($this->getOwner() /* TODO element and loader element should be passed explicitly */))->atkServerEvent($options);
}

/**
Expand Down
3 changes: 0 additions & 3 deletions src/UserAction/ConfirmationExecutor.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,6 @@ private function jsShowAndLoad(array $urlArgs, array $apiConfig): array
];
}

/**
* Return js expression that will trigger action executor.
*/
public function jsExecute(array $urlArgs = []): array
{
if (!$this->action) {
Expand Down
4 changes: 4 additions & 0 deletions src/UserAction/JsExecutorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@

namespace Atk4\Ui\UserAction;

use Atk4\Ui\JsExpressionable;

/**
* Add js trigger for executing an action.
*/
interface JsExecutorInterface extends ExecutorInterface
{
/**
* Return js expression that will trigger action executor.
*
* @return array<int, JsExpressionable>
*/
public function jsExecute(array $urlArgs): array;
}
3 changes: 0 additions & 3 deletions src/UserAction/ModalExecutor.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,6 @@ public function assignTrigger(View $view, array $urlArgs = [], string $when = 'c
return $this;
}

/**
* Generate js for triggering action.
*/
public function jsExecute(array $urlArgs = []): array
{
if (!$this->actionInitialized) {
Expand Down
26 changes: 22 additions & 4 deletions src/View.php
Original file line number Diff line number Diff line change
Expand Up @@ -960,8 +960,8 @@ public function jsReload($args = [], $afterSuccess = null, $apiConfig = [])
* @see http://agile-ui.readthedocs.io/en/latest/js.html
*
* @param string $event JavaScript event
* @param ($action is null|array ? string|JsExpressionable|\Closure|array|UserAction\ExecutorInterface|Model\UserAction : string|array) $selector Optional jQuery-style selector
* @param string|JsExpressionable|\Closure|array|UserAction\ExecutorInterface|Model\UserAction|null $action code to execute
* @param ($action is null|array ? string|JsExpressionable|JsCallback|\Closure|array|UserAction\ExecutorInterface|Model\UserAction : string|array) $selector Optional jQuery-style selector
* @param string|JsExpressionable|JsCallback|\Closure|array|UserAction\ExecutorInterface|Model\UserAction|null $action code to execute
*
* @return ($selector is null|string ? ($action is null ? Jquery : null) : ($action is null|array ? Jquery : null))
*/
Expand Down Expand Up @@ -1000,6 +1000,22 @@ public function on(string $event, $selector = null, $action = null, array $defau
$eventStatements['preventDefault'] = $defaults['preventDefault'] ?? true;
$eventStatements['stopPropagation'] = $defaults['stopPropagation'] ?? true;

$lazyJsRenderFx = function (\Closure $fx): JsExpressionable {
return new class($fx) implements JsExpressionable {
public \Closure $fx;

public function __construct(\Closure $fx)
{
$this->fx = $fx;
}

public function jsRender(): string
{
return ($this->fx)()->jsRender();
}
};
};

// Dealing with callback action.
if ($action instanceof \Closure || (is_array($action) && ($action[0] ?? null) instanceof \Closure)) {
if (is_array($action)) {
Expand All @@ -1021,7 +1037,7 @@ public function on(string $event, $selector = null, $action = null, array $defau
return $action($chain, ...$args);
}, $arguments);

$actions[] = $cb;
$actions[] = $lazyJsRenderFx(fn () => $cb->jsExecute());
} elseif ($action instanceof UserAction\ExecutorInterface || $action instanceof Model\UserAction) {
// Setup UserAction executor.
$ex = $action instanceof Model\UserAction ? $this->getExecutorFactory()->create($action, $this) : $action;
Expand All @@ -1044,11 +1060,13 @@ public function on(string $event, $selector = null, $action = null, array $defau
if ($defaults['apiConfig'] ?? null) {
$ex->apiConfig = $defaults['apiConfig'];
}
$actions[] = $ex;
$actions[] = $lazyJsRenderFx(fn () => $ex->jsExecute());
$ex->executeModelAction($arguments);
} else {
throw new Exception('Executor must be of type UserAction\JsCallbackExecutor or extend View and implement UserAction\JsExecutorInterface');
}
} elseif ($action instanceof JsCallback) {
$actions[] = $lazyJsRenderFx(fn () => $action->jsExecute());
} elseif (is_array($action)) {
$actions = array_merge($actions, $action);
} else {
Expand Down
Loading