Passing data to a Polymer element - dart

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'.

Related

two-way binding of paper-radio-group / paper-radio-button value with Vaadin 10

I am using beta3 of Vaadin 10 and I have a html-file bound to a Component (#HtmlImport), which contains a <dom-repeat> inside of which I have a paper-radio-group. I want the paper-radio-group#selected-property to be bound two way to my model, so that when a user selects a different radio-button, it's value will be written back to the model. Unfortunately, for me it works only as a one way model, as the java-side setter setAOrBProperty() is never called. Can someone give me a hint what I need to do to have the new value written to the server?
<link rel="import" href="./bower_components/polymer/polymer-element.html">
<link rel="import" href="bower_components/paper-radio-button/paper-radio-button.html">
<link rel="import" href="bower_components/paper-radio-group/paper-radio-group.html">
<dom-module id=“dmmdl”>
<template>
<div>
<dom-repeat items=“[[myListOfSomething]]”>
<div>
<paper-radio-group selected="{{item.aOrBProperty}}” allow-empty-selection>
<paper-radio-button name=“a”>A</paper-radio-button>
<paper-radio-button name=“b”>B</paper-radio-button>
</paper-radio-group>
</div>
</template>
</div>
</template>
<script>
class BooksGridElement extends Polymer.Element {
static get is() {
return 'books-grid'
}
// only for testing !!
// ready() {
// super.ready();
// this.books = results;
// }
}
customElements.define(BooksGridElement.is, BooksGridElement);
</script>
</dom-module>
I suspect this is caused by a security feature of Flow. Arbitrary model value changes from the client are not accepted for security reasons. Instead, changes are only allowed for properties that are used in two-way template bindings (i.e. {{propertyName}}) or explicitly annotated with #AllowClientUpdates on the corresponding Java getter.
The logic that looks for {{propertyName}} doesn't have any specific knowledge about the inner workings of <dom-repeat>, so it cannot know that {{item.aOrBProperty}} corresponds to myListOfSomething[*].aOrBProperty.
If my assumption is correct, you could fix this case by adding #AllowClientUpdates to the getAOrBProperty() method in your model interface.

Nested Polymer Custom Components Data Binding

I cannot get data to round trip from a child component to the parent component and back to a child component.
I have made a simple nested component testbed. Here is the child:
<link rel="import" href="../packages/polymer/polymer.html">
<polymer-element name="child-comp" attributes="x y">
<template>
<input type='text' value='{{x}}'/>
<p>-x={{x}}-</p>
<p>list:
<template repeat='{{y}}'>-{{ }}-</template>
</p>
</template>
<script type='application/dart'>
import 'package:polymer/polymer.dart';
#CustomTag('child-comp')
class ChildComp extends PolymerElement {
#observable var x;
#observable var y;
ChildComp.created() : super.created();
}
</script>
</polymer-element>
It takes a text input (x) and echoes it out (the p immediately after the input). It also send x back to the parent control where it is added to a list (y) that should then be iterated through in the child.
Here is the parent component:
<link rel="import" href="../packages/polymer/polymer.html">
<link rel="import" href="../components/child_comp.html">
<polymer-element name="top-comp">
<template>
<child-comp x='{{s}}' y='{{t}}'></child-comp>
</template>
<script type='application/dart'>
import 'package:polymer/polymer.dart';
#CustomTag('top-comp')
class TopComp extends PolymerElement {
#observable String s = '1234';
#observable List<String> t = ['A', 'B', 'C'];
TopComp.created() : super.created();
sChanged(oldVal, newVal) {
t.add(newVal);
}
}
</script>
</polymer-element>
From the child to the parent works and the change is correctly added to the List. But the List does not seem to get to the child - the DOM is never updated.
I have tried every possible combination of the decorators, but perhaps the answer is in a variant of the bind= syntax..
A side note: is there authoritative updated documentation on the decorators? Searching turns up a bunch of references of varying antiquity.
If I understand your problem correctly, you need to make sure that not only is your List observable, but also its elements. You can do so like this:
#observable List<String> t = toObservable(['A', 'B', 'C']);
This way, changes to t's elements (instead of just t itself) are "announced", and the DOM can update accordingly.

Creating tab header for tabs web component using polymer-dart

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']);
});
}
}

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?
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>

Reference static consts from HTML

I'd like to use a const within a Dart class and reference its value in my HTML. For example:
Dart class:
Class MyClass
{
static const String MY_VALUE = "foo";
}
HTML:
<input value="{{MyClass.MY_VALUE}}">
Can we do this?
No, as far as I know, you cannot use a static const in your template. The template expects instance methods and getters. There is however an easy workaround: define a getter that returns the value of the const, and then use that getter in your HTML.
Here is the code for the getter:
String get myValue => MY_VALUE;
And here is the use of the getter in the HTML:
<input value="{{myValue}}">
I tried it because I'm new to polymer, and I don't think you can have static final class variables published/observable, but you can have final instance variables. published/observable.
// my.dart
class MyClass
{
#observable static final String MY_VALUE="foo";
}
Doesn't work but this does.
// my.dart
class MyClass
{
#observable final String MY_VALUE="foo";
}
... the rest of my rambling answer.
then
<!-- mytemplate.html -->
<polymer-element name="my-tag" >
<!-- lowercase and minus convention for html tag names -->
<template>
<input value={{MY_VALUE}}/>
</template>
<script type="application/dart" src="my.dart"/>
</polymer-element>
then go back to my.dart , add
import 'package:polymer/polymer.dart';
import 'dart:html'; // what for ? why is there single quotes ?
#CustomTag('my-tag')
// camel case , brackets, single quotes,
class MyClass {
#observable static final int MY_VALUE="foo";
// lowercase after #, no brackets, no single quotes
}
from my reading of custom polymer elements
And then finally
<!-- index.html -->
<html>
<head>
<link rel="import" href="mytemplate.html"/>
<script type="application/dart">
export 'package:polymer/init.dart';
</script>
<script src="packages/browser/dart.js" />
</head>
<body>
<my-tag></my-tag>
</body>
</html>
Looking back , there's a lot of referencing going on.
The custom html polymer element has to link to dart code,
so inside the
<polymer-element></polymer-element>
tags there is a
<script type="application/dart" src="my.dart"/>
The dart code has to link to the custom html element , so there is a
#CustomTag('my-tag')
before the class declaration.
There is also a need to import the polymer library, and the html library,
and a need to make class static constant observable.
Maybe the #observable tag only works on objects.
I tried it , and it only worked on objects,
and it didn't compile when I used the abbreviated script tag inside my.html,
so I had to do
<script type="application/dart" src="my.dart">
</script>

Resources