Databinding structured object does not show any change on my polymer element - dart

I am migrating an application to Polymer 1 from 0.5 and using Dart. I was doing okay but now I am facing some data binding issues:
I have created a polymer element that programatically inserts on its Local DOM another polymer element giving the information of a contact. The second element will get the information from the constructor and show it on the interface via data binding.
Parent dart:
#PolymerRegister('test-app')
class TestApp extends PolymerElement {
TestApp.created() : super.created();
attached() {
super.attached();
async(() {
insertTile();
});
}
void insertTile() {
String contactJSON = '{"contactId":"1", "firstName":"Lex", "surname":"Luthor"}';
Contact contact = new Contact(JSON.decode(contactJSON));
ContactTile tile = new ContactTile(contact, this);
Polymer.dom(this.root).append(tile);
}
}
Parent html:
<dom-module id="test-app">
<style>
:host {
display: block;
}
</style>
<template>
</template>
</dom-module>
Children dart:
#PolymerRegister('contact-tile')
class ContactTile extends PolymerElement {
factory ContactTile(Contact contact, Element parent) {
ContactTile tile = new Element.tag('contact-tile');
tile.contact = contact;
return tile;
}
ContactTile.created() : super.created();
#property Contact contact;
}
Children html:
<dom-module id="contact-tile">
<style>
:host {
display: block;
}
</style>
<template>
<div>{{ contact.contactId }}</div>
<div>{{ contact.firstName }}</div>
<div>{{ contact.surname }}</div>
</template>
</dom-module>
Contact class:
class Contact {
String contactId;
String firstName;
String surname;
Contact(Map map) {
contactId = map['contactId'];
firstName = map['firstName'];
surname = map['surname'];
}
}
For some reason the data binding does not show any of the attributes of the contact on the web interface after the contact is updated on the constructor. Is anybody able to help me with this? I have done this many times on Polymer 0.5 but on Polymer 1 this does not work.
Thank you very much.
==============================
SOLUTION
The problem was related to the notification not being raised on the constructor of ContactTile. I could fix this by modifying the contact using the "set" function which updates the property and notifies. There are two ways to do this:
a. #Property(notify: true)
#PolymerRegister('contact-tile')
class ContactTile extends PolymerElement {
factory ContactTile(Contact contact, Element parent) {
ContactTile tile = new Element.tag('contact-tile');
tile.contact = contact;
return tile;
}
ContactTile.created() : super.created();
#Property(notify: true)
Contact contact;
}
b. polymerElement.set("name", value);
#PolymerRegister('contact-tile')
class ContactTile extends PolymerElement {
factory ContactTile(Contact contact, Element parent) {
ContactTile tile = new Element.tag('contact-tile');
// tile.contact = contact;
tile.set("contact", contact);
return tile;
}
ContactTile.created() : super.created();
#property
Contact contact;
}
Also for accessing to the object attributes, I had to make the class to extend JsProxy and add #property to every attribute (or #reflectable for methods).
import 'package:polymer/polymer.dart';
class Contact extends JsProxy {
#property
String contactId;
#property
String firstName;
#property
String surname;
Contact(Map map) {
contactId = map['contactId'];
firstName = map['firstName'];
surname = map['surname'];
}
}

Since the Contact class isn't a full custom element, should it inherit from JsProxy and have annotations of the properties. This will make the properties accessible in html. Shown below.
class Contact extends JsProxy {
#property String contactId;
#property String firstName;
#property String surname;
Contact(Map map) {
contactId = map['contactId'];
firstName = map['firstName'];
surname = map['surname'];
}
}

Related

Polymer Dart 1.0 - Observer not working

