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

Feature/js reloading #66

Merged
merged 28 commits into from
Mar 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a200018
Implement basic reloading for #51
romaninsh Mar 12, 2017
5715fa4
Apply fixes from StyleCI
romaninsh Mar 12, 2017
34add1d
Add functionality in order to reload in-place
ob6160 Mar 15, 2017
959a523
Apply fixes from StyleCI
romaninsh Mar 15, 2017
33bf457
Include <script> when calling reload call-back.
romaninsh Mar 15, 2017
bb7054c
Implement beforeRender and beforeOutput hooks.
romaninsh Mar 16, 2017
fb70e7e
Implemented $app->terminate(output)
romaninsh Mar 16, 2017
e8ec8d7
Merge branch 'develop' into feature/js-reloading
romaninsh Mar 16, 2017
f4eb419
cleanup $app->add(), although i think perhaps we should remove this??
romaninsh Mar 16, 2017
e95b66c
track application execution state.
romaninsh Mar 16, 2017
b0af2d8
Add comments and constructor for CallBack (supports defaults)
romaninsh Mar 16, 2017
38143fa
Add documentation for callbacks.
romaninsh Mar 16, 2017
e69dfd2
add documentation for app into index.
romaninsh Mar 16, 2017
b8f3b4a
Add CallbackLater which is not executed immediatelly.
romaninsh Mar 16, 2017
edb6237
Refactor jsReload to use callbackLater - more reliable.
romaninsh Mar 16, 2017
57df143
Add test-suite for callbacks.
romaninsh Mar 16, 2017
1e42465
Add documentation for Reloading.
romaninsh Mar 16, 2017
5a3cd36
Apply fixes from StyleCI
romaninsh Mar 16, 2017
76da286
add field->jsInput() method + docs
romaninsh Mar 16, 2017
141c2d8
Added really cool demo with counter containers.
romaninsh Mar 16, 2017
8e74ff3
Merge branch 'feature/js-reloading' of github.com:atk4/ui into featur…
romaninsh Mar 16, 2017
0ec6538
Apply fixes from StyleCI
romaninsh Mar 16, 2017
cbe4301
remove stopPropagation and preventDefault from default events
romaninsh Mar 16, 2017
a403ce6
Merge branch 'feature/js-reloading' of github.com:atk4/ui into featur…
romaninsh Mar 16, 2017
9218df6
fix test to reflect our update with the propagation
romaninsh Mar 16, 2017
ea80236
proper comment
DarkSide666 Mar 17, 2017
f548a1e
minor improvements
romaninsh Mar 19, 2017
c731166
even though we are not displaying this, shouldn't use 'hello world' t…
romaninsh Mar 19, 2017
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
1 change: 1 addition & 0 deletions demos/init.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

$basic = $layout->leftMenu->addGroup(['Interactivity', 'icon'=>'talk']);
$basic->addItem('JavaScript Events', ['button2']);
$basic->addItem('Element Reloading', ['reloading']);

$f = basename($_SERVER['PHP_SELF']);

Expand Down
51 changes: 51 additions & 0 deletions demos/reloading.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

require 'init.php';

// Test 1 - Basic reloading
$layout->add(['Header', 'Button reloading segment']);
$v = $layout->add(['View', 'ui'=>'segment'])->set((string) rand(1, 100));
$layout->add(['Button', 'Reload random number'])->js('click', new \atk4\ui\jsReload($v));

// Test 2 - Reloading self
$layout->add(['Header', 'JS-actions will be re-applied']);
$b2 = $layout->add(['Button', 'Reload Myself']);
$b2->js('click', new \atk4\ui\jsReload($b2));

// Test 3 - avoid duplicate
$layout->add(['Header', 'No duplicate JS bindings']);
$b3 = $layout->add(['Button', 'Reload other button']);
$b4 = $layout->add(['Button', 'Add one dot']);

$b4->js('click', $b4->js()->text(new \atk4\ui\jsExpression('[]+"."', [$b4->js()->text()])));
$b3->js('click', new \atk4\ui\jsReload($b4));

// Test 3 - avoid duplicate
$layout->add(['Header', 'Make sure nested JS bindings are applied too']);
$seg = $layout->add(['View', 'ui'=>'segment']);

