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 data maintenance screen to present and change link data using the web components for displaying, adding, editing, removing, and saving data, as shown in the following screenshot:

Using Polymer for the category links project

The category_links application

Note

You can get the code with the following command:

git clone git://github.com/dzenanr/polymer_category_links.git

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), edit (to edit an element of the list); they can be found in the webcomponent folder. In the Spiral s01 section, only the Category entity is defined as ConceptEntity together with its categories collection. The testcategories_entities_test.dart script applies unittest on this model. In spiral s01, we build a component for the Category entity: this is the first step toward 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:

  <link rel="import" href="view/component/category/category_table.html">      (1)
<polymer-element name="polymer-app">
  <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 categories collection, 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 the <tbody> tag in line (5) iterates through 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 spiral s02, the application is enriched with the add functionality, as shown in the following screenshot. Now, we can add new categories.

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. It 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 showAdd Boolean variable 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 is 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 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 <category-edit> web component, 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 will 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 will have to use a description variable in line (2) that is marked as @published and set to the category description in the attached method in category_edit.dart in line (3):

@published String description;
   CategoryEdit.created() : super.created();
   attached() {
     super.attached();
     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, which is 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 then read from the local storage in the constructor of the <polymer-app> component in line (1) of 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 the 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 will invoke 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 the fromJson method and deconstruct the 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 on, a new <link-table> web component will appear 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 on, the showLinks method from category_table.dart will be 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 attached() method in link_table.dart:

class LinkTable extends PolymerElement {
  @published Category category;
  @published Links links;
  @observable bool showAdd = false;
  attached() {
    super. attached();
    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.toString()}}">
              {{link.code}}
            </a>
          </td>
          <td>{{link.description}}</td>
        </tr>
    </tbody>

This <link-table> web component 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 all the knowledge to completely understand the code in these last spirals. Moreover, you are 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.14.252.56