📜 ⬆️ ⬇️

Declarative data binding in WinJS

Translation of Metro: Declarative Data Binding, Stephen Walther

The purpose of this article is to describe how declarative data binding works in the WinJS library. In particular, you will learn how to use the data-win-bind and data-win-bindsource attributes. You will also learn how to use calculated properties and converters to automatically format the value of properties when binding data.

Using data binding in WinJS, you can use the Model-View-ViewModel (MVVM) template when building Metro-style applications in JavaScript. Using the MVVM pattern you protect your code from chaos. The MVVM template provides you with a standard way of organizing JavaScript code, which makes your application more easily supported.
')

Using declarative binding


You can use the data-win-bind attribute with any HTML element on the page. The data-win-bind attribute allows you to associate an HTML attribute with a Javascript property of an object.

For example, you need to create a page about a certain product and you need to display information about this product on the page.
In this case, you can create the following HTML page to show product details:

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Application1</title> <!-- WinJS references --> <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet"> <script src="//Microsoft.WinJS.0.6/js/base.js"></script> <script src="//Microsoft.WinJS.0.6/js/ui.js"></script> <!-- Application1 references --> <link href="http://3a0ojxkezud2pogen3j4z0w18nq.wpengine.netdna-cdn.com/css/default.css" rel="stylesheet"> <script src="http://3a0ojxkezud2pogen3j4z0w18nq.wpengine.netdna-cdn.com/js/default.js"></script> </head> <body> <h1>Product Details</h1> <div class="field"> Product Name: <span data-win-bind="innerText:name"></span> </div> <div class="field"> Product Price: <span data-win-bind="innerText:price"></span> </div> <div class="field"> Product Picture: <br /> <img data-win-bind="src:photo;alt:name" /> </div> </body> </html> 

The page above contains three data-win-bind attributes - one attribute per product property. We use the data-win-bind attribute attribute to set the properties of the HTML element. The data-win-bind attribute accepts a list of <element property>: <model property> pairs separated by a semicolon:

data-win-bind = ”elementPropertyName: datasourcePropertyName; elementPropertyName: datasourcePropertyName; ... ”

On this page, the first two data-win-bind attributes are used to set the innerText property in SPAN elements. The last data-win-bind attribute is used for the src and alt attributes of the IMG element.

By the way, the use of the data-win-bind attribute is fully valid in HTML5. HTML5 standards allow you to add custom attributes to an HTML document, prefixing them with the data- prefix. this way you can add custom attributes to HTML5 with names like: data-stephen, data-funky, or data-rover-dog-is-hungry and your document will remain valid.

The product object displayed on the page above is created in the default.js file:

 (function () { "use strict"; var app = WinJS.Application; app.onactivated = function (eventObject) { if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) { var product = { name: "Tesla", price: 80000, photo: "/images/TeslaPhoto.png" }; WinJS.Binding.processAll(null, product); } }; app.start(); })(); 

In this code, the product object is created with the properties name, price and photo. The WinJS.Binding.processAll () method is called to bind the model to the view (Do not confuse the WinJS.Binding.processAll () and WinJS.UI.processAll () methods - these are completely different methods.

The first parameter of the processAll () method represents the root element for the binding. In other words, the binding happens to this element and all its descendants. If you specify null, the binding will occur to the entire body of the document (document.body).

The second parameter represents the data context. This is a property object that is displayed using data-win-bind attributes. In the code below, the product object is passed as a data context parameter. In other words, the data context is the view model .

Creating complex view models


In the previous section, we used the data-win-bind attribute to display the properties of a simple object: a single product. But also, we can use binding with more complex presentation models, including presentation models that represent a hierarchy of objects.

For example, the view model in the following default.js file represents two customer and product objects. In turn, the customer object contains the address object:

 (function () { "use strict"; var app = WinJS.Application; app.onactivated = function (eventObject) { if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) { var viewModel = { customer: { firstName: "Fred", lastName: "Flintstone", address: { street: "1 Rocky Way", city: "Bedrock", country: "USA" } }, product: { name: "Bowling Ball", price: 34.55 } }; WinJS.Binding.processAll(null, viewModel); } }; app.start(); })(); 

