- Basic Concepts
- Introduction
- Processing user interactions
- Nesting leaves
- Data Binding
- Tutorial
- Building a blog
- View Bridges
- Introduction
- The model
- Re-rendering
- Events
- Handling Children
- Controls
- Extending View Bridges
- Control Components
- What is a control?
- Text Controls
- TextBox
- TextArea
- PasswordTextBox
- Selection Controls
- The selection control pattern
- Standard Variants
- DropDown
- CheckSet
- RadioButtons
- SearchControl
- ModelSearchControl
- Attaching additional data
- Dynamically driving the available items
- File Uploads
- SimpleFileUpload
- Html5FileUpload
- Other
- Buttons
- Building your own control
- Application Components
- What is an application component?
- Pager
- Table
- SearchPanel
- Tabs
- Advanced Topics
- View Indexes
View Bridges
A View Bridge is a Javascript class that represents the client side behaviours of a Leaf and allows the client and server to be easily bridged.
It provides a solid pattern for building Javascript UIs without the need to build REST APIs or use large Javascript frameworks such as React or Vue.js
When to use a View Bridge
You should always use a View Bridge any time you require javascript interactions for your Leaf UI.
When not to use a View Bridge
If you're building a largely client side application you should consider going all-in on a Javascript framework like React or Vue.js and tie it to a REST API, instead of using Leaf and ViewBridges.
Using a View Bridge with React or Vue.js
In a fix you can host a React of Vue.js app inside a simple Leaf/View/ViewBridge container which allows you a very simple way to raise events back on the server instead of building a REST API. This may not be the right thing to do however if you're just starting an application. If you think you will need to build a public REST API anyway, you should just plan to use that.
Creating a View Bridge
Create a file in the same directory as your View class. By convention we give it the same name as the View with Bridge.js appended on the end.
The content should look like this: HomepageViewBridge.js
rhubarb.vb.create('HomepageViewBridge', function() {
return {
onReady:function() {
}
};
})
This registers the creation of a new View Bridge and gives it a name of HomepageViewBridge
. Again
the name is by convention the name of the View with Bridge appended on the end.
We also define our first method on the View Bridge. onReady
is called once the DOM is read. Any
child View Bridges will also be ready when this is called.
In the view we now need to declare the file and name of the View Bridge to use: HomepageView.php
class HomepageView extends View
{
public function getDeploymentPackage()
{
return new LeafDeploymentPackage(__DIR__ . '/HomepageViewBridge.js');
}
protected function getViewBridgeName()
{
return 'HomepageViewBridge';
}
}
By implementing getDeploymentPackage()
we return a deployment package to ensure our View Bridge
Javascript file is deployed and loaded on the page. getViewBridgeName()
confirms the name
of the View Bridge class registered by our Javascript file.
The viewNode property
All View Bridge objects have a viewNode
property which is a reference to the HTML DOM element
for our View's container. Usually this is a <div>
that surrounds our View content. Control
leaves or leaves that only have one HTML element the viewNode may refer to that single element.
For example the TextBox has a viewNode property that points directly to the <input type="text" />
element expressed by the TextBox view.
This allows you to scope searches for child DOM elements or to manipulate the container itself:
rhubarb.vb.create('HomepageViewBridge', function() {
return {
onReady:function() {
// Hide the view when it's ready
this.viewNode.style.display = 'none';
}
};
})
attachEvents versus onReady
There is a second lifecycle method often used interchangeably with onReady called attachEvents
.
The key difference between these two is that onReady
is only called when the page loads.
attachEvents
is also called on page load but is also called in the event of a server initiated
reRender
of the view.
It's good practice to use onReady for initial initialisation but put all DOM event registrations
in the attachEvents
method:
rhubarb.vb.create('HomepageViewBridge', function() {
return {
onReady:function() {
// Do initialisation things
this.counter = 0;
},
attachEvents: function(){
this.viewNode.querySelector('.hide-button').addEventListener('click', function(){
this.viewNode.style.display = 'none';
}.bind(this));
}
};
})
Raising events to the server
A common task for a View Bridge is contacting the server either to pass it data to complete an action, to fetch new information, or both.
Simply call raiseServerEvent
to initiate a server call:
rhubarb.vb.create('BasketViewBridge', function() {
return {
onReady:function() {
// Do initialisation things
this.counter = 0;
},
attachEvents: function(){
this.viewNode.querySelector('.update-basket').addEventListener('click', function(){
this.raiseServerEvent('updateBasket');
}.bind(this));
}
};
})
On the PHP side you need to create a matching named event on the model for your Leaf: BasketModel
class BasketModel extends LeafModel
{
/** @var Event $updateBasketEvent */
public $updateBasketEvent;
public function __construct()
{
parent::__construct();
$this->updateBasketEvent = new Event();
}
}
And then attach a handler on either the Leaf or the View.
While handlers can be attached from either the Leaf or the View it's extremely rare for the View to handle events. The exception is where the View needs to process and return new HTML for the View Bridge.
class Basket extends Leaf
{
/** @var BasketModel $model */
protected $model;
public function __construct()
{
parent::__construct();
}
protected function onModelCreated()
{
parent::onModelCreated();
$this->model->updateBasketEvent->attachHandler(function(){
$order = Order::singleton();
$order->calculateTotals();
return $order->totalPrice;
});
}
}
Notice that the event name on the server must end with the word Event
.
The PHP event handler can return a value which is passed back to the Javascript caller. Any value that can be json encoded can be returned: strings, ints, decimals, bools, arrays and simple objects.
To receive the value passed back you need to supply a success callback function to the
raiseServerEvent
call:
rhubarb.vb.create('BasketViewBridge', function() {
return {
onReady:function() {
// Do initialisation things
this.counter = 0;
},
attachEvents: function(){
this.viewNode.querySelector('.update-basket').addEventListener('click', function(){
this.raiseServerEvent('updateBasket', function(totalPrice){
// totalPrice is received from the response of the server event.
});
}.bind(this));
}
};
})
Passing arguments
The View Bridge can pass any number of arguments to the server event. Like return values these can be of any type that can be json encoded.
Simply pass the arguments after the event name and before the success callback:
rhubarb.vb.create('BasketViewBridge', function() {
return {
onReady:function() {
// Do initialisation things
this.counter = 0;
},
attachEvents: function(){
this.viewNode.querySelector('.apply-voucher').addEventListener('click', function(){
var voucherCode = this.viewNode.querySelector('.voucher').value;
this.raiseServerEvent('applyVoucherCode', voucherCode, function(totalPrice){
// voucherCode is passed to the server.
});
}.bind(this));
}
};
})
Simply define the argument on the event handler in the PHP to receive it:
class Basket extends Leaf
{
protected function onModelCreated()
{
parent::onModelCreated();
$this->model->applyVoucherCodeEvent->attachHandler(function($voucherCode){
$order = Order::singleton();
$order->applyVoucher($voucherCode);
return $order->totalPrice;
});
}
}