// Re-usable component implementing counter
class Counter extends \atk4\ui\FormField\Line
{
public $content = 20; // default

public function init()
{
parent::init();

$this->actionLeft = new \atk4\ui\Button(['icon'=> 'minus']);
$this->action = new \atk4\ui\Button(['icon'=> 'plus']);

$this->actionLeft->js('click', $this->jsInput()->val(new \atk4\ui\jsExpression('parseInt([])-1', [$this->jsInput()->val()])));
$this->action->js('click', $this->jsInput()->val(new \atk4\ui\jsExpression('parseInt([])+1', [$this->jsInput()->val()])));
}
}

// Add 3 counters
$seg->add(new Counter());
$seg->add(new Counter('40'));
$seg->add(new Counter('-20'));

// Add button to reload all counters
$bar = $layout->add('Buttons');
$b = $bar->add(['Button', 'Reload counter'])->js('click', new \atk4\ui\jsReload($seg));
31 changes: 31 additions & 0 deletions docs/app.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@


.. _app:

===
App
===

.. php:class:: App

App is the object that is a life-supply function for all of your views. App can
either implement features on it's own or rely on a Full Stack PHP framework (or Application)
that incapsulates Agile UI.

$app = new App();


.. php:method:: terminate(output)

Used when application flow needs to be terminated preemptievely. For example when
call-back is triggered and need to respond with some JSON.

Hooks
=====

Application implements HookTrait (http://agile-core.readthedocs.io/en/develop/hook.html)
and the following hooks are available:

- beforeRender
- beforeOutput

2 changes: 1 addition & 1 deletion docs/button.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Button
======

.. php:namespace:: atk4\ui\Button
.. php:namespace:: atk4\ui

.. php:class:: Button

Expand Down
31 changes: 30 additions & 1 deletion docs/callbacks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,34 @@ call-back to decide on-the-spot the correct response action for supplied data. T
PHP developer to invoke actions without writing custom handlers in the JavaScript.


.. php::class Callback
.. php::class: Callback

This class implements the actuall callback functionality. You can use it to perform arbitrary actions::

$button = $layout->add('Button');
$button->set('Click to do something')->link(
$button
->add('Callback')
->set(function(){
do_something();
})
->getURL()
);

In this example `do_something` will be executed, but the execution will continue.

.. php::method: getURL()

Returns a generated URL which will cause callback to be executed.

.. php:method: set(callback, arguments)

Will specify a callback to be executed if the URL is triggered.


.. php::class: CallbackLater

This class is very similar to Callback but it will not execute immediatelly. Instead it will be executed
either at the end at beforeRender or beforeOutput hook from inside App, whichever comes first.


14 changes: 13 additions & 1 deletion docs/field.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Input Fields

Agile UI dedicates a separate namespace for the Form Fields. What's common about
the Form Fields is that they have either 'input' or 'select' element inside them
making them perfect for using inside a :php:class:`Form`.
making them perfect for using inside a :php:class:`\atk4\ui\Form`.

Field can also be used on it's own like this::

Expand Down Expand Up @@ -122,3 +122,15 @@ Integration with Form
---------------------

This section explains how Field interracts with the form.

JavaScript on Input
-------------------

.. php:method:: jsInput([$event, [$other_action]])

Input class implements method jsInput which is identical to :php:meth:`View::js`, except
that it would target the INPUT element rather then the whole field::

$field->jsInput(true)->val(123);


1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Contents:
form
grid
misc
app



Expand Down
29 changes: 29 additions & 0 deletions docs/js.rst
Original file line number Diff line number Diff line change
Expand Up @@ -419,3 +419,32 @@ This will map into the following JavaScript code:

You can further simplify JavaScript code yourself, but keep the JavaScript logic inside the `.js` files
and leave PHP only for binding.


Reloading
=========

.. php:class:: jsReload

jsReload is a JavaScript action that performs reload of a certain object::

$js_reload_table = new jsReload($table);

This action can be used similar to any other jsExpression. For intance completing the form can reload some
other view::

$m_book = new Book($db);

$f = $app->layout->add('Form');
$t = $app->layout->add('Table');

$f->setModel($m_book);

$f->onSubmit(function($f) use($t) {
$f->model->save();
return new \atk4\ui\jsReload($t);
});

$t->setModel($m_book);

In this example, filling out and submitting the form will result in table contents being refreshed using AJAX.
26 changes: 26 additions & 0 deletions src/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ class App
init as _init;
}

