Skip to content
This repository was archived by the owner on Nov 17, 2022. It is now read-only.
Merged
Changes from all commits
Commits
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
181 changes: 124 additions & 57 deletions bindings.md
Original file line number Diff line number Diff line change
@@ -1,91 +1,158 @@
---
nav-title: "NativeScript Bindings"
title: "Bindings"
description: "NativeScript Documentation: Bindings"
nav-title: "NativeScript Data Binding"
title: "Data Binding"
description: "NativeScript Documentation: Data Binding"
position: 6
---

# Binding
Within NativeScript we refer to term Binding as "Data Binding" (according to Wikipedia this is a technique to connect two data elements together). A connection between two elements means that when a property value of the first element changes then the bound property of the second object is changed accordingly. Later in this document we will refer to the first object as "source" and the second object as "target".
In order to have a real binding (which changes a property on the target everytime when source is changed) we need a way to understand when source object is changed. NativeScript provides such base class called Observable which emits "property changed event" everytime when a property value is changed. For creating an Observable class instance we need to require "observable" module:
#Data Binding

``` JavaScript
var observableModule = require("data/observable");
var source = new observableModule.Observable();
```
##Overview

Next step is to create the target element (which should be an inheritor of the Bindable class - all UI elements inherits Bindable class in order to support Binding out-of-the-box).
Data binding is the process of connecting application user interface (UI) to a data object (business model). With a correct data binding settings and if data provides proper notifications when data changes then application UI will reflect changes accordingly (source to target). Depending on settings and requirements there is a possibility to update data from UI to data object (target to source). Where source is the business logic object, and target is an UI control like TextField.

``` JavaScript
var bindableModule = require("ui/core/bindable");
var targetObject = new bindableModule.Bindable();
```
##Basic data binding concepts

Now all we have to do is to bind source and the target (this snippet assumes that previous snippets exists in the source code):
Generally almost every UI control (since all controls are created with data binding in mind) could be bound to a data object. However there are few restrictions for data binding to work out of the box.

``` JavaScript
var bindingOptions = {
sourceProperty: "sourceProperty",
targetProperty: "targetProperty",
twoWay: true
};
* Target object should be a successor of **Bindable** class.
* Target property should be a **dependency property** in order to use data binding from target to source (or two way data binding).
* Data (business) object should raise **propertyChanged** event for every change in the value of the property.

targetObject.bind(bindingOptions, source);
```
##Direction of data flow

After that any change to the "sourceProperty" of the source object will be reflected on target object.
Part of the data binding settings is the way data (values) flows. NativeScript data binding supports following data transmissions.

``` JavaScript
source.set("sourceProperty", "ExpectedValue");
* OneWay - this is a setting that indicates that a change in the source object will update target property, but a change in target property will not be propagated back to source (data object). Any update to the target property (except binding) will stop the binding connection. - this is the **default** option.

console.log("Value of the target property on target object is: " + target.get("targetProperty");
// prints -> Value of the target property on target object is: ExpectedValue
* TwoWay - this setting indicates that both changes in source object will be reflected to the target and changes from target will update the source object. Very useful in cases when user input should be handled (for example user writes a text - text is written within a TextField control and is updated to a underlying data property of the source object).
In order to use this option binding options should set **twoWay as true**. Following examples show how to do that.

source.set("sourceProperty", "ChangedValue");
##Creating a binding

console.log("Value of the target property on target object is: " + target.get("targetProperty");
// prints -> Value of the target property on target object is: ChangedValue
```
* Creating binding in code.

The "twoWay" option within BindingOptions denotes that not only a change of the source is reflected on the target, but also any change on the target is reflected on the source.
1. In order to create a working binding first we should have a source object. Source object should raise **propertyChanged** event for every change of any property. NativeScript has a built-in class that fulfils that requirement (Observable). Following is a code snippet that creates an observable object instance.

Complete example (all snippets a copied for convinience):
``` JavaScript
var observableModule = require("data/observable");
var source = new observableModule.Observable();
```
``` TypeScript
import observableModule = require("data/observable");
var source = new observableModule.Observable();
```