The next page displays user information (including address) as well as product. Notice the use of the point to access the child objects of the model, such as: customer.address.street.

 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Application1</title> <!-- WinJS references --> <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet"> <script src="//Microsoft.WinJS.0.6/js/base.js"></script> <script src="//Microsoft.WinJS.0.6/js/ui.js"></script> <!-- Application1 references --> <link href="http://3a0ojxkezud2pogen3j4z0w18nq.wpengine.netdna-cdn.com/css/default.css" rel="stylesheet"> <script src="http://3a0ojxkezud2pogen3j4z0w18nq.wpengine.netdna-cdn.com/js/default.js"></script> </head> <body> <h1>Customer Details</h1> <div class="field"> First Name: <span data-win-bind="innerText:customer.firstName"></span> </div> <div class="field"> Last Name: <span data-win-bind="innerText:customer.lastName"></span> </div> <div class="field"> Address: <address> <span data-win-bind="innerText:customer.address.street"></span> <br /> <span data-win-bind="innerText:customer.address.city"></span> <br /> <span data-win-bind="innerText:customer.address.country"></span> </address> </div> <h1>Product</h1> <div class="field"> Name: <span data-win-bind="innerText:product.name"></span> </div> <div class="field"> Price: <span data-win-bind="innerText:product.price"></span> </div> </body> </html> 

A view model can be as complex as you need it, and you can associate a model with a view using declarative bindings.

Creating calculated properties


Sometimes you may need to modify properties when they are displayed.

For example, you need to format the price of the product when it is displayed. You do not want to display the price of the product in this form “80,000”. Instead, you need to display the price by formatting it: “$ 80,000”.

You may also need to display several properties combined. Like for example, the full name consists of the fields Last Name, First Name, and Middle Name.

In such cases, it will be convenient to call methods in data binding. For example, you can create a fullName () method that will concatenate a first name and a last name. Unfortunately, WinJS does not support the following syntax:

 <span data-win-bind=”innerText:fullName()”></span> 

Instead, in such cases, you need to create a property with a getter in the view model. For example, the customer object in the default.js file contains the fullName property which concatenates the firstName and lastName properties:

 (function () { "use strict"; var app = WinJS.Application; app.onactivated = function (eventObject) { if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) { var customer = { firstName: "Fred", lastName: "Flintstone", get fullName() { return this.firstName + " " + this.lastName; } }; WinJS.Binding.processAll(null, customer); } }; app.start(); })(); 

Thus, the customer object contains the properties firstName, lastName, and fullName. Notice that the fullName property is declared with a getter function. When you use the fullName property, the values ​​of the properties firstName and lastName are condensed and returned as a single string.

The next HTML page displays the fullName property in the H1 element. You can use the fullName property in the data-win-bind attribute just like any other property.

 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Application1</title> <!-- WinJS references --> <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet"> <script src="//Microsoft.WinJS.0.6/js/base.js"></script> <script src="//Microsoft.WinJS.0.6/js/ui.js"></script> <!-- Application1 references --> <link href="http://3a0ojxkezud2pogen3j4z0w18nq.wpengine.netdna-cdn.com/css/default.css" rel="stylesheet"> <script src="http://3a0ojxkezud2pogen3j4z0w18nq.wpengine.netdna-cdn.com/js/default.js"></script> </head> <body> <h1 data-win-bind="innerText:fullName"></h1> <div class="field"> First Name: <span data-win-bind="innerText:firstName"></span> </div> <div class="field"> Last Name: <span data-win-bind="innerText:lastName"></span> </div> </body> </html> 

Creating converters


In the previous section, you learned how to convert property values ​​by creating getter properties. This approach makes sense when the formatting logic is proprietary to this presentation model.

If you need to apply the same formatting actions for different models, then it makes sense to create a converter function. The converter is a function that you can apply when you use the data-win-bind attribute.

