Skip to content

Data Binding

Durisvk edited this page Jan 2, 2017 · 2 revisions

Data Binding

There are three ways of data binding inside EOSS. One is the Element to Element data binding. The second one is the Element to Property binding. The third one is Collection Item Source Binding. To make it clear, the element you are specifying data-binding attribute to is the Target. The Source is what you specify inside data-binding attribute and it's either the other element or path to a property.

Element to Element Binding:

What you need to specify is the SourceElement, SourceAttribute and TargetAttribute. You can specify the Mode either one-way or two-way. Depends on if you need one-way binding from Source to the Target or both ways. Let's look at the example. We want to create a range input and bind it to textbox so if we change range the textbox will get updated and if we change the textbox the range will get updated. We want to bind the attribute value of our range and attribute value of our textbox.

<div class="example">
            <input type="text" class="lblRange">
            <input type="range" data-binding="SourceElement: '.lblRange', SourceAttribute: 'value', TargetAttribute: 'value'" value="50" min="0" max="100" />
        </div>

This is it. We can explicitly set the Mode to two-way to be sure we get our result like that:

<input type="range" data-binding="SourceElement: '.lblRange', SourceAttribute: 'value', TargetAttribute: 'value', Mode: 'two-way'" value="50" min="0" max="100" />

And we are done now. This is how the Element to Element binding works.

Element to Property Binding:

This is a little more advanced way of binding. What you get as the final result is Element's attribute bound to the property (of your EOSS, Model, or anything accessible from your EOSS through public property or getter). So if the Element's attribute changes the Property gets automatically updated to the value of that changed attribute. If it's two-way binding and the Property changes the Element's attribute gets updated to the value of that Property. You need to specify only two parameters: SourcePath and TargetAttribute. To make the things clear the SourcePath is the path to the property delimited with dots (.). For example we have model called model in our indexEOSS so and we want to bind to the model's property foo. So we specify the SourcePath as follows: model.foo. It's not that difficult to understand. Just let's move to the example. Let's bind our range to the property in our indexEOSS. Let's create it.

<?php
use EOSS\EOSS;
use Binding\IBindableProperty

class indexEOSS extends EOSS
{

	/**
	 * @var IBindableProperty
	 */
    public $range;

    public function load() {
    	$this->setFile("index.php");
    }

    public function bind() {
    	$this->csi->button->onclick[] = "someMethod";
    }

    public function someMethod() {
    	$this->range->set($this->range->get() + 10);
    }
}

Now we can bind to that property.

<input type="button" id="button"/>
<input type="range" data-binding="SourcePath: 'range', TargetAttribute: 'value'"/>

We have successfully bound the range value to the range property in our indexEOSS. Notice that the property implements the IBindableProperty interface BUT! just after the load phase when the binding comes in play.

What if we want to keep our range private. Then we need to create the getter and the setter for that property.

<?php

use EOSS\EOSS;
use Binding\IBindableProperty

class indexEOSS extends EOSS
{

	/**
	 *	@var IBindableProperty
	 */
    private $range = 50;

    public function load() {
    	$this->setFile("index.php");
    }

    public function bind() {}

    public function getRange() {
    	return $this->range->get();
    }

    public function setRange($range) {
    	$this->range->set($range);
    }
}

Notice how we can set the initial value to the property. It will get reflected on the range input element's initial value. Be careful and before the bind() phase set the property value normally by assignment. After and inside the bind() phase set the property value with $this->property->set($value) -- set method.

If we want to bind a property of our model which is contained by the indexEOSS we can do that.

Here's the first way we can do it:

<?php

use EOSS\EOSS;

class indexEOSS extends EOSS
{

	/**
	 * @var ExampleModel
	 */
    public $model;

    public function load() {
    	$this->model = new ExampleModel();
    	$this->setFile("index.php");
    }

    public function bind() {}
}

Or we can do it this way to keep our model private:

<?php

use EOSS\EOSS;

class indexEOSS extends EOSS
{

	/**
	 * @var ExampleModel
	 */
    private $model;

