How can an ObservableList<T> be sorted in Dart? - 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>

Related

How do I do custom validation in Polymer Dart

Similar to javascript and the implementation of IronValidatorBehavior how do I do custom validation in Polymer Dart?
I can create a new custom-validator which implements IronValidatorBehavior e.g.
<dom-module id="form-input">
<template>
<style include='shared-styles iron-flex iron-flex-alignment'>
:host {
display: block;
padding: 1em;
}
</style>
<custom-validator id="validator" validator-name="jsonValidator" validator-type="json"></custom-validator>
<paper-textarea label="[[label]]" id="body" name="body" autofocus tabindex="0"
validator="jsonValidator" auto-validate error-message="This is not a valid format"></paper-textarea>
</template>
</dom-module>
The custom-validator is written as
library main_app.custom_validator;
import 'dart:html';
import 'package:polymer/polymer.dart';
import 'package:polymer_elements/iron_validator_behavior.dart' show IronValidatorBehavior;
#PolymerRegister('custom-validator')
class CustomValidator extends PolymerElement with IronValidatorBehavior{
CustomValidator.created() : super.created();
#reflectable
bool validate(e, [_]){
switch(validatorType) {
case 'json':
return textValidator(e);
}
return false;
}
bool textValidator(e){
return false;
}
}
okay, it was a typo, the above works if I change.
<custom-validator id="validator" validator-name="jsonValidator" validator-type="json"></custom-validator>
to
<custom-validator id="validator" validator-name="jsonValidator" validator-check="json"></custom-validator>
The property validatorType needs to be set to its default value of 'validator'. Instead added a new property validatorCheck, where I set the validation method name to be called.
library custom_validator;
import 'dart:mirrors' show reflect;
import 'package:polymer/polymer.dart';
import 'package:polymer_elements/iron_validator_behavior.dart' show IronValidatorBehavior;
import 'package:validator/validator.dart' show isJSON;
#PolymerRegister('custom-validator')
class CustomValidator extends PolymerElement with IronValidatorBehavior{
#property
String validatorCheck;
CustomValidator.created() : super.created();
#reflectable
bool validate(value, [_]){
if(validatorCheck.isNotEmpty) {
Symbol sym = new Symbol(validatorCheck);
return reflect(this).invoke(sym, [value]).reflectee;
}
return false;
}
bool json(String value, [_]){
if(value.isNotEmpty) {
return isJSON(value);
}
return true;
}
}

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

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

How to remove a nested dom rep element in dart using polymer >1.0

