Creating the polymer_links project

Get the code with: git clone git://github.com/dzenanr/polymer_links.git. We start our discussion with the first spiral in this project: polymer_linkspolymer_links_s01.

Spiral s01

In this spiral, we just show a web component that contains a list of links of type String, as shown in the following screenshot:

Spiral s01

Spiral s01 of polymer_links

The startup HTML page polymer_links.html imports the web component with:

     <link rel="import" href="links.html">

The component with name web-links is instantiated in the <body> tag of the page:

    <h1>Web Links</h1>
    <web-links></web-links>

The links.html file contains the UI definition of the component:

<polymer-element name="web-links">
  <template>                                                  (1)
    <ul>
      <template repeat="{{webLink in webLinks}}">             (2)
        <li>
          <a href="{{webLink}}">                              (3)
            {{webLink}}
          </a>
        </li>
      </template>
    </ul>
  </template>
  <script type="application/dart" src="links.dart"></script>  (4)
</polymer-element>

The outer template in line (1) is required. The template in line (2) uses a repeat statement to iterate over the list webLinks: repeat="{{webLink in webLinks}}. The variable webLink takes on the value of each list item in succession, and is shown through {{webLink }} on the next line. The list is constructed in the links.dart script referenced in line (4):

import 'package:polymer/polymer.dart';
@CustomTag('web-links')
class WebLinks extends PolymerElement {
  List webLinks =
    ['http://ondart.me/',
     'https://www.dartlang.org/polymer-dart/'];
  WebLinks.created() : super.created();
}

Spiral s02

Now, we base the web component on a model with one simple concept: a web link. This has two attributes: a name and a url. The class Link is defined in liblinks.dart:

library links;
class Link {
  String name;
  Uri url;
  Link(this.name, String link) {
    url = Uri.parse(link);
  }
}

It is a best practice to put our model in its own library links in a separate lib folder. The Polymer framework is based on the statement: everything is a component. This implies that it is recommended to create a component for the HTML page that starts the application and this is what we do here. We now introduce a web component <my-app></my-app> in polymer_links.html that encapsulates the user interface and is also imported through a <link> tag: <link rel="import" href="my_app.html">. We show all our links as a <web-links> component in my_app.html, so component <web-links> is embedded in <my-app>:

<polymer-element name="my-app">
  <link rel="import" href="web_links.html">
  <template>                
    <web-links weblinks="{{links}}"></web-links>     (1)
  </template>
  <script type="application/dart" src="my_app.dart"></script>
</polymer-element>

This component in its turn is defined in web_links.html and web_links.dart.

Note

Name the definition files of the web component like the component, replacing - with _ . This keeps a more complex project with different components structured.

In the my_app.dart file, we find the code for the <my_app> component that constructs a links collection named links:

import 'package:polymer_links/links.dart';
import 'package:polymer/polymer.dart';
@CustomTag('my-app')
class MyApp extends PolymerElement {
  var links = new List<Link>();
  MyApp.created() : super.created() {
    var link1 = new Link('On Dart', 'http://ondart.me/'),
    var link2 = new Link('Polymer.dart'        'https://www.dartlang.org/polymer-dart/'),
   var link3 = new Link('Books To Read', 'http://www.goodreads.com/'),
    links..add(link1)..add(link2)..add(link3);            (2)
  }
}

Our component <web-links> is now instantiated through the template in line (1) in the preceding code; it needs a links variable, which is made in the MyApp constructor in line (2).

The web component in web_links.html also uses the repeating template introduced in spiral s01, but now shows the names of the links:

     <template repeat="{{weblink in weblinks}}">
        <li>
          <a href="{{weblink.url}}">
            {{weblink.name}}
          </a>
        </li>
     </template>

The web_links.dart file now also imports our model from the links library and annotates the weblinks variable with @published in line (3) to show its contents:

import 'package:polymer_links/links.dart';
import 'package:polymer/polymer.dart';
@CustomTag('web-links')
class WebLinks extends PolymerElement {
  @published List<Link> weblinks;           (3)
  WebLinks.created() : super.created();
}

When you run the preceding code, it looks like the following screenshot:

Spiral s02

Spiral s02 of polymer_links

Apart from some added style in spiral s03, the <web-links> component is identical to that in spiral s02.

Spiral s04

In this spiral, we also provide the possibility to add a web link by the user:

Spiral s04

Adding a web link

A new link has to be shown; in order to accomplish this, we have to mark the list with toObservable in my_app.dart file:

  var links = toObservable(new List<Link>());

The definition of the web links component in the web_links.html file now contains additional UI markup in its <template> tag to enable adding links:

     <div>
        <label for="name">Name</label>
        <input id="name" type="text"/>
        <label for="url">web Link</label>
        <input id="url" type="text"/><br/>
        <button on-click="{{add}}" class="button">Add</button> (1)
        <label id="message"></label>
     </div>
    <ul> <!-- repeating template --> </ul></template>
    <script type="application/dart" src="web_links.dart"></script>

The add behavior from line (1) is found in the script web_links.dart:

