I'm trying to create custom-tabs web-component using polymer-dart. The component itself is a tab container, which can have a custom-tab elements inside of it.
I want to have an html like this:
<custom-tabs selected="three">
<custom-tab name="one">... content skipped ...</custom-tab>
<custom-tab name="two">... content skipped ...</custom-tab>
<custom-tab name="three">... content skipped ...</custom-tab>
</custom-tabs>
In custom-tabs html file I want to have something like this:
<polymer-element name="custom-tabs">
<template>
<div class="tabs">
<content select="custom-tab"></content>
</div>
<nav>
For each of custom-tab I want to create tab header (link) here
</nav>
</template>
</polymer-element>
Is it possible to:
For each custom tab inserted into .tabs create link inside div?
If custom-tab element has a property named 'caption', can I get it using some kind of {{attribute-name}} syntax?
Finally I want to look the component like this:
P.S. I only need help on polymer-dart <template> syntax, I can deal with css myself. Thanks in advance!
<link rel="import" href="../../packages/polymer/polymer.html">
<polymer-element name="custom-tabs">
<template>
<style>
:host {
display: block;
}
</style>
<nav>
<template repeat="{{tab in tabHeaders}}">
<div>{{tab}}</div>
</template>
</nav>
<div class="tabs">
<content id="content" select="custom-tab"></content>
</div>
</template>
<script type="application/dart" src="custom_tabs.dart"></script>
</polymer-element>
import 'package:polymer/polymer.dart';
import 'dart:html' as dom;
#CustomTag('custom-tabs')
class CustomTabs extends PolymerElement {
CustomTabs.created() : super.created() {}
#observable
// toObservable() is to make Polymer update the headers (using template repeat) when the tabs list changes later on
List<String> tabHeaders = toObservable([]);
attached() {
super.attached();
// initialize the header list when the custom-tabs element is attached to the dom
updateTabHeaders();
}
// needs to be called every time the list of custom-tab children changes
void updateTabHeaders() {
tabHeaders.clear();
// the content element needs to have the id 'content'
($['content'] as dom.ContentElement).getDistributedNodes().forEach((e) {
// you can skip elements here for example based on attributes like 'hidden'
tabHeaders.add((e as dom.Element).attributes['name']);
});
}
}
Related
I downloaded DartEditor
I created a new web components project with the click-counter example
I have made some modifications
Added custom component, list-select:
import 'package:polymer/polymer.dart';
#CustomTag('list-select')
class ListSelect extends PolymerElement {
List<String> intls = const ["enUS", "nlNL"];
ListSelect.created() : super.created();
}
with html
<polymer-element name="list-select">
<template>
<style>
</style>
<div>
<select>
<option template iterate="item in intls">{{item}}</option>
</select>
</div>
</template>
<script type="application/dart" src="list-select.dart"></script>
</polymer-element>
in {{myappname}}.html, I added two lines:
<link rel="import" href="list-select.html">
and
<list-select></list-select>
However, my select stays empty. What do I forget?
There is no iterate in Polymer and the {{}} are missing.
<option template iterate="item in intls">{{item}}</option>
should be
<template repeat="{{item in intls}}"
<option >{{item}}</option>
</template>
There are a few elements (for example <tr>) which don't allow other elements like <template> inside them on some browsers.
A supported workaround is to add the template repeat attributes.
<tr template repeat="{{item in intls}}"><td>{{item}}</td></tr>
But normally you use a <template> tag for repeat and if.
Let's say that you have one web component stored inside of another in Dart. Is there any way to access state from other components?
Let's say that I have a Polymer element called x-notebook-element and one called x-note-element embedded inside of it (via an iterator):
<polymer-element name="x-notebook-element">
<template>
<ul>
<template repeat="{{note in notes}}">
<li is="x-note-element" note="{{note}}"></li>
</template>
</ul>
</template>
<script type="application/dart" src="notebook.dart"></script>
</polymer-element>
The difficulty is that I want to be able to click on a Delete button within an x-note-element that removes the x-note-element from the DOM and removes the note associated with it from the notes list.
You can fire a CustomEvent in your x-note-element :
dispatchEvent(new CustomEvent('my-delete-event', detail: note.id));
And listen to those events :
<li is="x-note-element" note="{{note}}" on-my-delete-event="{{delete}}"></li>
Then in the dart part of x-notebook-element :
delete(CustomEvent e, var detail, Element target) {
final deletedId = e.detail;
// update notes
}
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?
Unfortunately, there are two issues here.
<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.
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);.
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>
I'm working on my first Dart app, having completed the Game of Darts tutorials. I am trying to create a semantically named top-menu element that will eventually display a list of navigation menu tabs at the top of my page. My Dart app is able to recognize my custom element and calls the associated constructor.
However, I am getting a null reference when trying to query for the UL element within my custom element. I need the UL reference in order to dynamically load my LI elements into the menu.
Question 1:
Should the element be visible in the DOM at the point where the constructor is running?
Question 2:
If it is not yet visible, is there a Dart event I can use to trigger loading of the LI elements after the custom element has been completely loaded into the DOM?
Thanks in advance! For reference, here is the source of my custom element:
topmenu-element.html
<!DOCTYPE html>
<html>
<body>
<element name="top-menu" constructor="TopMenu" extends="div">
<template>
<div>
Top Menu
<ul id="top-menu-list"></ul>
</div>
</template>
<script type="application/dart" src="topmenu-element.dart"></script>
</element>
</body>
</html>
topmenu-element.dart
import 'package:web_ui/web_ui.dart';
import 'dart:html';
class TopMenu extends WebComponent {
List menuItems = ['Session', 'Authentication Services', 'Vehicle Services', 'Subscriber Services', 'Data Services'];
void populateMenu() {
UListElement menuList = query('#top-menu-list');
LIElement newMenuItem = new LIElement();
newMenuItem.text = menuItems[0];
menuList.children.add(newMenuItem);
}
TopMenu() {
// populateMenu();
}
}
I can't speak specifically about the DOM visibility in a constructor with the query method as I'm truthfully not certain. However there are perhaps better methods which you can use, which are called at various stages in the elements lifecycle.
That said, can I ask why you need to use this particular method to add the children. It is probably much easier to do it with the template repeat like so:
<!DOCTYPE html>
<html>
<body>
<element name="top-menu" constructor="TopMenu" extends="div">
<template>
<div>
Top Menu
<ul id="top-menu-list">
<li template repeat="item in menuItems">{{item}}</li>
</ul>
</div>
</template>
<script type="application/dart" src="topmenu-element.dart"></script>
</element>
</body>
</html>
Then there's no need to put any of your menu display code in your constructor.
I have the following code
xviewcontainer.html
<!DOCTYPE html>
<html>
<head>
<title>xviewcontainer</title>
<link rel="components" href="xsearch.html">
<link rel="components" href="xcard.html">
</head>
<body>
<element name="x-view-container" constructor="ViewContainerComponent" extends="div">
<template>
<template instantiate="if view == 'SEARCH_VIEW'">
<x-search></x-search>
</template>
<template instantiate="if view == 'CARD_VIEW'">
<x-card></x-card>
</template>
</template>
</element>
<script type="application/dart" src="xviewcontainer.dart"></script>
<!-- for this next line to work, your pubspec.yaml file must have a dependency on 'browser' -->
<script src="packages/browser/dart.js"></script>
</body>
</html>
xviewcontainer.dart
import 'package:web_ui/web_ui.dart';
class ViewContainerComponent extends WebComponent {
String view = 'SEARCH_VIEW';
}
I have the event handling code within some other currently rendered sub-component of x-search. How do I get a reference to the containing x-view-container instance? I wish to change the .view property so that x-view-container will render x-card instead of the currently rendered x-search. I would be specifically interested in how to do so from my event handlers relative position, how to do it in a absolute fashion, as well as how to do so in any other manner.
void openCardView(){
WHAT_DO_I_PUT_HERE.view = 'CARD_VIEW';
}
You can query for the element you have on the DOM with query() method. Simplest example is query('x-view-container'). Or assign a class or an id on it and query against that. Then access the xtag property to get the actual web component instance.
Here's an example:
import 'package:web_ui/watcher.dart' as watchers;
main() {
// I'm assuming that the HTML tag is somewhere on the page.
query('x-view-container').xtag.view = 'CARD_VIEW';
watchers.dispatch(); // You may need to call this, or use #observable stuff.
}