Skip to content

Commit

Permalink
View::getJs() method returns JsBlock (#2152)
Browse files Browse the repository at this point in the history
  • Loading branch information
mvorisek authored Feb 1, 2024
1 parent bf72a14 commit 5c86e53
Show file tree
Hide file tree
Showing 15 changed files with 97 additions and 88 deletions.
2 changes: 1 addition & 1 deletion docs/view.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ Returns HTML for this View as well as all the child views.
:::

:::{php:method} getJs()
Return array of JS chains that was assigned to current element or it's children.
Returns JsBlock containing JS chains that were assigned to current element or it's children.
:::

## Modifying rendering logic
Expand Down
4 changes: 2 additions & 2 deletions js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ the JSON file for this.
npm run profile
```

This command will create a profile JSON file `atkjs-bundle-profile.json` with bundle information inside the profile folder. You can use this file with your
This command will create a profile JSON file `webpack-bundle-profile.local.json` with bundle information inside the profile folder. You can use this file with your
favorite bundle analyzer.

Another npm script is available for analyzing the bundle using the webpack-bundle-analyzer tool.
Expand All @@ -86,7 +86,7 @@ Another npm script is available for analyzing the bundle using the webpack-bundl
npm run analyze-profile
```

Note: In order to use this script, make sure that the webpack-bundle-analyzer package is install
Note: In order to use this script, make sure that the webpack-bundle-analyzer package is installed
globally.

```
Expand Down
4 changes: 2 additions & 2 deletions js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"scripts": {
"build": "webpack --env development && webpack --env production",
"dev": "webpack --progress --watch --env development",
"profile": "webpack --env production --profile --json > profile/atkjs-bundle-profile.json",
"analyze-profile": "webpack-bundle-analyzer profile/atkjs-bundle-profile.json",
"profile": "webpack --env production --profile --json > webpack-bundle-profile.local.json",
"analyze-profile": "webpack-bundle-analyzer webpack-bundle-profile.local.json",
"lint": "eslint --ext .js,.vue . && stylelint \"../public/**/*.less\" && prettier --config .prettierrc.js --log-level warn \"../public/**/*.less\" --check",
"lint-fix": "eslint --ext .js,.vue . --fix && stylelint \"../public/**/*.less\" --fix && prettier --config .prettierrc.js --log-level warn \"../public/**/*.less\" --write"
},
Expand Down
14 changes: 7 additions & 7 deletions js/src/services/api.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ class ApiService {
/**
* Execute JS code.
*
* This function should be called using .call() by passing proper context for 'this'.
* ex: apiService.evalResponse.call(this, code)
*
* @param {object} thisObject
* @param {string} code
*/
evalResponse(code) {
eval(code); // eslint-disable-line no-eval
evalJsCode(thisObject, code) {
(function () {
eval('\'use strict\'; (() => {' + code + '})()'); // eslint-disable-line no-eval
}).call(thisObject);
}

/**
Expand Down Expand Up @@ -113,13 +113,13 @@ class ApiService {
}

if (response.atkjs) {
atk.apiService.evalResponse.call(this, response.atkjs);
atk.apiService.evalJsCode(this, response.atkjs);
}

if (atk.apiService.afterSuccessCallbacks.length > 0) {
const callbacks = atk.apiService.afterSuccessCallbacks;
for (const callback of callbacks) {
atk.apiService.evalResponse.call(this, callback);
atk.apiService.evalJsCode(this, callback);
}
atk.apiService.afterSuccessCallbacks.splice(0);
}
Expand Down
14 changes: 7 additions & 7 deletions public/js/atkjs-ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -1968,13 +1968,13 @@ class ApiService {
/**
* Execute JS code.
*
* This function should be called using .call() by passing proper context for 'this'.
* ex: apiService.evalResponse.call(this, code)
*
* @param {object} thisObject
* @param {string} code
*/
evalResponse(code) {
eval(code); // eslint-disable-line no-eval
evalJsCode(thisObject, code) {
(function () {
eval('\'use strict\'; (() => {' + code + '})()'); // eslint-disable-line no-eval
}).call(thisObject);
}

/**
Expand Down Expand Up @@ -2046,12 +2046,12 @@ class ApiService {
}
}
if (response.atkjs) {
atk__WEBPACK_IMPORTED_MODULE_6__["default"].apiService.evalResponse.call(this, response.atkjs);
atk__WEBPACK_IMPORTED_MODULE_6__["default"].apiService.evalJsCode(this, response.atkjs);
}
if (atk__WEBPACK_IMPORTED_MODULE_6__["default"].apiService.afterSuccessCallbacks.length > 0) {
const callbacks = atk__WEBPACK_IMPORTED_MODULE_6__["default"].apiService.afterSuccessCallbacks;
for (const callback of callbacks) {
atk__WEBPACK_IMPORTED_MODULE_6__["default"].apiService.evalResponse.call(this, callback);
atk__WEBPACK_IMPORTED_MODULE_6__["default"].apiService.evalJsCode(this, callback);
}
atk__WEBPACK_IMPORTED_MODULE_6__["default"].apiService.afterSuccessCallbacks.splice(0);
}
Expand Down
2 changes: 1 addition & 1 deletion public/js/atkjs-ui.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/js/atkjs-ui.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/js/atkjs-ui.min.js.map

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion src/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
use Atk4\Ui\Exception\ExitApplicationError;
use Atk4\Ui\Exception\LateOutputError;
use Atk4\Ui\Exception\UnhandledCallbackExceptionError;
use Atk4\Ui\Js\Jquery;
use Atk4\Ui\Js\JsExpression;
use Atk4\Ui\Js\JsExpressionable;
use Atk4\Ui\Js\JsFunction;
use Atk4\Ui\Persistence\Ui as UiPersistence;
use Atk4\Ui\UserAction\ExecutorFactory;
use Nyholm\Psr7\Factory\Psr17Factory;
Expand Down Expand Up @@ -677,7 +679,10 @@ public function run(): void

$this->html->template->set('title', $this->title);
$this->html->renderAll();
$this->html->template->dangerouslyAppendHtml('Head', $this->getTag('script', [], '$(function () {' . $this->html->getJs() . ';});'));
$this->html->template->dangerouslyAppendHtml(
'Head',
$this->getTag('script', [], (new Jquery(new JsFunction([], $this->html->getJs())))->jsRender() . ';')
);
$this->isRendering = false;

if ($this->hasRequestQueryParam(Callback::URL_QUERY_TARGET) && $this->catchRunawayCallbacks) {
Expand Down
13 changes: 9 additions & 4 deletions src/Js/JsFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,15 @@ public function jsRender(): string
$pre .= $this->indent . ' event.stopPropagation();' . "\n";
}

$output = $this->indent . 'function (' . implode(', ', $this->args) . ') {' . "\n"
. $pre
. preg_replace('~^~m', $this->indent . ' ', $this->body->jsRender()) . "\n" // TODO IMPORTANT indentation must ignore multiline strings/comments!
. $this->indent . '}';
$body = $this->body->jsRender();

$output = $this->indent . 'function (' . implode(', ', $this->args) . ') {';
if ($body !== '') {
$output .= "\n" . $pre
. preg_replace('~^(?!$)~m', $this->indent . ' ', $body) . "\n" // TODO IMPORTANT indentation must ignore multiline strings/comments!
. $this->indent;
}
$output .= '}';

return $output;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Lister.php
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ public function renderToJsonArr(string $region = null): array

return [
'success' => true,
'atkjs' => $this->getJs(),
'atkjs' => $this->getJs()->jsRender(),
'html' => $this->template->renderToHtml($region),
'id' => $this->name,
];
Expand Down
33 changes: 5 additions & 28 deletions src/View.php
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,7 @@ public function render(): string
{
$this->renderAll();

$js = $this->getJs();
$js = $this->getJs()->jsRender();

return ($js !== '' ? $this->getApp()->getTag('script', [], '$(function () {' . $js . ';});') : '')
. $this->renderTemplateToHtml();
Expand All @@ -670,7 +670,7 @@ public function renderToTab(): array
$this->renderAll();

return [
'atkjs' => $this->getJsRenderActions(),
'atkjs' => $this->getJs()->jsRender(),
'html' => $this->renderTemplateToHtml(),
];
}
Expand All @@ -684,7 +684,7 @@ public function renderToJsonArr(): array

return [
'success' => true,
'atkjs' => $this->getJs(),
'atkjs' => $this->getJs()->jsRender(),
'html' => $this->renderTemplateToHtml(),
'id' => $this->name,
];
Expand Down Expand Up @@ -1077,29 +1077,10 @@ public function jsRender(): string
return $res;
}

/**
* Return rendered js actions as a string.
*/
public function getJsRenderActions(): string
{
$actions = [];
foreach ($this->_jsActions as $eventActions) {
foreach ($eventActions as $action) {
$actions[] = $action;
}
}

return (new JsBlock($actions))->jsRender();
}

/**
* Get JavaScript objects from this render tree.
*
* TODO dedup with getJsRenderActions()
*
* @return string
*/
public function getJs()
public function getJs(): JsBlock
{
$actions = [];
foreach ($this->_jsActions as $eventActions) {
Expand All @@ -1108,11 +1089,7 @@ public function getJs()
}
}

if (count($actions) === 0) {
return '';
}

return (new JsExpression('[]()', [new JsFunction([], $actions)]))->jsRender();
return new JsBlock($actions);
}

// }}}
Expand Down
14 changes: 11 additions & 3 deletions src/VirtualPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

namespace Atk4\Ui;

use Atk4\Ui\Js\Jquery;
use Atk4\Ui\Js\JsFunction;

/**
* Virtual page normally does not render, yet it has it's own trigger and will respond
* to the trigger in a number of useful way depending on trigger's argument:.
Expand Down Expand Up @@ -95,7 +98,10 @@ public function getHtml(): string
if ($mode === 'popup') {
$this->getApp()->html->template->set('title', $this->getApp()->title);
$this->getApp()->html->template->dangerouslySetHtml('Content', parent::getHtml());
$this->getApp()->html->template->dangerouslyAppendHtml('Head', $this->getApp()->getTag('script', [], '$(function () { ' . $this->getJs() . '; });'));
$this->getApp()->html->template->dangerouslyAppendHtml(
'Head',
$this->getApp()->getTag('script', [], (new Jquery(new JsFunction([], $this->getJs())))->jsRender() . ';')
);

$this->getApp()->terminateHtml($this->getApp()->html->template);
}
Expand Down Expand Up @@ -132,8 +138,10 @@ public function getHtml(): string
}

$this->getApp()->html->template->dangerouslySetHtml('Content', $this->getApp()->layout->template->renderToHtml());

$this->getApp()->html->template->dangerouslyAppendHtml('Head', $this->getApp()->getTag('script', [], '$(function () {' . $this->getApp()->layout->getJs() . ';});'));
$this->getApp()->html->template->dangerouslyAppendHtml(
'Head',
$this->getApp()->getTag('script', [], (new Jquery(new JsFunction([], $this->getApp()->layout->getJs())))->jsRender() . ';')
);

$this->getApp()->terminateHtml($this->getApp()->html->template);
}
Expand Down
63 changes: 34 additions & 29 deletions tests/JsIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,7 @@ public function testChainTrue(): void
$v->setApp($this->createApp());
$v->renderAll();

self::assertSame('(function () {
$(\'#b\').hide();
})()', $v->getJs());
self::assertSame('$(\'#b\').hide();', $v->getJs()->jsRender());
}

public function testChainClick(): void
Expand All @@ -66,13 +64,16 @@ public function testChainClick(): void
$v->setApp($this->createApp());
$v->renderAll();

self::assertSame('(function () {
$(\'#b\').on(\'click\', function (event) {
event.preventDefault();
event.stopPropagation();
$(this).hide();
});
})()', $v->getJs());
self::assertSame(
<<<'EOF'
$('#b').on('click', function (event) {
event.preventDefault();
event.stopPropagation();
$(this).hide();
});
EOF,
$v->getJs()->jsRender()
);
}

public function testChainClickEmpty(): void
Expand All @@ -82,15 +83,16 @@ public function testChainClickEmpty(): void
$v->setApp($this->createApp());
$v->renderAll();

self::assertSame('(function () {
$(\'#b\').on(\'click\', function (event) {
event.preventDefault();
event.stopPropagation();
'
. '$(this);' // this JS statement is not required
. '
});
})()', $v->getJs());
self::assertSame(
<<<'EOF'
$('#b').on('click', function (event) {
event.preventDefault();
event.stopPropagation();
EOF
. "\n $(this);" // this JS statement is not required
. "\n});",
$v->getJs()->jsRender()
);
}

public function testChainNested(): void
Expand All @@ -108,15 +110,18 @@ public function testChainNested(): void
$v->setApp($this->createApp());
$v->renderAll();

self::assertSame('(function () {
$(\'#b1\').on(\'click\', function (event) {
event.stopPropagation();
$(\'#b1\').hide();
$(\'#b2\').hide();
$(\'#b2\').hide();
});
$(\'#b1\').data(\'x\', \'y\');
})()', $v->getJs());
self::assertSame(
<<<'EOF'
$('#b1').on('click', function (event) {
event.stopPropagation();
$('#b1').hide();
$('#b2').hide();
$('#b2').hide();
});
$('#b1').data('x', 'y');
EOF,
$v->getJs()->jsRender()
);
}

public function testChainNullReturn(): void
Expand Down Expand Up @@ -169,7 +174,7 @@ public function jsExecute(): JsBlock
$v->renderAll();
self::assertSame(0, $jsCallback->counter);

$v->getJs();
$v->getJs()->jsRender();
self::assertSame(1, $jsCallback->counter);
}
}
9 changes: 9 additions & 0 deletions tests/JsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,15 @@ public function testComplex1(): void
EOF, $fx->jsRender());
}

public function testFunctionEmpty(): void
{
$js = new JsFunction([], []);
self::assertSame('function () {}', $js->jsRender());

$js = new JsFunction([], new JsBlock());
self::assertSame('function () {}', $js->jsRender());
}

public function testTagNotDefinedRenderException(): void
{
$js = new JsExpression('[foo]', ['foo']);
Expand Down

0 comments on commit 5c86e53

Please sign in to comment.