Observe package from polymer dart - dart

I am trying to use the observe package without having to have annotations in my Model, and only by raising notifyPropertyChange in setters, so to test i made the following test
import 'package:observe/observe.dart';
import 'dart:async';
import 'dart:math';
void main() {
var dummyWatchingModel = new DummyWatchingModel();
new Timer.periodic(new Duration(milliseconds:1000), (_){
//calls a function that set a random value to the property in the observable model
dummyWatchingModel.setModelProps();
});
}
class Model extends Observable{
int _x;
Model(this._x);
int get x=> _x;
void set x(int value){
_x = notifyPropertyChange(#_x, _x, value);
}
}
class DummyWatchingModel{
Model model = new Model(1);
final rng = new Random();
anotherModel(){
//watch for changes in model instance properties
this.model.changes.listen((List<ChangeRecord> records) {
for(ChangeRecord change in records){
print(change.toString());
}
});
}
//the callback for the timer to assign a random value model.x
setModelProps(){
model.x = rng.nextInt(100);
print('called...');
}
}
i am changing the value of a property in an instance of Model using a setter that raises notifyPropertyChange but it never listens for changes, any idea why?

I think you want to use ChangeNotifier instead of Observable.
I'm not sure about notifyPropertyChange but with Observable you normally need to call dirtyCheck to get notified about changes.
I made a small example a while ago to learn how these two work:
import 'package:observe/observe.dart';
class Notifiable extends Object with ChangeNotifier {
String _input = '';
#reflectable
get input => _input;
#reflectable
set input(val) {
_input = notifyPropertyChange(#input, _input, val + " new");
}
Notifiable() {
this.changes.listen((List<ChangeRecord> record) => record.forEach(print));
}
}
class MyObservable extends Observable {
#observable
String counter = '';
MyObservable() {
this.changes.listen((List<ChangeRecord> record) => record.forEach(print));
}
}
void main() {
var x = new MyObservable();
x.counter = "hallo";
Observable.dirtyCheck();
Notifiable notifiable = new Notifiable();
notifiable.input = 'xxx';
notifiable.input = 'yyy';
}

Related

Error in Iteration example in A Tour of the Dart Libraries

Problem
How can I implement the example of Iteration in A Tour of the Dart Libraries?
I understood that it is a code that must be supplemented, but how do I supplement it?
A Tour of the Dart Libraries
class Process {
// Represents a process...
}
class ProcessIterator implements Iterator<Process> {
#override
Process get current => ...
#override
bool moveNext() => ...
}
// A mythical class that lets you iterate through all
// processes. Extends a subclass of [Iterable].
class Processes extends IterableBase<Process> {
#override
final Iterator<Process> iterator = ProcessIterator();
}
void main() {
// Iterable objects can be used with for-in.
for (var process in Processes()) {
// Do something with the process.
}
}
Development Environment
Dart 2
DartPad
Tried → Error
I read and executed the document on my own, but the following error occurred.
class ProcessIterator implements Iterator<Process> {
#override
Process get current => new Process();
#override
bool moveNext() => false;
}
-> Error: 'IterableBase' expects 0 type arguments.
Best regards,
Two things.
1) Process is not available on the web, so your DartPad example won't work.
2) IterableBase is in dart:collection. Don't forget to import that.
This code seems to work for me:
import 'dart:collection';
class ProcessIterator implements Iterator<int> {
#override
int get current => 0;
#override
bool moveNext() => false;
}
// A mythical class that lets you iterate through all
// processes. Extends a subclass of [Iterable].
class Processes extends IterableBase<int> {
#override
final Iterator<int> iterator = ProcessIterator();
}
main() {
for (var thing in Processes()) {
print(thing);
}
}
Keep in mind, it's often MUCH easier to use sync* for custom iterators.
Iterable<int> _myProcesses() sync* {
yield 1;
yield 2;
yield 3;
}
main() {
for (var thing in _myProcesses()) {
print(thing);
}
}
Refer to #Kevin 's answer and rewrite the code below.
Code
import 'dart:collection';
class Process {
var foo = 'foo';
}
class ProcessIterator implements Iterator<Process> {
int i = 0;
#override
Process get current => new Process();
#override
bool moveNext() {
if(i++ < 5) {
return true;
} else {
return false;
}
}
}
// A mythical class that lets you iterate through all
// processes. Extends a subclass of [Iterable].
class Processes extends IterableBase<Process> {
#override
final Iterator<Process> iterator = ProcessIterator();
}
void main() {
// Iterable objects can be used with for-in.
for (var process in Processes()) {
print(process.foo);
}
}
Console
foo
foo
foo
foo
foo

