Polymer custom element property not accessible in nested template - dart

I have a custom element like below:
<polymer-element>
<template if="{{primaryLoaded}}">
<template repeat="{{pData in primaryData}}">
<p>{{pData.propertyOne}}</p>
<p>{{someClass.someOperation()}}</p>
<template if="{{secodaryLoaded}}">
<p>{{secondaryData.someProperty}}</p>
<p>{{someClass.someOperation()}}</p>
</template>
</template>
</template>
</polymer-element>
and a corresponding dart file:
class CustomElement extends PolymerElement with ObservableMixin
{
#observable bool primaryLoaded = false;
#observable bool secondaryLoaded = false;
#observable var primaryData;
#observable var secondaryData;
#observable var someClass;
void created()
{
primaryData = toObservable(new List<var>());
secondaryData = toObservable(new List<var>());
}
void inserted()
{
someClass = new SomeClass();
loadPrimaryData().then((pData) {
primaryData = pData;
primaryLoaded = true;
loadSecondaryData().then((sData) {
secondaryData = sData;
secondaryLoaded = true;
});
});
}
}
Everything works fine for the primaryData. Its properties get printed and the call to someOperation() on someClass prints its data correctly.
The problem is in the nested template. Nothing under the <template if="{{secondaryLoaded}}"> gets displayed. Even the call to someClass.someOperation() fails to display anything.
Is there a problem with scope here? It looks like the outer template can reference the properties defined in the dart file without problem but the nested template can't.
I read about setting variables as globals in the template by setting a custom binding delegate here. However, I can't set the bindingDelegate in my custom element as isTemplate returns false.
Is there any other way of setting a global variable in a custom template? Or am I going about this all wrong?

It's a bug. You can star this issue to be notified of changes:
https://code.google.com/p/dart/issues/detail?id=12742
The only thing you can do for now is to turn nested templates into other polymer elements or figure out a way so the template isn't nested.
In your case, instead of using if="primaryLoaded" you could just set primaryData = null and the template shoud not display anything until primaryData is set, assuming primaryData is #observable.

Related

Set paper-tooltip from dart

In html paper-tooltip works fine like
<div>
Text
<paper-tooltip>The tooltip</paper-tooltip>
</div>
But howto build apaper-tooltip from dart?
DivElement divText = new DivElement();
divText.text = 'Text from dart';
PaperTooltip pT = document.createElement('paper-tooltip');
//How to set the tooltip String "The tooltip" on pT?
divText.append(pT);
I have tried to set the underlying content of paper-tool, but don't succeed. Anyone with a tip on how this maybe could be done?
Not sure what is going on here. Seems like the creation from dart with document.createElement('paper-tooltip'); don't give a proper paper-tooltip element.
Found an ok workaround by wrapping the paper-tooltip with a custom element. The custom element can be then created with document.createElement('papertooltip-wrap');
Html
<dom-module id="papertooltip-wrap">
<template>
<paper-tooltip>[[tooltipText]]</paper-tooltip>
</template>
</dom-module>
Dart
#PolymerRegister('papertooltip-wrap')
class PapertooltipWrap extends PolymerElement {
#property
String tooltipText;
PapertooltipWrap.created() : super.created(){
}
void setAnchorElement(Element e, final String tipText){
set('tooltipText', tipText);
append(e);
}
}

Best way to notify polymer of updates to list items

I have a Dart web app using Polymer 1.0. The main-app is displaying a list of items. The main-app can add items to that list and then some time later an AJAX call will return and update the state of the added item. The update methods will need to utilize the dart-polymer helper methods in order to notify polymer of the change. However, I don't see how to pass a reference to the dart-polymer helper methods for this update.
I have it working below by having the MyItem element make the AJAX call at construction and then MyItem update the item on return. This works because MyItem only has a single item. However, this is not ideal because MyItem always makes an AJAX call and cannot simply be used to display the state of item if the page is reloaded.
I'm looking for a way to notify Polymer that an item has changed state that is contained somewhere in a list (the index is unknown).
main-app.html:
<dom-module id="main-app">
<paper-button on-tap="addItem>Add</paper-element>
<template is="dom-repeat" items="items" as="item">
<my-item item="{{item}}"></my-item>
</template>
</dom-module>
main-app.dart:
#PolymerRegister('main-app')
class MainApp extends PolymerElement {
final List<Item> items = [];
#reflectable
void addItem(Event e, Object detail) {
insert('items', 0, new Item());
}
}
my-item.html:
<dom-module id="my-item">
<template>
<div>{{item.state}}</div>
</template>
</dom-module>
my-item.dart:
#PolymerRegister('my-item')
class MyItem extends PolymerElement {
#property
Item item;
MyItem.create() : super.create();
ready() {
HttpRequest.postFormData("/items").then((HttpRequest request) {
set('item.state', request.responseText);
});
}
}
item.dart:
class Item extends JsProxy {
#reflectable
String state;
Item(this.state);
}
* Edit *
As Günter pointed out I could look up the item in the list and then remove and add it again. I wrote this function in main-app to be called on update:
void fireItemUpdate(Item item) {
int i = items.indexOf(item);
removeItem('items', item);
insert('items', i, item);
}
However, when I called that polymer did not render the updated item. I had to create a new item:
void fireItemUpdate(Item item) {
int i = items.indexOf(item);
removeItem('items', item);
Item item2 = new Item(item.state);
insert('items', i, item2);
}
Of course that isn't great either because fireItemUpdate cannot be called more than once (because it removes the original run from the list).
Documentation on Polymer Bindings: https://github.com/dart-lang/polymer-dart/wiki/data-binding-syntax
You need to make items a property
#property final List<Item> items = [];

Dart Polymer: How to set a non-string field in custom at creation

I am writing a polymer app. I extend a polymer element as follows:
#CustomTag("custom-element")
class CustomElement extends PolymerElement {
#published List<String> years;
CustomElement.created() : super.created();
}
the corresponding html:
<link rel="import" href="../../../../packages/polymer/polymer.html">
<polymer-element name="custom-element" attributes="years">
<template>
Years listed are
<template repeat="{{ year in years }}">
<p> {{ year }} </p>
</template>
</template>
<script type="application/dart" src="custom_element.dart"></script>
</polymer-element>
and in a dart script referenced in the entry html file, after I have fetched some data from server I dynamically create such elements:
initPolymer().run((){
querySelect("#custom-elements").children.add(
new Element.tag("custom-element")
..id = "my_custom_element"
);
});
Given code works fine if years in CustomElement is set to some value, now I want to set it in my main script. If the property years was of type String then I would simply altered the last piece to
initPolymer().run((){
querySelect("#custom-elements").children.add(
new Element.tag("custom-element")
..id = "my_custom_element"
..setAttribute("years", "2010")
);
});
But the problem is that I need to set a whole List, not a single value. I can't figure out a way to do so. How would I go about this? Solutions and suggestions are very welcome.
EDIT:
So my main script looks as follow
void main(){
// in reality this list is fetched from the server
List<String> customYears = ['2010', '2011'];
initPolymer().run((){
querySelect("#custom-elements").children.add(
new Element.tag("custom-element")
..id = "my_custom_element"
// HERE I would like to set a public field *years*
// of the CustomElement
// the following does not work
// 1.
// ..setAttribute('years', customYears)
// Exception: Uncaught Error: type 'List' is not a subtype of type 'String' of 'value'.
// 2.
// ..setAttribute('years', toObservale(customYears))
// Exception: Uncaught Error: type 'ObservableList' is not a subtype of type 'String' of 'value'.
// 3.
// ..years = customYears
// ..years = toObservable( custom Years )
// NoSuchMethodError: method not found: 'years='
);
});
}
So the question is, how do I assign a value to a non-string member field of an instance of CustomElement outside the class itself? A straightforward assignment inside the class causes no problems, but this isn't what I seek for. I hope I explained myself more clearly now.
Basically #Ozan answers your actual question but there is an additional issue.
The element is not yet fully initialized when the code assigns the value. This is the reason Dart fails and tells you that HtmlElement doesn't have a setter years. After adding Polymer.onReady.then() Polymer is fully initialized and the assignment works.
Hint: If you cast the created element to its concrete type you don't get a warning in DartEditor.
import 'package:polymer/polymer.dart';
import 'dart:html';
// added import to be able to use the type
import 'custom_element.dart';
void main() {
// in reality this list is fetched from the server
List<String> customYears = ['2010', '2011'];
// Polymer => 0.16.0
initPolymer().then((zone) => zone.run(() {
Polymer.onReady.then((_) { // added - wait for onReady
querySelector("#custom-elements").children.add(
(new Element.tag('custom-element') as CustomElement)
..id = "my_custom_element"
..years = toObservable(customYears));
});
}));
}
it is more convenient to add a constructor to the custom element like
import 'package:polymer/polymer.dart';
import 'dart:html';
#CustomTag('custom-element')
class CustomElement extends PolymerElement {
factory CustomElement() {
final x = new Element.tag('custom-element');
return x;
}
CustomElement.created() : super.created();
#published List<String> years;
}
then you can create the element like
querySelector("#custom-elements").children.add(
new AppElement()
..id = "my_custom_element"
..years = toObservable(customYears));
see also
Instantiating polymer element via dart code
how to implement a main function in polymer apps
Try to simply assign the value to the field:
import 'package:observe/observe.dart';
initPolymer().run((){
querySelect("#custom-elements").children.add(
new Element.tag("custom-element")
..id = "my_custom_element"
..years = toObservable(["2010"])
);
});
Usually custom elements are used in a main polymer element and attributes like years are bound to its scope:
<link rel="import" href="../../../../packages/polymer/polymer.html">
<polymer-element name="main-element">
<template>
<custom-element years="{{years}}"></custom-element>
</template>
<script type="application/dart" src="main_element.dart"></script>
</polymer-element>
#CustomTag("main-element")
class MainElement extends PolymerElement {
#published List<String> years = toObservable(["2010"]);
MainElement.created() : super.created();
}