Imagine, for example, that you need to create a generic method for displaying dates. And you want to always display dates in short format 12/25/1988. The following file, called converters.js, contains a shortDate () converter:

 (function (WinJS) { var shortDate = WinJS.Binding.converter(function (date) { return date.getMonth() + 1 + "/" + date.getDate() + "/" + date.getFullYear(); }); // Export shortDate WinJS.Namespace.define("MyApp.Converters", { shortDate: shortDate }); })(WinJS); 

In the file above, the Module template is used, this one is used everywhere in the WinJS library. To learn more about the Template Module, read my note about namespaces and modules:
http://stephenwalther.com/blog/archive/2012/02/22/windows-web-applications-namespaces-and-modules.aspx

The file contains the definition of the shortDate () function. This function converts the date object embedded in JavaScript into a short date string 12/1/1988.

The converter was created using the WinJS.Binding.converter () method for creating converters. This method takes an ordinary function and returns a converter.

Finally, the shortDate () converter is added to the MyApp.Converters namespace. You can call the shortDate () function by calling MyApp.Converters.shortDate ().

The default.js file contains the user object that we want to bind. The user object contains the properties firstName, lastName, and birthday. We will use our new new shortDate () converter when displaying the birthday property of a user object:

 (function () { "use strict"; var app = WinJS.Application; app.onactivated = function (eventObject) { if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) { var customer = { firstName: "Fred", lastName: "Flintstone", birthday: new Date("12/1/1988") }; WinJS.Binding.processAll(null, customer); } }; app.start(); })(); 

As a result, we use our converter shortDate in an HTML document. The following HTML document displays all the properties of the user object:

 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Application1</title> <!-- WinJS references --> <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet"> <script src="//Microsoft.WinJS.0.6/js/base.js"></script> <script src="//Microsoft.WinJS.0.6/js/ui.js"></script> <!-- Application1 references --> <link href="http://3a0ojxkezud2pogen3j4z0w18nq.wpengine.netdna-cdn.com/css/default.css" rel="stylesheet"> <script src="http://3a0ojxkezud2pogen3j4z0w18nq.wpengine.netdna-cdn.com/js/default.js"></script> <script type="text/javascript" src="js/converters.js"></script> </head> <body> <h1>Customer Details</h1> <div class="field"> First Name: <span data-win-bind="innerText:firstName"></span> </div> <div class="field"> Last Name: <span data-win-bind="innerText:lastName"></span> </div> <div class="field"> Birthday: <span data-win-bind="innerText:birthday MyApp.Converters.shortDate"></span> </div> </body> </html> 

Notice how the data-win-bind attribute is used to display the birthday property:

 <span data-win-bind="innerText:birthday MyApp.Converters.shortDate"></span> 

The shortDate converter is applied to the birthday property when it is bound to the innerText property of the SPAN element.

Using data-win-bindsource


Usually you define the view model (data context) that you will use on in the data-win-bind attributes, passing it as a parameter to the WinJS.Binding.processAll () method as follows:

 WinJS.Binding.processAll(null, viewModel); 

Alternatively, you can declaratively specify the model in your markup using the data-win-datasource attribute. For example, in the default.js script, we provide access to the view model. We can get the model by specifying the full path - MyWinWebApp.viewModel:

 (function () { "use strict"; var app = WinJS.Application; app.onactivated = function (eventObject) { if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) { // Create view model var viewModel = { customer: { firstName: "Fred", lastName: "Flintstone" }, product: { name: "Bowling Ball", price: 12.99 } }; // Export view model to be seen by universe WinJS.Namespace.define("MyWinWebApp", { viewModel: viewModel }); // Process data-win-bind attributes WinJS.Binding.processAll(); } }; app.start(); })(); 

In the code above, the view model with user and product fields is represented as MyWinWebApp.viewModel.

The following HTML page shows how you can use the data-win-bindsource attribute to attach to this view model:

 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Application1</title> <!-- WinJS references --> <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet"> <script src="//Microsoft.WinJS.0.6/js/base.js"></script> <script src="//Microsoft.WinJS.0.6/js/ui.js"></script> <!-- Application1 references --> <link href="http://3a0ojxkezud2pogen3j4z0w18nq.wpengine.netdna-cdn.com/css/default.css" rel="stylesheet"> <script src="http://3a0ojxkezud2pogen3j4z0w18nq.wpengine.netdna-cdn.com/js/default.js"></script> </head> <body> <h1>Customer Details</h1> <div data-win-bindsource="MyWinWebApp.viewModel.customer"> <div class="field"> First Name: <span data-win-bind="innerText:firstName"></span> </div> <div class="field"> Last Name: <span data-win-bind="innerText:lastName"></span> </div> </div> <h1>Product</h1> <div data-win-bindsource="MyWinWebApp.viewModel.product"> <div class="field"> Name: <span data-win-bind="innerText:name"></span> </div> <div class="field"> Price: <span data-win-bind="innerText:price"></span> </div> </div> </body> </html> 

The data-win-bindsource attribute is used twice on the page above: it is used in the element that contains the user's details as well as in the element that contains the details about the product.

The context of the model specified by the data-win-bindsource attribute is passed to all child elements. The data-win-bind attributes of the child elements are bound to the context specified by the data-win-bindsource attribute.

Results


This post was about data binding in the WinJS library. You learned how to use the data-win-bind attribute to bind attributes of HTML elements to the view model. We also looked at several additional data binding features. We have dealt with the use of calculated properties, by using properties with getters in the view model. We also looked at how you can create converters to format the value of a view model when binding properties. Finally, you learned how to use the data-win-bindsource attribute to declare the presentation model.

Source: https://habr.com/ru/post/226309/


All Articles