-
-
Save dam0vm3nt/d50b4862ccfdacd91d11 to your computer and use it in GitHub Desktop.
import "package:polymer/polymer.dart"; | |
import "package:observe/observe.dart"; | |
import "package:smoke/smoke.dart"; | |
import "package:logging/logging.dart"; | |
class ObservablePolymerNotifier { | |
static final Logger _logger = new Logger("polymer.auto.notify"); | |
String _path=""; | |
PolymerElement _element; | |
Observable _target; | |
StreamSubscription _sub; | |
Map<String,ObservablePolymerNotifier> _subNotifiers = {}; | |
ObservablePolymerNotifier(PolymerElement element, var target,[String path=""]) { | |
_element = element; | |
_path = path; | |
_target = target; | |
if (target is Observable) { | |
_sub = target.changes.listen((List<ChangeRecord> recs) { | |
recs.where((ChangeRecord cr) => cr is PropertyChangeRecord).forEach((PropertyChangeRecord pcr) { | |
var val = pcr.newValue; | |
_notifySymbol(symbolToName(pcr.name), val); | |
}); | |
}); | |
} else if (target is ObservableList) { | |
_sub = (target as ObservableList).listChanges.listen((List<ListChangeRecord> rc){ | |
// Notify splice | |
rc.forEach((ListChangeRecord lc) { | |
_notifySplice(target,"${_path}${symbolToName(pcr.name)}",lc.index,lc.addedCount,lc.removed); | |
// Fix observers | |
if(lc.removed!=null) { | |
for(int i=0;i<lc.removed.length;i++) { | |
_subNotifiers.remove((lc.index+i).toString()).close(); | |
} | |
// fix path on the rest | |
for (int i=lc.index;i<target.length;i++) { | |
_subNotifiers[i.toString()]=_subNotifiers.remove((i+lc.removed.length).toString()) | |
.._path="${_path}${symbolToName(pcr.name)}.${i}"; | |
} | |
} | |
if (lc.addedCount>0) { | |
// Fix path on tail | |
for (int i=lc.index+lc.addedCount;i<target.length;i++) { | |
_subNotifiers[i.toString()] =_subNotifiers.remove((i-lc.addedCount).toString()) | |
.._path="${_path}${symbolToName(pcr.name)}.${i}"; | |
} | |
// Add new observers | |
for (int i=lc.index;i<lc.addedCount+lc.index;i++) { | |
_notifySymbol(i.toString(),target[i]); | |
} | |
} | |
}); | |
}); | |
} | |
// Add Sub | |
if (target is List) { | |
for (int i=0;i<target.length;i++) { | |
_notifySplice(target,"${_path}".substring(0,_path.length-1),0,target.length,[]); | |
_notifySymbol(i.toString(),target[i]); | |
} | |
} else { | |
List<Declaration> fields = query(target.runtimeType,new QueryOptions(includeFields:true,includeProperties:true,includeInherited:false)); | |
fields.forEach((Declaration f) { | |
if (f.annotations.any((a)=> a is ObservableProperty)) { | |
var fieldValue=read(target,f.name); | |
_notifySymbol(symbolToName(f.name),fieldValue); | |
} | |
}); | |
} | |
} | |
void _notifySymbol(String name,var value) { | |
String path = "${_path}${name}"; | |
_logger.fine("Notify ${path} with ${value} for ${_element}"); | |
_element.notifyPath(path,value); | |
_installSubNotifier(name,value); | |
} | |
void _installSubNotifier(String name,var target) { | |
// Attach a new sub notifier for observable objects | |
_logger.fine("Installing subnotifier for ${name} with ${target}"); | |
ObservablePolymerNotifier subNotifier = _subNotifiers[name]; | |
if (subNotifier!=null) { | |
subNotifier.close(); | |
} | |
String subPath = "${_path}${name}"; | |
if (target!=null && (target is Observable || target is List)) { | |
subNotifier = new ObservablePolymerNotifier(_element,target,"${subPath}."); | |
_subNotifiers[name] = subNotifier; | |
} | |
} | |
void close() { | |
_subNotifiers.values.forEach((ObservablePolymerNotifier x) => x.close()); | |
_subNotifiers.clear(); | |
if (_sub!=null) { | |
_sub.cancel(); | |
_sub=null; | |
} | |
} | |
void _notifySplice(List array, String path, int index, int added, List removed) => | |
_element.jsElement.callMethod('_notifySplice', [jsValue(array),path,index,added, jsValue(removed)]); | |
} | |
@behavior | |
abstract class PolymerAutoNotifySupportMixin { | |
ObservablePolymerNotifier _observablePolymerNotifier; | |
static created(mixin) { | |
print("MIXIN CREATED CALLED !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!11"); | |
} | |
void attached() { | |
_observablePolymerNotifier = new ObservablePolymerNotifier(this,this); | |
} | |
void detached() { | |
_observablePolymerNotifier.close(); | |
_observablePolymerNotifier=null; | |
} | |
} |
Great! I will definitely follow your advice.
I made some changes using attached and detached callback and polishing a bit.
I'm going to write a smoke transformer to make the thing work with dart2js too and/or patch observable to carry String field info (shouldn't be too difficult to modify the PropertyChangeEvent and the transformer) along with symbol thus to garantee retrocompatibilty and to submit a pull request.
I will package this little thing and submit to pub for public utility.
I turns out smoke and some reflection is indeed necessary otherwise we don't get notifications for initial values.
Added _notifySplice and ObservableList support.
I would also use the static attached
and detached
methods, instead of the instance methods, otherwise they could be overridden by other mixins.
fyi, if you make your mixin be a behavior (annotate it with
@behavior
) then you can callstartNotify
in a staticcreated
method. Something like this: