Using Polymer for the category links project

On top of the Category Links model that was discussed in Chapter 4, Modeling Web Applications with Model Concepts and Dartlero (the model is contained in the lib folder), we will now build a typical master-detail screen to present and change its data using web components for displaying, adding, editing, removing, and saving data, as shown in the following screenshot:

Note

Get the code with: git clone git://github.com/dzenanr/polymer_category_links.git

Using Polymer for the category links project

The category_links application

In the final spiral, there are three web components per concept of the model: table (for a list), add (to add an element to the list), and edit (to edit an element of the list); they can be found in the webcomponent folder. In spiral s00, only the Category entity is defined as ConceptEntity together with its collection Categories. The script testcategories_entities_test.dart applies unittest on this model. In spiral s01, we build a component for the Category entity: this is the first step towards the upper part of the preceding screenshot. In this spiral, we only show a table with the category data, using a <category-table> web component, as shown in the following screenshot:

Using Polymer for the category links project

Category_links spiral s01

The polymer_category_links.html page exposes a <polymer-app> web component defined in the polymer_app.html file:

  <polymer-element name="polymer-app">
  <link rel="import"            href="component/category/category_table.html">      (1)
  <template>
  <category-table categories="{{categories}}">
</category-table>                                (2)
  </template>
  <script type="application/dart"   src="polymer_app.dart"></script>
</polymer-element>

This contains the <category-table> component in line (2) that shows the data from the collection categories, which is made and filled with data in line (4) in polymer_app.dart:

import 'package:polymer_category_links/category_links.dart';   (3)
import 'package:polymer/polymer.dart';
@CustomTag('polymer-app')
class PolymerApp extends PolymerElement {
  @observable Categories categories;
  PolymerApp.created() : super.created() {
    var categoryLinksModel = new CategoryLinksModel();
    categoryLinksModel.init();                                 (4)
    categories = categoryLinksModel.categories;
  }
}

Line (3) imports the model. The categories variable in the <category-table> component gets its value from the {{ categories }} polymer expression, where the categories object in the expression is the property of the <polymer-app>.

In this way, data from the application is passed to the web component. The <category-table> component is imported through line (1), and is defined in the category_table.html file:

<polymer-element name="category-table">
  <template>
    // <style> markup omitted
    <table>
      <caption class="marker">
        Categories
      </caption>
      <tr>
        <th>Name</th>
        <th>Description</th>
      </tr>
      <tbody template repeat="{{category in            categories.toList()}}">  (5)
        <tr>
          <td>{{category.code}}</td>
          <td>{{category.description}}</td>
        </tr>
      </tbody>
    </table>
  </template>
  <script type="application/dart" src="category_table.dart"></script>
</polymer-element>

The repeat-template in line (5) iterates through the List to show the categories declared in the category_table.dart file:

import 'package:polymer_category_links/category_links.dart';
import 'package:polymer/polymer.dart';
@CustomTag('category-table')
class CategoryTable extends PolymerElement {
  @published Categories categories;
  CategoryTable.created() : super.created();
}

In spirals 02, the application is enriched with the add functionality, as shown in the following screenshot:

Using Polymer for the category links project

Category_links spiral s02

This is achieved through a <category-add> component that is embedded in the <category-table> component, and that is only shown when showAdd is true:

    <template if="{{showAdd}}">
        <category-add
          categories="{{categories}}">
        </category-add>
   </template>

In the Categories caption (in the table header), a button is added to toggle the appearance of the add component:

<button id="show-add" on-click="{{show}}">Show Add</button>

The button is monitored by the Boolean variable showAdd, marked as observable in the script:

  @observable bool showAdd = false;
show(Event e, var detail, Node target) {
    ButtonElement addCategory = shadowRoot.querySelector("#show-   add");
    if (addCategory.text == 'Show Add') {
      showAdd = true;
      addCategory.text = 'Hide Add';
    } else {
      showAdd = false;
      addCategory.text = 'Show Add';
    }
   }

Now the categories data can change, so we must mark it as observable. This is done in polymer_app.dart through:

categories.internalList = toObservable(categories.internalList);

This internalList is a property of the ConceptEntities class in the Dartlero model and inherited by the Categories class. It is now shown in category-table through the following code:

  <tbody template repeat="{{category in            categories.internalList}}">
        <tr>
          <td>{{category.code}}</td>
          <td>{{category.description}}</td>
        </tr>
   </tbody>

Our add component defined in the category_add.html component contains two input text fields and an Add button with an add event handler, that checks whether a name is given and that the category is not yet in use. The code property is inherited from ConceptEntity in Dartlero and used as a category name; the inherited add method checks that the code is unique. Here is the code from category_add.dart:

add(Event e, var detail, Node target) {
    InputElement code = shadowRoot.querySelector("#code");
    InputElement description = shadowRoot.querySelector("#description");
    Element message = shadowRoot.querySelector("#message");
    var error = false;
    message.text = '';
    if (code.value.trim() == '') {
      message.text = 'category name is mandatory;${message.text}';
      error = true;
    }
    if (!error) {
      var category = new Category();
      category.code = code.value;
      category.description = description.value;
      if (categories.add(category)) {
        message.text = 'added';
        categories.order();
      } else {
        message.text = 'category name already in use';
      }
    }
    }

Spiral s03 adds an Edit functionality analogous to Add. This is achieved by a second embedded web component <category-edit>, again shown through a conditional template:

      <template if="{{showEdit}}">
        <category-edit categories="{{categories}}" category="{{category}}">
        </category-edit>
      </template>

Here, the categories and category properties of the category table component are passed to the <category edit> component by using the {{categories}} and {{category }} expressions. We also add a new button in the table row:

     <td><button on-click="{{edit}}" category-code={{category.code}}>Edit</button></td>

This button has the following event-handler:

  edit(Event e, var detail, Element target) {
    String code = target.attributes['category-code'];    (1)
    category = categories.find(code);
    showEdit = true;
     }

Notice how we get the category code as the value of an attribute in line (1). The edit component is defined in componentcategory_edit.html. It is nearly identical to the add component , but the Name field is read-only. The following is a snippet of the HTML code:

    <input readonly="true" value="{{category.code}}"/>
    <br/>
    <input id="{{category.code}}-description"
      type="text" size="96" value="{{description}}"/>    (2)

To be able to change the description, we have to use a description variable in line (2), that is marked as @published and set to the category description in the enteredView method in category_edit.dart in line (3):

@published String description;
   CategoryEdit.created() : super.created();
   enteredView() {
     super.enteredView();
     description = category.description;                 (3)
   }

The Update button calls the corresponding method in the same script:

  update(Event e, var detail, Node target) {
    category.description = description;
    categories.order();                                  (4)
    var polymerApp = querySelector('#polymer-app'),
    var categoryTable =            polymerApp.shadowRoot.querySelector('#category-    table'),                                              (5)
    categoryTable.showEdit = false;                      (6)
  }

The sorting of categories in line (4) is also needed to show the new description. A previously instantiated web component can also be retrieved by querySelector. Line (5) uses this to toggle the appearance of the edit component in line (6).

Adding local storage

Spiral s04 adds persistency to the browser local storage; our model Category class implements the necessary toJson and fromJson methods. In the body of the <polymer-app> component, a Save button is added, coupled to a save() method in polymer_app.dart:

save(Event e, var detail, Node target) {
    window.localStorage['polymer_category_links'] =
        JSON.encode(categories.toJson());
   }

The data is read from local storage in the constructor of the <polymer-app> component in line (1) in the following code (see polymer_app.dart):

PolymerApp.created() : super.created() {
  var categoryLinksModel = new CategoryLinksModel();
  categories = categoryLinksModel.categories;
  String json = window.localStorage['polymer_category_links']; (1)
if (json == null) {
    categoryLinksModel.init();
  } else {
    categories.fromJson(JSON.decode(json));
  }
  categories.internalList = toObservable(categories.internalList);
}

If the data is not in local storage, the init() method is called, and the model is populated. Spiral s04 also adds a Remove functionality through a new button in every table row, which invokes the following method in category_table.dart:

delete(Event e, var detail, Element target) {
    String code = target.attributes['category-code'];
    category = categories.find(code);
    categories.remove(category);
}

Now how about viewing the links for each category? This is taken care of in spiral s05, first by adding the Link and Links classes to our model in libmodellink_entities.dart. Being good Dartlero citizens, they know to construct themselves fromJson method and deconstruct toJson method, so they are ready for (local storage) persistence. Test programs are also provided in testmodel. When the app is run, a Show button is added to every category row. If this is clicked, a new web component <link-table> appears with the links of the selected category shown; this was added to <category-table>. This can be seen in the Category_links spiral s01 screenshot, where the Web Link column contains real hyperlinks.

  <template if="{{showCategoryLinks}}">
      <link-table category="{{category}}"></link-table>    (2)
  </template>

Here is the code for the Show button:

  <button on-click="{{showLinks}}" category-code={{category.code}}>Show</button>

When clicked, the method showLinks from category_table.dart is executed:

  showLinks(Event e, var detail, Element target) {
    String code = target.attributes['category-code'];
    ButtonElement categoryLinks = target;
    if (!showCategoryLinks && categoryLinks.text == 'Show') {
      showCategoryLinks = true;
      category = categories.find(code);
      categoryLinks.text = 'Hide';
    } else if (showCategoryLinks && categoryLinks.text == 'Hide')      {
      showCategoryLinks = false;
      categoryLinks.text = 'Show';
    }
  }

This code toggles the appearance of the <link-table> component in line (2) in the preceding code, which also passes the category variable; the <link-table> component gets this value in its enteredView() method in link_table.dart:

class LinkTable extends PolymerElement {
  @published Category category;
  @published Links links;
  @observable bool showAdd = false;
  enteredView() {
    super. enteredView();
    links = category.links;
    links.internalList = toObservable(links.internalList);
  }

The links are shown in a repeating template in the link_table.html file:

    <tbody template repeat="{{link in links.internalList}}">
        <tr>
          <td>
            <a href="{{link.url}}">
              {{link.code}}
            </a>
          </td>
          <td>{{link.description}}</td>
        </tr>
    </tbody>

This web component <link-table> also has a Show Add button to activate a <link-add> component in a conditional template. This is shown when @observable bool showAdd becomes true and toggled in the code of the show method of the LinkTable class.

The link-add web component is very similar to category-add, so with what we discussed here, you should be able to analyze the code for yourself.

Spiral s06 introduces the edit functionality for links through a new <link-edit> component and finally in spiral s07; you can remove links from a category. Now you have now all the knowledge to understand the code in these last spirals completely. Moreover, you are now able to apply the model and build a web components app for every (1-n) relation between data, such as Departments and Employees, or Orders and Order Details. We now look at using web components in a (n-m) or many-to-many relationship.

..................Content has been hidden....................

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