Skip to content
This repository has been archived by the owner on Jul 11, 2019. It is now read-only.

Commit

Permalink
feature: MaterialRepeat / ObservableList got callback for fast rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
MikeMitterer committed Mar 8, 2017
1 parent 297269c commit 46d70f6
Show file tree
Hide file tree
Showing 4 changed files with 287 additions and 126 deletions.
3 changes: 2 additions & 1 deletion lib/mdlobservable.dart
Expand Up @@ -19,8 +19,9 @@

library mdlobservable;

import 'dart:collection';
import "dart:async";
import 'dart:collection';
import 'dart:html' as dom;

import 'package:logging/logging.dart';
import "package:mdl/mdlcore.dart";
Expand Down
1 change: 1 addition & 0 deletions lib/src/grinder/src/samples.dart
Expand Up @@ -181,6 +181,7 @@ void createSampleList() {
samples.add(new Sample("observe", Type.Directive, hasOwnDartMain: true, hasOwnDemoHtml: true));
samples.add(new Sample("repeat", Type.Directive, hasOwnDartMain: true, hasOwnDemoHtml: true));
samples.add(new Sample("repeat_data-table", Type.Directive, hasOwnDartMain: true, hasOwnDemoHtml: true));
samples.add(new Sample("repeat_callback", Type.Directive, hasOwnDartMain: true, hasOwnDemoHtml: true));

// SPA Samples
samples.add(new Sample("content", Type.SPA, hasOwnDartMain: true, hasOwnDemoHtml: true));
Expand Down
93 changes: 83 additions & 10 deletions lib/src/observable/ObservableList.dart
Expand Up @@ -23,26 +23,79 @@ enum ListChangeType {
ADD, INSERT, UPDATE, REMOVE, CLEAR
}

typedef bool UpdateItem<T>(final dom.HtmlElement element, final T item);

/// Propagated if List changes
class ListChangedEvent<T> {
/// [changetype] shows what changed
final ListChangeType changetype;

/// [item] is set on ADD, REMOVE and update
final Object item;
final T item;

/// [prevItem] is set on UPDATE and defines the old Entry
/// It is also set on INSERT. It defines the previous items a the given index
final Object prevItem;
final T prevItem;

ListChangedEvent(this.changetype,{ final Object this.item, final this.prevItem });
ListChangedEvent(this.changetype,{ final T this.item, final this.prevItem });
}

/**
* List that sends [ListChangeEvents] to the listener if this list changes
* Supported methods:
* Add, insert, update ([]), remove, clear, removeAll
*/
/// List that sends [ListChangeEvents] to the listener if this list changes
/// Supported methods:
/// Add, insert, update ([]), remove, clear, removeAll
///
/// @MdlComponentModel
/// class MyComponent extends MdlTemplateComponent {
///
/// final ObservableList<RecordsPerDay> records = new ObservableList<RecordsPerDay>(
/// updateCallback: _updateListItem);
///
/// void _init() {
///
/// render().then((_) {
/// _bindStoreActions();
/// });
/// }
///
/// void _bindStoreActions() {
/// if(_store == null) { return;}
///
/// _store.onChange.listen((final DataStoreChangedEvent event) {
/// _updateView();
/// });
/// }
///
/// void _updateView() {
///
/// if(records.length != _store.records.length) {
/// records.clear();
/// records.addAll(_store.records);
///
/// } else {
/// final List<RecordsPerDay> _storeRecords = _store.records;
/// for (int index = 0; index < _storeRecords.length; index++) {
///
/// // Triggers _updateListItem!!! in MaterialRepeat
/// records[index] = _storeRecords[index];
/// }
/// }
/// }
///
/// static bool _updateListItem(final dom.HtmlElement element, final RecordsPerDay item, final RecordsPerDay prevItem) {
/// if(element == null) {
/// return false;
/// }
/// final dom.SpanElement date = element.querySelector(".date");
/// final dom.SpanElement records = element.querySelector(".records");
/// date.text = item.date.toString();
/// records.text = item.records.toString();
/// return true;
/// }
///
/// @override
/// final String template = "...";
/// }
///
@MdlComponentModel
class ObservableList<T> extends ListBase<T> {
//final Logger _logger = new Logger('mdlobservable.ObservableList');
Expand All @@ -52,10 +105,18 @@ class ObservableList<T> extends ListBase<T> {

StreamController<ListChangedEvent<T>> _onChange;

final UpdateItem _updateCallback;

/// If [updateCallback] is given it will be called from [MaterialRepeat]
/// if the list updates
ObservableList({final UpdateItem updateCallback: _defaultUpdateCallback })
: _updateCallback = updateCallback;

/// Propagated Event-Listener
Stream<ListChangedEvent<T>> get onChange {
if(_onChange == null) {
_onChange = new StreamController<ListChangedEvent<T>>.broadcast(onCancel: () => _onChange = null);
_onChange = new StreamController<ListChangedEvent<T>>.broadcast(
onCancel: () => _onChange = null);
}
return _onChange.stream;
}
Expand All @@ -66,6 +127,7 @@ class ObservableList<T> extends ListBase<T> {
_innerList.length = length;
}

/// Updates the internal List.
void operator []=(int index, T value) {
_fire(new ListChangedEvent<T>(ListChangeType.UPDATE,
item: value,
Expand Down Expand Up @@ -134,7 +196,7 @@ class ObservableList<T> extends ListBase<T> {

@override
bool remove(final Object element) {
_fire(new ListChangedEvent<T>(ListChangeType.REMOVE,item: element ));
_fire(new ListChangedEvent<T>(ListChangeType.REMOVE,item: element as T ));
return _innerList.remove(element);
}

Expand Down Expand Up @@ -194,6 +256,10 @@ class ObservableList<T> extends ListBase<T> {
}
}

/// Called by MaterialRepeat do make fast updates
bool update(final dom.HtmlElement element, final T item)
=> _updateCallback(element,item);

//- private -----------------------------------------------------------------------------------

void _fire(final ListChangedEvent<T> event) {
Expand All @@ -212,4 +278,11 @@ class ObservableList<T> extends ListBase<T> {
void _clearFilter() {
_filterBackup.clear();
}

/// Default Callback for updating the UI
///
/// It returns false which indicates that MaterialRepeater should call it's own
/// update routines
static bool _defaultUpdateCallback<T>(final dom.HtmlElement element, final T item) => false;

}

0 comments on commit 46d70f6

Please sign in to comment.