In the first recipe, we will go through an example that shows the complete mechanics of working with a Polymer component and data binding, and at the same time emphasizes some important best practices to work with polymer.dart. You can find the code in the project bank_terminal
. The app creates an object of class BankAccount
(which can be found in the lib
folder) and populates it with the data that is shown. You can also provide a transaction amount and the balance of the account is updated. This is depicted in the following screenshot:
Perform the following steps to bind data with polymer.dart:
polymer
dependency in your project by adding polymer: ">=0.11.0 <0.12.0"
to the pubspec.yaml
file. Save the file in the editor and run pub get
from the command line. This has to be accompanied by an import line; in webank_app.dart
, we use import'package:polymer/polymer.dart';
but also in webank_app.html
with a <link rel="import">
tag as the first line.transformers
section, as shown in the following code:transformers: - polymer: entry_points: - web/bank_terminal.html - web/index.html
build.dart
file, with the following content:import'package:polymer/builder.dart'; main() { build(entryPoints: ['web/bank_terminal.html']); }
bank_terminal.html
must contain the <script>
and <link>
tags in the <head>
section, in the following order:<script src="packages/web_components/platform.js"></script> <script src="packages/web_components/dart_support.js"></script> <!-- import the bank-app custom element --> <link rel="import"href="bank_app.html"> <script type="application/dart">export 'package:polymer/init.dart';</script> <script src="packages/browser/dart.js"></script>
bank-app
is instantiated in the <body>
section of the same entry file:<bank-app></bank-app>
bank_app.html
as follows:<link rel="import"href="packages/polymer/polymer.html"> <polymer-element name="bank-app"> <style> .auto-style1 { width: 25%; border: 1px solid #0000FF; } .auto-style2 { width: 107px; } .btns { width: 127px; } .red { color: red; } </style> <template> <table class="auto-style1" on-keypress="{{enter}}"> <tr> <td class="auto-style2">Number</td> <td>{{bac.number}}</td> </tr> <tr> <td class="auto-style2">Owner</td> <td>{{bac.owner.name}}</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> </tr> <tr> <td class="auto-style2">Amount</td> <td><input id="amount" type="text"/></td> </tr> <tr> <td> t</td> </tr> </table> </template> <script type="application/dart" src="bank_app.dart"></script> </polymer-element>
bank_app.dart
:import'dart:html'; import'package:polymer/polymer.dart'; import'package:bank_terminal/bank_terminal.dart'; @CustomTag('bank-app') classBankAppextendsPolymerElement { @observableBankAccountbac; @observable double balance; doubleamount = 0.0; BankApp.created() : super.created() { } @override attached() { super.attached(); varjw = new Person("John Witgenstein"); bac = newBankAccount(jw, "456-0692322-12", 1500.0); balance = bac.balance; } transact(Event e, vardetail, Node target) { InputElementamountInput = shadowRoot.querySelector("#amount"); if (!checkAmount(amountInput.value)) return; bac.transact(amount); balance = bac.balance; } enter(KeyboardEvent e, vardetail, Node target) { if (e.keyCode == KeyCode.ENTER) { transact(e, detail, target); } } checkAmount(String in_amount) { try { amount = double.parse(in_amount); } onFormatExceptioncatch(ex) { returnfalse; } returntrue; } }
The actual numbers in step 1 for the polymer
dependency may vary, but because the changes between versions can still be significant, it is better to use explicit versions rather than any other version here in order to not break your app; you can then also upgrade it when ready after thorough testing. The build.dart
script in step 3 ensures that the project is built whenever a file in it is saved. The <link>
tag in step 4 imports the Polymer element bank-app
, which lives in bank_app.html
. The platform.js
and dart_support.js
files contain the so-called platform polyfills; this is the JavaScript code to make new web standards such as custom elements, shadow DOM, template elements, and HTML imports work. They are called polyfills because at a later stage the browser should provide native code for these standards. The init.dart
script starts up polymer.dart, and dart.js
checks whether there is a Dart VM available to run the code directly. If this is not the case, the app runs from the compiled JavaScript code. Step 5 uses the name of the Polymer element as an HTML tag.
Step 6 comprises the HTML code for the Polymer component. It defines its style (in a <style>
tag) and structure (within a <template>
tag) between the <polymer-element>
tags; its starting tag has the name of the component as an attribute, which is name="bank-app"
. We see how one-way data binding is achieved with the {{ … }}
syntax of the polymer
expression, as in <td>{{bac.number}}</td>
or <td>{{bac.owner.name}}</td>
. The dots between the curly braces take the place of a variable from the code, whose value is shown in that place in the web page. In the code, these variables are annotated with @published.Event
. Interaction is achieved through declarative on-event = "{{ … }}"
handlers, such as on-click="{{transact}}"
in the button. Here, the dots stand for the name of a method in the code to be executed when the event happens. After <template>
, the accompanying Dart script bank_app.dart
is referenced through a <script>
tag.
Finally, in step 7, we get to the Dart class that is backing up for our Polymer component and see the BankApp
class extend PolymerElement
; its class inherits from PolymerElement
. The class declaration must be preceded by the annotation @CustomTag('bank-app')
to associate it with the Polymer component. Variables that have to be visualized on the web page are annotated with @observable
.
Override a custom element life cycle method such as attached
to execute component-specific code. This method is executed when an element is inserted into the DOM; so it is a good place to initialize an app. It is obligatory to provide the created named constructor for your component. The constructor or any overridden method must call the superclass constructor or method first. The transact
or enter
method shows the signature for an event handler, enter(KeyboardEvent e, var detail, Node target)
. The transact
method shows how we can get the value for an input element in our Polymer component; use the shadowRoot.querySelector
method. After the format of the input amount is checked, the transact
method of the class BankAccount
in the bank_terminal
library changes the state of the bac
object.
The name of a Polymer component must contain at least one dash (-
). Notice the naming scheme; if the name of the Polymer component is pol-comp1
:
< pol-comp1>
pol_comp1.html
and pol_comp1.dart
Polcomp1
Polymer components can also be added dynamically via code, instead of being statically declared in the page's HTML. Suppose you want to add a component named pol-comp1
in a <div>
tag with an ID of add-comp1
. You can do this with the following code:
querySelector('#add-comp1').children.add(new Element.tag('pol-comp1'));
In step 3, you could call a linter instead of an ordinary build by replacing this line, as shown in the following code:
main(args) { lint(entryPoints: ['web/index.html'], options: parseOptions(args)); }
This will display more extensive syntax or usage warnings in your code.