Hello I cant seem to find the correct way to remove a nested element using polymer 1.0
createMigrationTable.html
<dom-module id="custom-create-migration-table">
<template>
<style>
paper-button {
/**border: 1px solid #d81b60;*/
}
paper-material {
width: 98%;
margin: 5px auto;
}
br{
clear:both;
}
</style>
<paper-material elevation="2" id="page_2">
createTable:
<paper-button raised on-tap="addTable">
+ add new table
</paper-button>
<template is="dom-repeat" items="{{createTables}}">
{{item.name}}
<paper-input required label="Table Name" value="{{item.name}}"></paper-input>
<template is="dom-repeat" items="{{item.columns}}" as="column">
<custom-column-view column="{{column}}"></custom-column-view>
<a on-tap="removeColumn">remove</a>
<br/>
</template>
<paper-button raised on-tap="addColumn">
Add Column
</paper-button>
</template>
</paper-material>
</template>
</dom-module>
createMigrationTable.dart
#HtmlImport('createMigrationTable.html')
library dartabase.poly.createMigrationTable;
// Import the paper element from Polymer.
import 'package:polymer_elements/iron_pages.dart';
import 'package:polymer_elements/paper_material.dart';
import 'package:polymer_elements/paper_button.dart';
import 'package:polymer_elements/paper_input.dart';
//import "../poly/columnView.dart";
import "../poly/table.dart";
// Import the Polymer and Web Components scripts.
import 'package:polymer/polymer.dart';
import 'package:web_components/web_components.dart';
#PolymerRegister('custom-create-migration-table')
class CreateMigrationTable extends PolymerElement {
#property
List<Table> createTables = new List();
CreateMigrationTable.created() : super.created();
#reflectable
addTable(event, [_]) {
Table table = new Table(name:"defaultName",columns: [{
"name":"defaultName",
"type":["BINT",
"INT",
"VARCHAR"
],
"def":"",
"nil":true
}]);
/*Map table = {
"tableName":"defaultTableName",
"columns":[{
"name":"defaultName",
"type":"defaultType",
"default":"",
"notNull":true
}]
};*/
//createTables.add(table);
add('createTables', table);
}
#reflectable
transition(event, [_]) {
IronPages ip = Polymer.dom(this.root).querySelector("iron-pages");
ip.selectNext();
}
#reflectable
void addColumn(event, [_]) {
var model = new DomRepeatModel.fromEvent(event);
model.add("item.columns", {
"name":"defaultName",
"type":"",
"def":"",
"nil":true
});
}
#reflectable
void removeColumn(event, [_]) {
var model = new DomRepeatModel.fromEvent(event);
model.removeItem("item", model.item);
}
void ready() {
print("$runtimeType::ready()");
}
}
tabel.dart
import 'package:polymer/polymer.dart';
import 'package:web_components/web_components.dart';
class Table extends JsProxy {
#reflectable
String name;
#reflectable
List columns;
Table({this.name, this.columns});
}
what should be the correct way to remove the element.
first I tried to call remove from inside the custom-column-view. but that did not update the bindings
then I read that the parent elements should have the control over adding and removing elements.. so I moved it one level above.
I would have thought that I can do it like in the addColumn method but but inside removeColumn I only receive the column view item.
I also tried to use index-as but I dont know how to be able to access both indexes i and j inside the removeColumn functions.
A custom equals implementation might fix it (not sure)
class Table extends JsProxy {
#reflectable
final String name;
#reflectable
List columns;
Table({this.name, this.columns});
int get hasCode => name.hashCode;
int operator ==(Object other) => other is Table && other.name == name;
}

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 subscribe to change of an observable field

until lately I could use bindProperty like shown below or in this question, but that has changed with 0.8.0 and I don't know how to change my code to get the old behaviour (doSomething() gets called):
<polymer-element name="my-login" attributes="model">
<template>
<template if="{{"model.isLoggedIn}}">
...
</template>
</template>
<script type= ... ></script>
</polymer-element>
.
#CustomTag("my-login")
class MyLogin extends PolymerElement with ObservableMixin {
LoginModel model;
#override
inserted() {
void doSomething() {
...
}
logoutChangeSubscription = bindProperty(model, #isLoggedIn, () => doSomething());
}
}
class Model extends Object with ObservableMixin {
#observable bool isLoggedIn = false;
}
With Polymer.dart 0.8 or greater, you can also use this convenience form:
isLoggedInChanged(oldValue) {
doSomething();
}
Notice how you can create a method inside your PolymerElement that uses a name of yourFieldName*Changed
There's also onPropertyChange as defined here: http://api.dartlang.org/docs/bleeding_edge/observe.html#onPropertyChange
From the docs:
class MyModel extends ObservableBase {
StreamSubscription _sub;
MyOtherModel _otherModel;
MyModel() {
...
_sub = onPropertyChange(_otherModel, const Symbol('value'),
() => notifyProperty(this, const Symbol('prop'));
}
String get prop => _otherModel.value;
set prop(String value) { _otherModel.value = value; }
}
Polymer.dart >= 1.0.0
#Property(observer: 'doSomething') bool isLoggedIn;
#reflectable
void doSomething(bool newValue, bool oldValue) => ...
or
#Observe('isLoggedIn')
void doSomething(event, detail) => ...
Polymer.dart < 1.0.0
Ok, found it
new PathObserver(model, "isLoggedIn").changes.listen((e) => doSomething());
It seems that the syntax has changed a little bit.
The syntax of the solution proposed by Günter now seems to be:
new PathObserver(model, "isLoggedIn").open((e) => doSomething());

Resources