    public function load() {
    	$this->model = new ExampleModel();
    	$this->setFile("index.php");
    }

    public function bind() {}

    public function getModel() {
    	return $this->model;
    }
}

And our ExampleModel will look like this:

<?php

class ExampleModel
{

	/**
	 * @var IBindableProperty
	 */
    private $range;

    public function getRange()
    {
        return $this->range->get();
    }

    public function setRange($range)
    {
        $this->range->set($range);
    }

}

We can now bind to our model instead.

<input type="text" data-binding="SourcePath: 'model.range', TargetAttribute: 'value'">

And this is it. The Element to Property data binding.

If we now want to change the bounded attribute of an element we use IBindedAttribute set method. Let's say we want to change the value of a range input with id range which is bounded to some property, we will do this like that:

	public function buttonClicked($sender) {
		$this->csi->range->value->set(69);
	}

We need to use method set to keep the binding working.

If you want to bind two or more elements to the one and the same property just assign them an id attribute.

Collection Item Source Binding

EOSS is able to bind to the collection. It's again more advanced topic, but I hope it's not too difficult to understand.

We'll create a list of persons with name, age and id. We will start with the indexEOSS

app/controller/indexEOSS.php:

<?php
use EOSS\EOSS;
use Binding\IBindableCollection

class indexEOSS extends EOSS
{

	/**
	 *	@var array|IBindableCollection
	 */
	public $collection = [["id" => 0, "name" => "Andrew Perkins", "age" => 25],
                            ["id" => 1, "name" => "John Doe", "age" => 43],
                            ["id" => 2, "name" => "Some Person", "age" => 32]];

    // Here we deal with collection as an array.

	public function load() {
		// Here we deal with collection as an array.
		$this->csi->setFile("index.latte.php");
	}

	public function bind() {
		// Here we start to deal with IBindableCollection
		// instead of array.
		$this->csi->removePerson->onclick[] = "removeThePerson";
	}

	public function removeThePerson($sender) {
		// Here we deal with IBindableCollection
		$this->collection->removeWhere("id", $sender->data_id);
	}

}

Notice where we deal with collection as IBindableCollection and where we deal with it as an array. Now we can move on and create the view.

app/view/index.latte.php:

	<ul data-binding="ItemSourcePath: 'collection'">
        <li>Person <b data-key="name"></b> is  <span data-key="age"></span> years old. <a href="" data-id="(*id*)" data-group="deletePerson">X</a></li>
    </ul>

Notice you can use data-key attribute to get the value from the data of collection OR you can use (*attribute*) notation to get the value of data from collection.

If you want to bind two or more elements to the one and the same collection just assign them an id attribute.

Now if we want to include some type of logic inside the template we could use an external template file like this:

app/view/indexView.latte.php

<ul data-binding="ItemSourcePath: 'collection', ItemTemplateFilePath: 'itemTemplate.blade.php'">
</ul>

ItemTemplateFilePath takes the path relative to the layout_dir your views directory. And now the itemTemplate.blade.php looks like this:

app/view/itemTemplate.blade.php

<li>Person <b>{{ $name }}</b> is  <span>{{ $age }}</span> years old. @if ($age > 18) can Drive @endif <a href="" data-id="{{ $id }}" data-event="Event: 'onclick', Action: 'deleteThePerson'">X</a></li>

This is much more usable way of items templating. You should use any of the available templating engines for templating the items in separate file.

And this is the Collection Item Source Binding. We now get our list of persons on the page.

Troubleshooting:

Working with the property and collection binding you will get the understoodable errors. Either the specified SourcePath is not correct (the property for example doesn't exist) or any of the parts of the specified SourcePath aren't accessible (are for example private and no getter/setter are defined).

Notice that the getter and the setter to the property must be in the format: getPropertyName() / setPropertyName($value). Getter should not take an argument and should return any value, the setter should take argument. The getters and the setters need to be camelCase.

We are looking for contributors, come and join us, write me an email on durisvk2@gmail.com or post something on a http://eoss.wz.sk .

Clone this wiki locally