It might have been a long run, but it's time that we give our HTML-writing skill a proper send-off. It has served us well all this time, but we all have a better things to do with our lives then copy-pasting HTML between Bootstrap CSS example page and your template engine.
composer require atk4/ui
Next use this code to add a button:
$button = new \atk4\ui\Button('Hello World');
echo $button->render();
Because, see, my favourite CSS framework is Semantic UI and your favourite CSS framework is probably Bootstrap CSS. If you insist on using bootstrap, then the following code is for you:
$button = new \atk4\ui\Button(['skin'=>'bootstrap3']);
echo $button->render();
Why go through all the trouble, you ask?
Abstraction.
Once the button is an object, you can do so much with it.
I have set up a demo page, where you can see the following examples live: http://ui.agiletoolkit.org/buttons.php. Let's change the label of our existing button:
$button->set('Click me');
or perhaps we can make it look different:
$b1->set(['Load', 'primary']);
$b2->set(['Load', 'labeled', 'icon'=>'pause']);
$b3->set(['Next', 'right labeled', 'icon'=>'right arrow']);
You don't have to specify all properties right away:
$button->set('Click me');
$button->set(['primary' => true]);
$button->set(['icon'=>'check']);
$button->set(['size big'=>true]);
Use positive / negative to clarify button intent:
$b_yes = new \atk4\ui\Button();
$b_yes->set(['Yes', 'positive basic']);
$b_no = new \atk4\ui\Button();
$b_no->set(['No', 'negative basic']);
All of that can also be passed to constructor:
use \atk4\ui\Button;
$b_yes = new Button(['Yes', 'positive basic']);
$b_no = new Button(['No', 'negative basic']);
Remember, that after you create a button object you need to render()
to get HTML of your button then do whatever you need to do with it.
Since we are adding a bunch of buttons, there is a handy way to group them. I am introducing a new class here, Buttons
. It's probably important to note that both Button
and Buttons
extend a View
class, that implements majority of the functionality for them. Using add()
will physically place one View inside another.
The following code will create a vertical button-bar. Note, that you only need to render $bar
which will recursively collect HTML from all the other objects you have added into it.
$bar = new Buttons('vertical'); // NOTE: class called Buttons, not Button
$bar->add(new Button(['Play', 'icon'=>'play']));
$bar->add(new Button(['Pause', 'icon'=>'pause']));
$bar->add(new Button(['Shuffle', 'icon'=>'shuffle']));
echo $bar->render();
This is another example that creates icon-bar:
use \atk4\UI\Buttons;
use \atk4\UI\Button;
$bar = new Buttons('blue big');
$bar->add(new Button(['icon'=>'file']));
$bar->add(new Button(['icon'=>['save','yellow']]));
$bar->add(new Button(['icon'=>'upload', 'disabled'=>true]));
Semantic UI allows me to combine buttons too:
use \atk4\UI\Button;
use \atk4\UI\Label;
use \atk4\UI\Icon;
$forks = new Button(['labeled'=> true]); // Button, not Buttons!
$forks->add(new Button(['Forks', 'blue']))->add(new Icon('fork'));
$forks->add(new Label(['1,048', 'basic blue left pointing']));
As long as you add()
one view into another view, it's guaranteed that the button will have unique ID within that container. Next example also loads a custom template, which you need to create.
use \atk4\UI\View;
use \atk4\UI\Button;
$view = new View(['template'=>'./mytemplate.html']);
$view->add(new Button('World'), 'tag1');
$view->add(new Button(['Agile UI', 'blue']), 'tag2');
// remember to render things!
echo $view->render(); // IDs are: #atk, #atk_button, #atk_button_2.
Alternatively you can specify ID yourself, but you shouldn't really do that, because it's boring.
$b1 = new Button(['Button 1', 'id'=>'b1']);
$b2 = new Button(['Butotn 2', 'id'=>'b2']);
NOTE: Agile UI is still in development. JavaScript functionality as described below will be released in verion 0.3.
Button allows you to bind some JavaScript to it. When using JavaScript you can use method on()
. My next code will create 2 buttons, first will toggle "active" status when clicked. Other will hide itself if you hover mouse over it.
$view = new View();
$view->add(new Button('Button 1'))->on('click')->toggle('active');
$view->add(new Button('Butotn 2'))->on('hover')->hide('slow');
Let's make buttons play nice with each-other too. Next code will create 3 buttons
$view = new View();
$bp = $view->add(new Button(['icon'=>'plus']));
$bn = $view->add(new Button('Hello'));
$bm = $view->add(new Button(['icon'=>'minus']));
$bp->on('click', $view->js()->removeClass('huge')->addClass('tiny'));
$bn->on('click', $view->js()->removeClass('huge tiny');
$bm->on('click', $view->js()->removeClass('tiny')->addClass('huge'));
The js()
method returns a jQueryChain
class object that will be mapped into a string like:
$('#button_3').removeClass('huge').addClass('tiny');
Actually the same is done with the on()
method, but on()
will automatically bind the chain to a specific JavaScript event, producing that nicely looking syntax.
IMPORTANT: remember to load jQuery! Rendering views with JavaScript will output <script>
tag as part of their HTML response.
If you wish to place it inside your on-dom-ready call-back yourself, use getJS()
:
$b1 = new Button('Click me');
$b1->on('click')->toggle('active');
$on_ready_js = '';
$b1->addHook('outputJS', function($js) use ($on_ready_js) {
$on_ready_js .= $js->render();
});
$html = $b1->render();
Buttons can be pretty interractive too. First, let's add a PHP handler for your button event. When you click on the Hello button it would keep changing it's value to a random number:
$b1 = new Button('Hello');
$b1->on('click', function($js) {
return $js->text(rand(1, 100));
});
But you can also make button interract with another view:
$forks = new Button(['labeled'=> true]);
$forks->add(new Button(['Forks', 'basic blue', 'icon'=>'fork']));
$label = $forks->add(new Label(50, ['basic blue left pointing']));
$forks->on('click', function() use ($label) {
return $label->text(51);
});
The other powerful concept is reloading, which is similar to repaint
event in a classical UI Toolkits (old-school desktop apps). You don't have to worry about JavaScript events on the button that you are re-loading, because it will be re-applied automatically if required.
$bar = new Buttons();
$b1 = $bar->add(new Button('Hello'));
$b2 = $bar->add(new Button(rand(1, 100)))->on('click')->hide();
$b1->on('click', $b2->reload());
IMPORTANT: Interractive events will perform GET request to your current URL with some additional arguments.
NOTE: Agile UI is still in development. Callback functionality as described below will be released in verion 0.4.
Because Button is an object now, you can easily extend it to enhance it's functionality. Let's create a "Stargazer" button just like in GitHub uses (which you can test here: http://github.com/atk4/ui)
class StargazerButton extends \atk4\ui\Button {
public $model;
public $label;
function init() {
parent::init();
$this->add(new Button(['Stars', 'basic blue', 'icon'=>'star']));
$this->label = $this->add(new Label($model['stars'], ['basic blue left pointing']));
$this->on('click', function() {
$this->model['stars']++;
$this->model->save();
return $this->reload();
});
}
}
The method init()
is called automatically when you add object into a container or render it:
$stars = new StargazerButton(['model'=>$model]);
echo $stars->render();
The beauty of having objects accessible, is that you can fine-tune them after they are created:
$stars = new StargazerButton(['model'=>$model]);
$stars->init(); // if we don't plan to add stars into any other view
$stars->label->removeClass('blue')->addClass('red');
echo $stars->render();
In fact I plan to create many other UI classes including Form, List, Menu, Grid, CRUD and so on. You are welcome to keep track of my project or even take part in it by providing feedback or testing. I would also have to rely on you to integrate it with YOUR favourite PHP frameworks or application.
Before someone will point out about performance tax and "we already know HTML", I want to present my reasons for creating Agile UI in a first place:
For the question on the Q&A site:
Q: What do you need before I can learn a PHP framework?
The current answer is "HTML, JavaScript, CSS". My ambition is to abstract those technologies away so that those who learn PHP could spend more time on learning how to consistently name their methods and variables.
The HTML, JS and CSS are abstracted away and are still there for those who want to mess with it.
A lot of commercial projects with tight deadlines will also appreciate decrease in development and support costs.
Since the technical implementaion is abstracted, 3rd party code can finally start interacting with the user interface objects without worrying about messing your HTML. I would expect to see more and more 3rd party UI-focused add-ons that want to take advantage of a standartised Web UI.
It takes a good timing, a clever architecture and good cause for new technology to take over.
Yes, there were other attempts to implement server-side object mapping into HTML. Microsoft's WebForms has flopped, PHOCOA and some similar projects gained very little traction. While the efficiency was evident, those solutions have had significant implementation issues (proprietary and bulky JS, weird/old look, lack of theeming and slow performance).
By carefully studying why they have failed, I can make sure it's not happening with Agile UI.
A very significant factor is the prominent growth of CSS frameworks. Many of new CSS frameworks introduce theming and create it super-simple to implement layouts.
My rule is to keep PHP as transparent as possible, instead of creating thick layer for mapping every single method, class and property. This allows you to take full advantage of CSS framework capabilities while remaining inside PHP.
Most UI objects will have sophisticated multi-element HTML template, so your typical page will use 5-10 PHP UI objects and not one object for each HTML element. That will keep performance from spiraling out of control.
Agile UI is designed in such a way, so that ANY PHP framework or application could take advantage of it. Have you got some legacy project with a terrible code? Agile UI will work there just fine.
Using a massively popular jQuery instead of some custom JavaScript and absence of any proprietary dependencies or deep linking with any specific MVC framework should help Agile UI to become a User-Interface-framework of choice.
Well, at least that's my wish. Since it's my birthday today, I wonder if it will be granted.
The final remark is attribution to the object architecture. I have been refining it since 2001 (mostly as a proprietary project). If you are looking for a proof, look at the site www.linkedfinance.com which is just one of the web apps that runs a full-stack UI framework: Agile Toolkit 4.
But who am I to take you away from the comfort of your Laravel, Symfony or Wordpress eco-system or teach you about Web Programming.
I am here just to give you a Button.