I'm using Dart and Polymer 1.0.0-rc.9. I have my own element (simplified here of course):
<dom-module id="something">
<style>
</style>
<template>
<paper-slider id='myslider'></paper-slider>
<div id='mydiv'></div>
</template>
</dom-module>
On the Dart side, if I do:
var div=queryselector('#mydiv')
it returns null.
I then set:
<template is='dom-bind'>
it finds it. But that creates errors, which say I must use a simple template.
So how do I find my <div> element?
Use
var listdivs = Polymer.dom (this.root).querySelectorAll('div');
It's likely because you're calling
var div=queryselector('#mydiv')
outside that Polymer Element. I'm not sure, but I'm guessing the ID's within the 'something' element are only available from within it.
Your best course of action would be to give your element an ID wherever you declared it.
<something id="something"></something>
then access the children of the element
var elm =queryselector('#something');
var children = Polymer.dom(elm).children[1];
Related
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>
When working with Web UI I could pass data to a component like this:
<my-element" name="{{someName}}"></my-element>
How do I pass data to a Polymer element?
You can pass data to a Polymer element, but there is a little bit more detail involved. Imagine the element with a single field, name:
// In element.dart.
import 'package:polymer/polymer.dart';
#CustomTag("my-element")
class MyElement extends PolymerElement with ObservableMixin {
#observable String name;
}
And here is the accompanying html:
// In element.html.
<polymer-element name='my-element' attributes="name">
<template>
<h2>{{name}}</h2>
</template>
<script type="application/dart" src="element.dart"></script>
</polymer-element>
Note the attributes="name" part. That configures things so that the element can be passed the name attribute when it is created (you can pass in multiple attributes by separating them with a comma if your element needs it).
When creating the element, wrap it in a <template> tag that is bound to the values you want to pass to it:
// In index.html.
<template id='name1' bind>
<my-element name="{{}}"></my-element>
</template>
<template id='name2' bind>
<my-element name="{{}}"></my-element>
</template>
Each <template> gets bound to a separate value (we'll get to that in a second). When creating the element, you can get that value using {{}} syntax.
The data can be bound to the template in the following manner:
// In index.dart.
void main() {
query('#name1').model ='Bob';
query('#name2').model ='Rohan';
}
This way, the first <my-element> is created with the name 'Bob', and the second <my-element> is created with the name 'Rohan'.
I'm wondering if it's possible to assign a class to the component element itself.
Let's say I have this component:
<html>
<body>
<element name="x-preview" constructor="PreviewComponent" extends="div" class="preview">
<template>
<div class="preview">
</div>
</template>
</element>
</body>
</html>
Now I would like to be able to remove the <div class="preview"> element inside, since I already have the wrapping x-preview div. Simply setting class="preview" on the <element> doesn't work.
Is it possible to do that?
You have two options that I am aware of:
1) Assign the class wherever you use it:
<div is="x-preview" class="preview"></div>
The problem with this method is that if you change the class name, you will have to change it anywhere the component is used.
2) Use the inserted lifecycle method to add the class to the root element:
void inserted() {
getShadowRoot('x-preview').attributes['class'] = 'preview';
}
inserted() will be called whenever the component is added to the DOM. getShadowRoot() will fetch the root element of the component and then set the class to 'preview'. The advantage of this method is you only change the class in one location.
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.