``` JavaScript
var observableModule = require("data/observable");
var source = new observableModule.Observable();
2. Creating a target object. For the sake of example we will also create a target object an instance of Bindable class (all UI controls derives from it).

``` JavaScript
var textFieldModule = require("ui/text-field");
var targetTextField = new textFieldModule.TextField();
```
``` TypeScript
import textFieldModule = require("ui/text-field");
var targetTextField = new textFieldModule.TextField();
```

3. Create a data binding.

``` JavaScript
var bindingOptions = {
sourceProperty: "textSource",
targetProperty: "text",
twoWay: true
};
targetTextField.bind(bindingOptions, source);
source.set("textSource", "Text set via binding");
```
``` TypeScript
var bindingOptions = {
sourceProperty: "textSource",
targetProperty: "text",
twoWay: true
};
targetTextField.bind(bindingOptions, source);
source.set("textSource", "Text set via binding");
```
This example will update targetTextField.text property with a "Text set via binding" value, **twoWay** option ensures that every change of the targetTextField.text property (via user input) will be stored within source.text property. The new value of the text property could be get via:

``` JavaScript
source.get("textSource");
```
``` TypeScript
source.get("textSource");
```

var bindableModule = require("ui/core/bindable");
var targetObject = new bindableModule.Bindable();
* Create a data binding in xml

var bindingOptions = {
sourceProperty: "sourceProperty",
targetProperty: "targetProperty",
twoWay: true
};
1. Create a source object. "source" object from the previous case (creating binding with code) will be used for following examples.

targetObject.bind(bindingOptions, source);
2. Describe a binding within xml (using a mustache syntax).

source.set("sourceProperty", "ExpectedValue");
``` XML
<Page>
<StackPanel>
<TextField text= {{ textSource }} />
</StackPanel>
</Page>
```

console.log("Value of the target property on target object is: " + target.get("targetProperty");
// prints -> Value of the target property on target object is: ExpectedValue
> Note: For an UI elements created with an xml declaration when data binding is set **twoWay** option is **true** by default.

source.set("sourceProperty", "ChangedValue");
With an xml declaration we set only properies names both for target (text) and source (textSource). The interesting thing here is that there is no source of the binding (actually it is not set directly).

console.log("Value of the target property on target object is: " + target.get("targetProperty");
// prints -> Value of the target property on target object is: ChangedValue
##Binding source

The important part of the data binding is setting the source object. NativeScript data binding works with any object that emits a **propertyChanged** event. On the process of creating binding source can be set as second parameter of the bind(bindingOptions, source) or could be omitted. In that case for source is used a special property named **bindingContext** of the Bindable class. The special about this property is that it is inheritable across the visual tree. This means that control can use the **bindingContext** of the first **parent** element with a valid **bindingContext**. With the previous example **bindingContext** can be set either on Page instance or StackPanel instance and TextField will have a proper source for its "text" property binding.

``` JavaScript
page.bindingContext = source;
//or
stackPanel.bindingContext = source;
```
``` TypeScript
page.bindingContext = source;
//or
stackPanel.bindingContext = source;
```

* Create a data binding to an event in xml

Previous example shows how to bind any Bindable element to a specific Observable object instance. NativeScript supports one very essensial feature which unlocks most of the "MVVM" scenarios, binding with a special inherited (via VisualTree) property called bindingContext. "bindingContext" is a property of the View class (very basic class for all UI elements within NativeScript), so every UI element have it. The magic about this property is that this property is inheritable in other words an observable data model could be set as bindingContext for the page and every single nested control will have it (except in case when bindingContext (for the inner control) is set to something else). To create such binding call "Bindable.bind" method without a source.
There is an option to bind a function to execute on a specific event (MVVM command like). This option is available only through xml declaration (code behind has different way to hook for events). The different part is that the source property should have an event handler function as value.

``` JavaScript
targetObject.bind(bindingOptions);
page.bindingContext = source;
source.set("onTap", function(eventData) {
console.log("button is tapped!");
});
```
``` TypeScript
page.bindingContext = source;
source.set("onTap", function(eventData) {
console.log("button is tapped!");
});
```
This will create a binding to the nearest set (up through VisualTree) bindingContext.

Unbinding two data elements requires a call to "Bindable.unbind(targetProperty)" method:
and how xml will look like:

``` XML
<Page>
<StackPanel>
<Button text="Test Button For Binding" tap={{ onTap }}
</StackPanel>
</Page>
```

> Note: Be aware that if there is an event handler function **onTap** within the page code behind ([more info about xml declarations](./ui-with-xml.md)), and **onTap** function within the **bindingContext** object then there will be 2 event handlers hooked for that button and both will be executed on tap event.

##Stop binding

Generally there is no need to stop binding explicitly, since Binding object uses weak references which prevents any memory leaks. However there are some scenarios (business logic) where binding must be stopped. In order to stop existing data binding just call **unbind** method with target property name as argument.

``` JavaScript
target.unbind("targetProperty");
targetTextField.unbind("text");
```
``` TypeScript
targetTextField.unbind("text");
```
More information about binding can be found in [API-Ref](./ApiReference/ui/core/bindable/Bindable.md).