use \atk4\core\HookTrait;

// @var string Name of application
public $title = 'Agile UI - Untitled Application';

Expand All @@ -30,6 +32,8 @@ class App

public $run_called = false;

public $is_rendering = false;

public $ui_persistence = null;

public function __construct($defaults = [])
Expand Down Expand Up @@ -108,6 +112,18 @@ public function outputDebug($str)
echo 'DEBUG:'.$str.'<br/>';
}

/**
* Will perform a preemptive output and terminate. Do not use this
* directly, instead call it form Callback, jsCallback or similar
* other classes.
*/
public function terminate($output = null)
{
echo $output;
$this->run_called = true; // prevent shutdown function from triggering.
exit;
}

public function initLayout($layout, $options = [])
{
if (is_string($layout)) {
Expand Down Expand Up @@ -154,16 +170,26 @@ public function add()
} else {
list($obj) = func_get_args();

if (!is_object($obj)) {
throw new Exception(['Incorrect use of App::add']);
}

$obj->app = $this;

return $obj;
}
}

public function run()
{
$this->run_called = true;
$this->hook('beforeRender');
$this->is_rendering = true;
$this->html->template->set('title', $this->title);
$this->html->renderAll();
$this->html->template->appendHTML('HEAD', $this->html->getJS());
$this->is_rendering = false;
$this->hook('beforeOutput');
echo $this->html->template->render();
}

Expand Down
46 changes: 46 additions & 0 deletions src/Callback.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,57 @@
use atk4\core\AppScopeTrait;
use atk4\core\TrackableTrait;

/**
* Add this object to your render tree and it will expose a unique URL which, when
* executed directly will perform a PHP callback that you set().
*
* $button = $layout->add('Button');
* $button->set('Click to do something')->link(
* $button
* ->add('Callback')
* ->set(function(){
* do_something();
* })
* ->getURL()
* );
*/
class Callback
{
use TrackableTrait;
use AppScopeTrait;

/**
* Will look for trigger in the POST data. Will re-use existing URL, but
* $_POST[$this->name] will have to be set.
*/
public $POST_trigger = false;

public $triggered = false;

/**
* Initialize object and set default properties.
*
* @param array|string $defaults
*
* @throws Exception
*/
public function __construct($defaults = [])
{
if (!is_array($defaults)) {
throw new Exception(['Constructor requires array argument', 'arg' => $defaults]);
}

foreach ($defaults as $key => $val) {
if (property_exists($this, $key)) {
if (is_array($val)) {
$this->$key = array_merge(isset($this->$key) && is_array($this->$key) ? $this->$key : [], $val);
} elseif ($val !== null) {
$this->$key = $val;
}
}
}
}

/**
* Executes user-specified action when call-back is triggered.
*
Expand All @@ -24,6 +66,10 @@ class Callback
*/
public function set($callback, $args = [])
{
if (!$this->app) {
throw new Exception(['Call-back must be part of a RenderTree']);
}

if ($this->POST_trigger) {
if (isset($_POST[$this->name])) {
$this->triggered = true;
Expand Down
30 changes: 30 additions & 0 deletions src/CallbackLater.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace atk4\ui;

/**
* Works same as Callback but will be executed when the current
* pass completes. Agile UI uses two-pass system - first to
* initialize objects, second to render them. If you use this during
* Init, then it will be executed.
*/
class CallbackLater extends Callback
{
public function set($callback, $args = [])
{
if (!$this->app) {
throw new Exception(['Call-back must be part of a RenderTree']);
}

if ($this->app->is_rendering) {
$hook = 'beforeOutput';
} else {
$hook = 'beforeRender';
}

$this->app->addHook($hook, function (...$args) use ($callback) {
array_shift($args); // Hook will have first argument pointing to the app. We don't need that.
return parent::set($callback, $args);
}, $args);
}
}
Loading