DART POLYMER: How I can update a property from a custom element by another?

I would like to update a property labels in a custom element (signin_element) by another custom element (localized_element). But the property labels have changed type from Map to String during the operation.
Explanation:
1/ Just after the application start, the custom element (signin-element) update the property labels with a Map (see the file signin.dart).
2/ After the custom element localized-element is initialized, the property labels is updated with a new Map from an external json file. The method update can't assign a new Map to the property labels, because is now a String.
Chromium return warnings:
Attributes on signin-element were data bound prior to Polymer
upgrading the element. This may result in incorrect binding types.
Attributes on localized-element were data bound prior to Polymer
upgrading the element. This may result in incorrect binding types.
And finally return an error:
Class 'String' has no instance method '[]='.
Signin.html:
<core-animated-pages selectedindex="0" notap id="core_animated_pages">
<section id="section1" layout horizontal active center-justified center>
<core-card id="core_card" layout vertical>
<paper-input label="Your email addres" floatinglabel id="email_input"></paper-input>
<paper-input label="Your password" floatinglabel id="password_input"></paper-input>
<div id="div" layout horizontal end-justified>
<paper-fab icon="check" id="paper_fab"></paper-fab>
</div>
<h2>{{labels['hello']}}</h2>
</core-card>
</section>
</core_animated_pages>
<localized-element id="l10n" locale={{locale}} labels={{labels}}></localized-element>
signin.dart
#CustomTag('signin-element')
class Signin extends PolymerElement {
#published String locale = 'en';
#observable Map labels = toObservable({
'hello': 'Hello 1'
});
Signin.created() : super.created();
}
localized.dart
#CustomTag('localized-element')
class Localized extends PolymerElement {
#published String locale = 'en';
#published Map labels;
Localized.created() : super.created();
ready() {
super.ready();
_loadTranslations();
}
update() {
if (!_l10n.containsKey(locale)) return;
var l10n = _l10n[locale];
labels['hello'] = l10n['hello'];
}
List locales = ['en', 'fr'];
_loadTranslations() {
locales.forEach((l10n)=> _loadLocale(l10n));
}
Map _l10n = {};
_loadLocale(_l) {
HttpRequest.getString('i18n/translation_${_l}.json')
.then((res) {
_l10n[_l] = JSON.decode(res);
update();
})
.catchError((Error error) {
print(error.toString());
});
}
}
I think you need to change
#published Map labels;
in localized.dart to
#PublishedProperty(reflect: true) Map labels;
the same with locale
see
Google Groups - Dart Web Development - PSA: Polymer 0.11.0 #published properties no longer reflect to attributes by default
Günter found the solution and I push the code change in case of you encounter the same issue.
First, update polymer dependencies to dev:
polymer: '>=0.12.0-dev <0.13.0'
Second, replace #published from localized.dart
#PublishedProperty(reflect: true) String locale = 'en';
#PublishedProperty(reflect: true) Map labels;
and in the file signin.dart
#PublishedProperty(reflect: true)

How to initialize an instance of the custom element declared in an entry page

How to initialize an instance of the custom element declared in an entry page?
For example:
<!-- index.html -->
<polymer-element name='my-elm' attributes="count" noscript>
<template>
<span>{{count}}</span>
</template>
</polymer-element>
<my-elm id="my-elm"></my-elm>
// index.dart
(querySelector("#my-elm") as dynamic).count = 1;
When I attempt to initialize count propery of my-elm polymer element instance I get the following execption: Breaking on exception: Class 'HtmlElement' has no instance setter'count='. What is wrong in my code?
EDIT
You can only access fields of the backing class this way.
If you don't have a class you can accesss it using
querySelector("#my-elm").attributes['count'] = '1'; // attributes can only store strings
EDIT END
You need to delay code in index.dart until Polymer is ready
import "package:polymer/polymer.dart";
main() {
initPolymer().run(() {
// code here works most of the time
Polymer.onReady.then(() {
// some things must wait until onReady callback is called
(querySelector("#my-elm") as dynamic).count = 1;
});
});
}

Resources