Using a stream for PropertyChanges

Trying to understand how streams work so i wrote this
class ViewModelBase{
final List<PropertyChangedRecord> _changeRecords = new List<PropertyChangedRecord>();
Stream<PropertyChangedRecord> _changes;
Stream<PropertyChangedRecord> get changes{
//lazy initialization
if(_changes==null)
_changes = new Stream<PropertyChangedRecord>.fromIterable(_changeRecords);
return _changes;
}
_raisePropertyChanged(oldValue,newValue,propertySymbol){
if(oldValue!=newValue){
_changeRecords.add(new PropertyChangedRecord(this, propertySymbol,oldValue,newValue));
}
return newValue;
}
}
class PropertyChangedRecord{
final ViewModelBase viewModel;
final Symbol propertySymbol;
final Object newValue;
final Object oldValue;
PropertyChangedRecord(this.viewModel,this.propertySymbol,this.oldValue,this.newValue);
}
and used it as
void main() {
var p = new Person('waa',13);
p.age = 33334;
p.name = 'dfa';
p.changes.listen((p)=>print(p));
p.age = 333834;
p.name = 'dfia';
}
class Person extends ViewModelBase{
String _name;
String get name => _name;
set name(String value) => _name = _raisePropertyChanged(_name,value,#name);
int _age;
int get age => _age;
set age(int value) => _age = _raisePropertyChanged(_age,value,#age);
Person(this._name,this._age);
}
and got the following exception
Uncaught Error: Concurrent modification during iteration: Instance(length:4) of '_GrowableList'
which i think is because the stream is removing items from the list while new PropertyChangedRecords are being added, how do i go around that?
The error could be caused by adding an item while the stream iterates the list.
You could use a StreamController to create the stream instead (see How to pass a callback function to a StreamController for an example).
class ViewModelBase{
//final List<PropertyChangedRecord> _changeRecords = new List<PropertyChangedRecord>();
//Stream<PropertyChangedRecord> _changes;
final StreamController _changeRecords = new StreamController<PropertyChangedRecord>();
Stream<PropertyChangedRecord> get changes => _changeRecords.stream;
_raisePropertyChanged(oldValue,newValue,propertySymbol){
if(oldValue!=newValue){
_changeRecords.add(new PropertyChangedRecord(this, propertySymbol,oldValue,newValue));
}
return newValue;
}
}

Observing an object containing a list of observables?

I am using the observe package.
Consider this example:
class Product extends Object with ChangeNotifier {
double _price = 0.0;
#reflectable double get price => _price;
#reflectable void set price(double value) {
if (value == null) throw new ArgumentError();
_price = notifyPropertyChange(#price, price, value);
}
}
class Order extends Object with ChangeNotifier {
final ObservableList<Product> products = new ObservableList<Product>();
double get total {
double sum = 0.0;
for (var item in products) {
sum += item.price;
}
return sum;
}
}
// Synchronizes the view total with the order total.
// Or rather, I'd like it to do that.
var order = new Order();
order.changes.listen((records) {
view.total = order.total;
});
How would I rewrite this example to make it work?
I would like to be notified of any changes to the object's state, even if they happen to the list or the items of the list.
Do I have to manage change subscriptions to all items and the list itself? Inside or outside of the Order class? Through which property would I notify the change? It seems messy either way.
The elements in the ObservableList do not propagate the notification to the list that contains them. They can't because they have no reference to the list.
Also the list does not forward the notifications to the class it is referenced by.
Not really satisfying but the best I could come up with.
import 'dart:async' as async;
import 'package:observe/observe.dart';
class Product extends Object with ChangeNotifier {
double _price = 0.0;
#reflectable double get price => _price;
#reflectable void set price(double value) {
if (value == null) throw new ArgumentError();
_price = notifyPropertyChange(#price, price, value);
}
#override
String toString() => 'Product - price: $price';
}
class Order extends Object with ChangeNotifier {
final ObservableList<Product> products = new ObservableList<Product>();
// keep listeners to be able to cancel them
final List<async.StreamSubscription> subscriptions = [];
Order() {
products.changes.listen((cr) {
// only react to length changes (isEmpty, isNotempty changes are redundant)
var lengthChanges = cr.where((c) => c.name == #length);
if(lengthChanges.isNotEmpty) {
lengthChanges.forEach((lc) =>
notifyChange(lc));
// we can't know if only additions/removals were done therefore we
// cancel all existing listeners and set up new ones for all items
// after each length change
_updateProductsListeners();
}
});
// initial setup
_updateProductsListeners();
}
// cancel all product change listeners and create new ones
void _updateProductsListeners() {
print('updateListeners');
subscriptions.forEach((s) => s.cancel());
subscriptions.clear();
products.forEach((p)
=> subscriptions.add(p.changes.listen((crs) =>
crs.forEach((cr) =>
notifyPropertyChange(cr.name, cr.oldValue, cr.newValue)))));
}
double get total {
double sum = 0.0;
for (var item in products) {
sum += item.price;
}
return sum;
}
}
void main() {
// Synchronizes the view total with the order total.
// Or rather, I'd like it to do that.
var order = new Order();
order.changes.listen((records) {
//view.total = order.total;
records.forEach(print);
});
// a PathObserver example but it doesn't seem to be more convenient
var op = new PathObserver(order, 'products[3].price')..open((c) =>
print(c));
var prods = [new Product()..price = 1.0, new Product()..price = 2.0, new Product()..price = 3.0, new Product()..price= 4.0];
var prods2 = [new Product()..price = 5.0, new Product()..price = 6.0];
order.products.addAll(prods);
// use Future to allow change notification propagate between changes
new async.Future(() =>
order.products..addAll(prods2)..removeWhere((p) => p.price < 3.0))
.then((_) => new async.Future(() => order.products[3].price = 7.0));
new async.Future.delayed(new Duration(seconds: 1), () => print('done'));
}
I suggest to use something like an event bus for this where the objects that want/should to notify about something just send and event and objects that are interested in something listen for that without any knowledge of where the other object exists.
For example https://pub.dartlang.org/packages/event_bus
Another solution is to use the ListPathObserver. The class is deprecated but you can copy his code and reuse it. With that class you can listen for specific changes in the contained items. The field to watch is specified by path.

How can I listen for changes to properties on objects inside a list, with polymer-dart?

I have a list of objects (for example, people), and I dynamically add and remove from the list. I want to run a query across the list when a certain property changes on any item in the list.
For example, I want to know if any object in the list has its "signedAgreement" property changed. I don't want to manually attached listeners to each object, I just want to ask the list. How can I do this?
My code:
library my_element;
import 'package:polymer/polymer.dart';
import 'dart:html';
import 'models.dart';
#CustomTag("my-element")
class MyElement extends PolymerElement with ObservableMixin {
final List people = toObservable([]); // observe adds/removes to the list
final Person newPerson = new Person();
// How can I know when to re-evaluate signedCount?
int get signedCount => people.where((Person p) => p.signedAgreement).length;
void save(Event e, var detail, Node target) {
people.add(new Person.from(newPerson));
newPerson.blank();
}
}
And my model object looks like:
library models;
import 'package:polymer/polymer.dart';
class Person extends Object with ObservableMixin {
#observable String name;
#observable bool signedAgreement = false;
Person();
Person.from(Person other) {
name = other.name;
signedAgreement = other.signedAgreement;
}
blank() {
name = '';
signedAgreement = false;
}
}
Enter: ListPathObserver!
Add this constructor:
MyElement() {
ListPathObserver observer = new ListPathObserver(people, 'signedAgreement');
observer.changes.listen((_) => notifyProperty(this, const Symbol('signedCount')));
}
Here, observer will fire when any person in people has its signedAgreement property changed.
Then, in the callback, we notify the observer system that it should go look at signedCount.

Dynamic class method invocation in Dart

Like the question at Dynamic class method invocation in PHP I want to do this in Dart.
var = "name";
page.${var} = value;
page.save();
Is that possible?
There are several things you can achieve with Mirrors.
Here's an example how to set values of classes and how to call methods dynamically:
import 'dart:mirrors';
class Page {
var name;
method() {
print('called!');
}
}
void main() {
var page = new Page();
var im = reflect(page);
// Set values.
im.setField("name", "some value").then((temp) => print(page.name));
// Call methods.
im.invoke("method", []);
}
In case you wonder, im is an InstanceMirror, which basically reflects the page instance.
There is also another question: Is there a way to dynamically call a method or set an instance variable in a class in Dart?
You can use Dart Mirror API to do such thing. Mirror API is not fully implemented now but here's how it could work :
import 'dart:mirrors';
class Page {
String name;
}
main() {
final page = new Page();
var value = "value";
InstanceMirror im = reflect(page);
im.setField("name", value).then((_){
print(page.name); // display "value"
});
}
You can use Serializable
For example:
import 'package:serializable/serializable.dart';
#serializable
class Page extends _$PageSerializable {
String name;
}
main() {
final page = new Page();
var attribute = "name";
var value = "value";
page["name"] = value;
page[attribute] = value;
print("page.name: ${page['name']}");
}

Resources