diff --git a/bindings.md b/bindings.md index 920400268..b8d70adb9 100644 --- a/bindings.md +++ b/bindings.md @@ -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 + + + + + + ``` -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 + + +