Two-way data binding in Polymer.dart

Model Driven View (MDV) is a set of techniques to help you bind data to your views in a more direct way than we did in Chapter 6, Combining HTML5 Forms with Dart. The idea here is simple:

  • We have one or more classes (with properties) in a model
  • Our app contains one or more views (implemented as web components) to present the model's data (the data binding)

Note

For the code files of this section, refer to codechapter_8ank_terminal in the code bundle.

Data binding can be one-way (model to view) with or without observing the changes in the model: this means that the data from our model (a variable or a method that returns a value) is shown (read-only) on the page, and we do this by writing {{var}} on the web page and marking the var variable in code as @observable var. When its value changes, the altered value will be shown on the web page. In general, you can show any Dart expression with the {{ expression }} notation, but be careful that the expression doesn't cause any unwanted side effects by changing the variables. Use @published var, when var is also an attribute in a tag. Data binding can also be two-way (model to view and view to model), meaning that the data can be shown, but the input from the web page also changes the Dart variables. In other words, the data and the web page are then synchronized.

Data binding combined with event listeners allows us to create simple and sophisticated views for our model in a declarative way, thereby reducing the need to manually create controller objects that do these bindings. They can be exploited fully only as a part of the web components, that is to say, the polymer elements. This is what we will do in the following projects in this chapter. Let's apply this first to our Bank Terminal project; we now build a form that also takes an input amount that is deposited in our account, changing the balance on the screen, as shown in the following screenshot:

Two-way data binding in Polymer.dart

Two-way data binding

The start up bank_terminal.html page will show a <bank-app> web component through:

    <h1>Bank Terminal</h1>
    <bank-app></bank-app>

This web component is linked in with <link rel="import" href="bank_app.html">. Our component uses the following markup in the bank_app.html file to show the data:

<link rel="import" href="bank_account.html">
<link rel="import" href="../../packages/polymer/polymer.html">
<polymer-element name="bank-app">
<template>
   <bank-accountp bac="{{bac}}"></bank-accountp>        (1)
 </template>
 <script type="application/dart" src="bank_app.dart"></script>                                          (2)
</polymer-element>

In the template in line (1), a second polymer element named <bank-accountp> is instantiated. The bank_app.dart script publishes and initializes the BankAccount object bac in lines (3) and (4), respectively:

import 'package:polymer/polymer.dart';
import 'package:bank_terminal/bank_terminal.dart';

@CustomTag('bank-app')
class BankApp extends PolymerElement {
  @published BankAccount bac;                               (3)
  BankApp.created() : super.created()  {  }
  attached() {
    super.attached();
    var jw = new Person("John Witgenstein");
    bac = new BankAccount(jw, "456-0692322-12", 1500.0);    (4)
  }
}

We also have to override the attached method of the PolymerElement class, which is called when the element is inserted into the document. The bac object is passed to the <bank-account> web component in line (1) in the preceding code. The markup of this component is then found in the bank_account.html file:

  <polymer-element name="bank-accountp">
  <style> // left out
  <template>  
  <table class="auto-style1" on-keypress="{{enter}}>
        <tr>
         <td class="auto-style2">Number</td>
         <td> {{ bac.number }} </td>                         (1)
        </tr>
        <tr>
         <td class="auto-style2">Owner</td>
         <td> {{ bac.owner.name}} </td>                      (2)
         <td></td>
        </tr>
        <tr>
         <td class="auto-style2">Starting balance</td>
         <td> {{bac.balance}}</td>  
        </tr>
        <tr>
         <td class="auto-style2"> After transaction:</td>
         <td> {{balance}}</td>                              (3)
        </tr>
        <tr>
          <td class="auto-style2">Amount transaction</td>
          <td><input id="amount" type="text"/></td>          (4)
          <td></td>
        </tr>
        <tr>
         <td><button class="btns" on-click="{{transact}}">   (5)
             Transaction</button></td>
        </tr></table></template>
<script type="application/dart" src="bank_account.dart"></script>

As the bac object is bound to the component, its properties can be shown like bac.number in line (1) or even nested properties like in line (2). In line (4), we take in a money amount, which is bound to a variable with the same name in line (13) as in the accompanying bank_account.dart script (see the next code). A click on the button in line (5) starts the transact event handler in line (11), which changes the balance in line (12). To show balance in line (3), we need to mark it as @published in line (9). In lines (6) to (8), we see the required code to define a Polymer web component. Line (10) shows the BankAccount.created constructor, which our web component has to override from the PolymerElement class:

import 'dart:html';
import 'package:polymer/polymer.dart';                      (6)
@CustomTag('bank-accountp')                                 (7)

class BankAccountp extends PolymerElement {                  (8)
  @published var bac;
  @published double balance;                                (9)
  double amount = 0.0;
  BankAccount.created() : super.created() {  }              (10)
  attached() {
    super.attached();
    var jw = new Person("John Witgenstein");
    bac = new BankAccount(jw, "456-0692322-12", 1500.0);
    balance = bac.balance;
  }
  transact(Event e, var detail, Node target) {              (11)
    InputElement amountInput =        shadowRoot.querySelector("#amount");
    if (!checkAmount(amountInput.value)) return;
    bac.transact(amount);
    balance = bac.balance;                                  (12)
  }
  enter(KeyboardEvent  e, var detail, Node target) {
    if (e.keyCode == KeyCode.ENTER) {
      transact(e, detail, target);
    }
  }
  checkAmount(String in_amount) {
    try {
      amount = double.parse(in_amount);                    (13)
    } on FormatException catch(ex) {
      return false;
    }
    return true;
  }
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.9.147