I have a custom element <test-object> which looks like this:
#PolymerRegister('test-object')
class TestObject extends PolymerElement with TestBehavior {
TestObject.created() : super.created() {
}
}
The TestBehavior:
#reflectable
class TestModel extends JsProxy {
#Property(notify: true)
num value = 0;
PolymerElement _target;
TestModel(PolymerElement target) {
_target = target;
}
changeBy(num by) {
value += by;
_target.set('testModel.value', value);
}
}
#behavior
abstract class TestBehavior implements PolymerBase {
#Property(notify: true)
TestModel testModel;
PolymerElement _instance;
static ready(instance) {
instance._init(instance);
}
static created(instance) {
instance._instance = instance;
}
_init(PolymerElement instance) {
set('testModel', new TestModel(instance));
}
}
My main app looks like this:
<dom-module id="main-app">
<style>
:host {
display: block;
#apply(--layout-center-center);
}
</style>
<template>
<div>{{testObject.testModel.value}}</div>
<test-object id="obj"></test-object>
</div>
</template>
</dom-module>
#PolymerRegister('main-app')
class MainApp extends PolymerElement {
#Property(notify: true)
TestObject testObject = null;
MainApp.created() : super.created();
ready() {
set('testObject', testObject = $$('#obj') as TestObject);
}
#Listen('click')
clicked([_]) {
testObject.testModel.changeBy(1);
}
#Observe('testObject.testModel.*')
valueChanged([_]) {
window.console.log('Value was changed');
}
}
This is a very simple toy example. I click <main-app> which causes the value in testModel to increase (this works). However, <main-app> doesn't update the div which should display the value. Also, valueChanged is never invoked.
I want to notify testObject about the valueupdate in testModel and propagate this notification up to main-app which than should update its UI via data binding.
Why is this not working?
You can't have a property in model classes, only in components
#Property(notify: true)
num value = 0;
should just be
#reflectable
num value = 0;
The set and notifyPath methods should really be called from your element classes, not the models. This actually simplifies your code a lot, and makes everything work as expected as well. Below I have pasted the new MainApp, TestModel, and TestBehavior classes (I also made some other minor edits).
#PolymerRegister('main-app')
class MainApp extends PolymerElement {
// Defines a read-only property (implicit because of the getter).
#property
TestObject get testObject => $['obj'];
MainApp.created() : super.created();
// I added the 2nd optional argument here, to fix a reflectable error
#Listen('click')
clicked([_, __]) {
// Notify directly here, this is the primary change.
notifyPath('testObject.testModel.value', testObject.testModel.changeBy(1));
}
#Observe('testObject.testModel.*')
valueChanged([_]) {
window.console.log('Value was changed');
}
}
// Removed the `_instance` field.
class TestModel extends JsProxy {
// use #reflectable instead of #Property(notify: true)
#reflectable
num value = 0;
TestModel();
num changeBy(num by) {
value += by;
// Added a return value for convenience
return value;
}
}
#behavior
abstract class TestBehavior implements PolymerBase {
#Property(notify: true)
TestModel testModel = new TestModel();
}

How to detect that a polymer button has been clicked in the dart class

I want to be able to catch the click event on the paper button and change the "clic me" text.
my-module.html:
<dom-module id="my-module">
<template>
<paper-button>{{btnTxt}}</paper-button>
</template>
<script type="application/dart" src="my-module.dart"></script>
</dom-module>
my-module.dart:
#HtmlImport('my-module.html')
library blink.my_module;
import 'package:polymer/polymer.dart';
import 'package:web_components/web_components.dart' show HtmlImport;
#PolymerRegister('my-module')
class MyModule extends PolymerElement {
#property String btnTxt = "Clic me!";
MyModule.created() : super.created();
}
OK, to do so you have to add this to the following files:
my-module.html:
//Adding the 'on-tap' tag with the dart methode name attribute, can also use 'on-click'
<paper-button on-tap="btnClicked">{{btnTxt}}</paper-button>
my-module.dart:
#PolymerRegister('my-module')
class MyModule extends PolymerElement {
#property String btnTxt = "Clic me!";
MyModule.created() : super.created();
//this method will listen for the linked event in the html
#reflectable
void btnClicked(Event e, [_]) {
set('btnTxt', btnTxt = 'Button Clicked!');
}
}

Databinding using Polymer-Dart and Dart objects

I have a custom class called MyClass. I have a List<MyClass> testList in my custom element and want to use databinding for displaying the List's content.
MyClass:
class MyClass {
String name;
MyClass(String name) {
this.name = name;
}
}
custom_element.dart:
...
attached() {
super.attached();
var lst = new List<MyClass>();
lst.add(new MyClass('test'));
set('testList', lst);
}
...
custom_element.html:
...
<template is="dom-repeat" items="{{testList}}">
<span>{{item}}</span>
<span>{{item.name}}</span>
</template>
...
However, the output is:
<span>[object DartObject]<span>
<span><span>
<span>[object DartObject]<span>
<span><span>
Why doesn't it show the object's name? How can I access the property name? Neither item.name nor item['name'] work... It used to work on Polymer 0.5 and the corresponding Polymer-Dart version. :(
This is a bit confusing as there is no documentation out yet for the new polymer version, but all you (should) need to do is make your class extend the JsProxy class. If you are on the behaviors branch you will also need to annotate the class with #jsProxyReflectable. For instance:
// #jsProxyReflectable // only if you are on the `behaviors` branch
class MyClass extends JsProxy {
#reflectable // from >= 1.0.0-rc.2
String name;
MyClass(String name) {
this.name = name;
}
}

How can an ObservableList<T> be sorted in Dart?

I can't seem to sort an ObservableList<T> and have the sort reflected in my Polymer element. Here's a sample element definition:
class PeopleElement extends PolymerElement {
ObservableList<Person> people = toObservable(new List<Person>());
PeopleElement.created() : super.created();
}
And here is the definition of the Person class:
class Person {
String name;
Person(this.name);
int compare(Person p) {
return name.compareTo(p.name);
}
}
Now, I want to add a sort function to the PeopleElement class:
class PeopleElement extends PolymerElement {
...
ObservableList<Person> sortByName() {
people.sort((Person a, Person b) {
a.compare(b);
}
}
}
I've also tried the above function as returning void and that didn't seem to work either. Does anything jump out as being incorrect here?
Your Person class needs to implement Comparable with the method compareTo instead of compare
library people_element;
import 'dart:async';
import 'package:polymer/polymer.dart';
class Person implements Comparable{
String name;
Person(this.name);
#override
int compareTo(Person other) {
return name.compareTo(other.name);
}
}
#CustomTag('people-element')
class PeopleElement extends PolymerElement {
var people = toObservable(new List<Person>());
PeopleElement.created() : super.created() {
print('PeopleElement');
}
#override
void enteredView() {
people.add(new Person('c'));
people.add(new Person('a'));
people.add(new Person('f'));
people.add(new Person('b'));
super.enteredView();
new Timer(new Duration(seconds: 3), () => sortByName());
}
ObservableList<Person> sortByName() {
people.sort();
}
}
<polymer-element name="people-element">
<template>
<template repeat="{{p in people}}">
<p>{{p.name}}</p>
</template>
</template>
<script type="application/dart" src="people_element.dart"></script>
</polymer-element>

Observing internal model attributes with Dart Polymer

I'm setting up custom Polymer elements that need to display data from the model (Property objects that need to be passed around).
I can successfully display the values of the properties, but when I update a value inside a Property object, the value is not updated inside the element.
What am I missing? I based myself on the following examples:
https://github.com/sethladd/dart-polymer-dart-examples/tree/master/web/bind_component_field_to_model_field
https://github.com/sethladd/dart-polymer-dart-examples/tree/master/web/getter_setter_as_attribute
wb-control-text.html
<polymer-element name="wb-control-text">
<template>
<div>
<label for="{{id}}">{{label}}</label>
<input type="text" id="{{id}}" value="{{value}}" />
<button on-click="{{updateValue}}">Update me</button>
</div>
</template>
<script type="application/dart" src="wb-control-text.dart"></script>
</polymer-element>
wb-control-text.dart
import "package:polymer/polymer.dart";
import "package:rqn_workbench/wb-property.dart";
#CustomTag("wb-control-text")
class WorkbenchControlTextElement extends PolymerElement {
final TextProperty _idProperty = new TextProperty("id", value: "1");
final TextProperty _valueProperty = new TextProperty("value", value:"Bla");
final TextProperty _labelProperty = new TextProperty("label", value:"Label!");
WorkbenchControlTextElement.created() : super.created();
#published String get id => _idProperty.value;
void set id(String id) { _idProperty.value = id; }
#published String get value => _valueProperty.value;
void set value(String value) { _valueProperty.value = value; }
#published String get label => _labelProperty.value;
void set label(String label) { _labelProperty.value = label; }
void updateValue() {
value += "Bla";
print(value); // Will print the appended value
}
}
wb-property.dart
library workbench_property;
import "package:polymer/polymer.dart";
#observable
class Property {
String key;
}
#observable
class TextProperty extends Property {
String value;
TextProperty ( String key, {String value} ) {
this.key = key;
this.value = value;
}
}
The solution was to use #reflectable on the getters and setters, so it could be picked up by the PathObserver.

Resources