观察包含可观察列表的对象?

时间:2014-09-28 20:56:17

标签: dart observer-pattern

我正在使用observe package

考虑这个例子:

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

如何重写此示例以使其正常工作?

我希望收到对象状态的任何更改的通知,即使这些更改发生在列表或列表中的项目中。

我是否必须管理所有项目和列表本身的更改订阅? Order课程的内部或外部?通过哪个属性我会通知变更?无论如何,它似乎都很混乱。

2 个答案:

答案 0 :(得分:2)

ObservableList中的元素不会将通知传播到包含它们的列表。他们不能,因为他们没有参考列表。 此外,列表不会将通知转发给它所引用的类。

不是很满意,但我能想出的最好。

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

我建议使用类似事件总线的东西,其中想要/应该通知事物的对象只是发送,事件和对某事感兴趣的对象在不知道其他对象存在的情况下监听。< / p>

例如https://pub.dartlang.org/packages/event_bus

答案 1 :(得分:0)

另一种解决方案是使用ListPathObserver。该类已弃用,但您可以复制其代码并重复使用它。通过该类,您可以监听所包含项目中的特定更改。要监视的字段由路径指定。