-
-
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 callstartNotifyin a staticcreatedmethod. Something like this: