Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rendering a content tag as part of a template in polymer and dart

I wish to make a generic list using polymer and dart. I am extending the UL element to do so. I want to place template variables within the content of this custom element.

<ul is="data-ul">
  <li>{{item['first_name']}}</li>
</ul>

The custom element

<polymer-element name="data-ul" extends="ul">
  <template repeat="{{item in items}}">
      <content></content>
  </template>
  <script type="application/dart" src="data-ul.dart"></script>
</polymer-element>

I was expecting the template variable to be interpolated however it simply gets outputted to the DOM as is. How do I output the content tag to be rendered as a template and not just directly outputted?

like image 913
Stewart Avatar asked Dec 22 '13 04:12

Stewart


2 Answers

Unfortunately, there are two issues here.

  1. <content> cannot be used like this. It's a placeholder for rendering light DOM nodes at specific locations in the Shadow DOM. The first <content> that selects nodes, wins [1]. Stamping out a bunch like you're doing, while very intuitive, won't work as expected.

  2. You're mixing the internal world of Polymer with the external world outside the element. What this really means is that bindings (e.g. {{}}) only work in the context of <polymer-element>.

One thing you can do is create a copy of the distributed light DOM children as the items property of your element. In JavaScript this looks like:

<template repeat="{{item in items}}">
  <li>{{item['first_name']}}</li>
</template>
<content id="content" select="li"></content>
<script>
  Polymer('data-ul', {
    ready: function() {
      this.items = this.$.content.getDistributedNodes();
    }
  });
</script>

Note: The only reason I've used <content select="li"> is to insure the element only takes in <li> nodes. If you're not worried about users using other types of elements, just use this.items = [].slice.call(this.children);.

like image 173
ebidel Avatar answered Oct 09 '22 06:10

ebidel


To do that you should override the parseDeclaration method. This method is in charge of parsing/creating the needed html that will be bound. For example, let say that you have next template

<polymer-element name="data-ul" extends="ul" attributes="items">
  <template>
    <template repeat="{{item in items}}" ref="itemTemplate"></template> <!-- this is the replacement of content tag -->
  </template>
  <script type="application/dart" src="data-ul.dart"></script>
</polymer-element>

Or if you want to have some default elements:

<polymer-element name="data-ul" extends="ul" attributes="items">
  <template>
    <template repeat="{{item in items}}">
      <!-- Def elements -->
      <template bind="{{item}}" ref="itemTemplate"></template> <!-- this is the replacement of content tag -->  
      <!-- Def elements -->
    </template>
  </template>
  <script type="application/dart" src="data-ul.dart"></script>
</polymer-element>

then you should have next class:

@CustomTag('data-ul')
class DataUl extends LiElement with Polymer, Observable {
  DataUl.created() : super.created();

  @published List items;

  void parseDeclaration(Element elementElement) {
    // We need to remove previous template from element.templateContent
    // in that way it no continues adding a new content every time that we instantiate 
    // this component.
    var previousTemplate = element.templateContent.querySelector('template#item');
    if(previousTemplate != null)
      previousTemplate.remove();

    var t = this.querySelector('#itemTemplate'); // Gets the template with id itemTemplate from the content html
    if(t != null)  // if not null
      element.templateContent.append(t); // append itemTemplate to element.templateContent
    else 
      element.templateContent.append(new TemplateElement()..id='itemTemplate'); //if no template is added append an empty template to avoid errors


    super.parseDeclaration(elementElement); // call super
  }
}

And finally use the custom element as follow:

<ul is="data-ul" items="{{[{'first_name': 'jay'}, {'first_name': 'joy'}]}}">
  <template id="itemTemplate">
    <li>{{item['first_name']}}</li>
  </template>
</ul>
like image 1
Luis Vargas Avatar answered Oct 09 '22 07:10

Luis Vargas