add(Event e, var detail, Node target) {                       (2)
    InputElement name = shadowRoot.querySelector("#name");    (3)
    InputElement url = shadowRoot.querySelector ("#url");
    LabelElement message = shadowRoot.querySelector ("#message");
    var error = false;
    message.text = '';
    if (name.value.trim() == '') {
      message.text = 'name is mandatory; ${message.text}';
      error = true;
    }
    if (url.value.trim() == '') {
      message.text = 'web link is mandatory; ${message.text}';
      error = true;
    }
    if (!error) {
      var weblink = new Link(name.value, url.value);
      weblinks.add(weblink);             (4)
    }
  }

Notice how in line (1) the add event handler is called in {{ }}, and in line (2) we can see that it has three arguments: the third one is a direct reference to the target element on which the event happened. In lines (3) and the following, we see how to get a reference to the inner markup of a web component. The familiar querySelector method call is now preceded by shadowRoot:

    shadowRoot.querySelector("#name");

Spiral s05

In spiral s05, we add the functionality to store our web links in local storage by adding the code needed to load and save data to the model. The save functionality is implemented in the web_links.dart script of the <web-links> component. In the preceding code after line (4), we now add:

   if (!error) {
  // previous code
   save();
}
and this save-method:
save() {
  window.localStorage['polymer_links'] =        JSON.encode(Model.one.toJson());
}

We want to save our data in JSON format. To this end, our model class Link needs to know how to transform itself in a Map (with a toJson method) or to construct itself from a Map (using the Link.fromJson constructor).

Map<String, Object> toJson() {
    var linkMap = new Map<String, Object>();
    linkMap['name'] = name;
    linkMap['url'] = url.toString();
    return linkMap;
}
Link.fromJson(Map<String, Object> linkMap) {
    name = linkMap['name'];
    url = Uri.parse(linkMap['url']);
}

Instead of always using List<Link>, let's introduce a Model class that envelops such a List using a singleton design pattern in line (1) (see liblinks.dart):

class Model {
  var links = new List<Link>();
// singleton design pattern:           // http://en.wikipedia.org/wiki/Singleton_pattern
  static Model model;
  Model.private();
  static Model get one {                                      (1)
    if (model == null) {
      model = new Model.private();
    }
    return model;
  }
  init() {
    var link1 = new Link('On Dart', 'http://ondart.me/'),
    var link2 = new Link('Web UI',         'http://www.dartlang.org/articles/web-ui/'),
    var link3 = new Link('Books To Read',  'http://www.goodreads.com/'),
    Model.one.links
    ..add(link1);
          ..add(link2);
          ..add(link3);
  }
  List<Map<String, Object>> toJson() {
    var linkList = new List<Map<String, Object>>();
    for (Link link in links) {
      linkList.add(link.toJson());                            (2)
    }
    return linkList;
  }
  fromJson(List<Map<String, Object>> linkList) {
    if (!links.isEmpty) {
      throw new Exception('links are not empty'),
    }
    for (Map<String, Object> linkMap in linkList) {
      Link link = new Link.fromJson(linkMap);                 (3)
      links.add(link);
    }
  }
}

The Model class also has toJson and fromJson methods, applying the corresponding methods for Link class while iterating over its internal list of Link objects, see lines (2) and (3) in the preceding code. The constructor MyApp() method in my_app.dart first creates a list in line (4), and then calls the load() method in line (5) to read the data from local storage:

MyApp.created() : super.created() {
  toObservable(Model.one.links);                     (4)
  load();                                            (5)
}
load() {
  String json = window.localStorage['polymer_links'];
  if (json == null) {
    Model.one.init();                                (6)
  } else {
    Model.one.fromJson(JSON.decode(json));           (7)
  }
}

If nothing was stored yet, the Model object is initialized via the init() method in line (6), else it is parsed from local storage in line (7). In line (4), something special happens: We apply the singleton pattern to make sure that we only have one object of Model class ever. The getter one in line (1) in the Model class only constructs an object when model is null, and this object is always returned. Because there is only one model object, we can safely refer to the unique links object of the model to feed links within the web component's Dart code.

Spiral s06

Here the possibility is added to remove links, as shown in the following screenshot:

Spiral s06

Removing a web link

A second button is placed inside the <template> tag of the <web-links> component:

<button on-click="{{delete}}" class="button">Remove</button>

The delete method is implemented in the script of the web_links.dart component:

delete(Event e, var detail, Node target) {
    InputElement name = shadowRoot.querySelector("#name");
    InputElement url = shadowRoot.querySelector ("#url");
    LabelElement message = shadowRoot.querySelector("#message");
    message.text = '';
    Link link = links.find(name.value);
    if (link == null) {
      message.text = 'web link with this name does not exist';
    } else {
      url.value = link.url.toString();
      if (links.remove(link)) save();           (8)
    }
  }

It calls in line (8) a remove method in the updated Links class in liblinks.dart. The Model class now encapsulates:

    var links = new Links();

Instead of:

    var links = new List<Link>();

The Links class now contains:

 var _list = new List<Link>();

The remove method goes like this:

bool remove(Link link) {
    return _list.remove(link);
}

It also has a getter for the private List _list:

      List<Link> get internalList => _list;

This is now called in MyApp.created() as follows:

  toObservable(Model.one.links.internalList);
..................Content has been hidden....................

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