Skip to content
This repository has been archived by the owner on Feb 22, 2018. It is now read-only.

Dropping NodeAttrs from the injectable services #768

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 20 additions & 7 deletions lib/core_dom/directive.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ typedef void _AttributeChanged(String newValue);
typedef void Mustache(bool hasObservers);

/**
* NodeAttrs is a facade for element attributes. The facade is responsible
* for normalizing attribute names as well as allowing access to the
* value of the directive.
* NodeAttrs is a facade for element attributes.
*
* This facade allows reading and writing the attribute values as well as
* adding observers triggered when:
* - The value of an attribute changes,
* - An element becomes observed.
*/
class NodeAttrs {
final dom.Element element;
Expand All @@ -30,15 +33,20 @@ class NodeAttrs {
} else {
element.attributes[attrName] = value;
}

if (_observers != null && _observers.containsKey(attrName)) {
_observers[attrName].forEach((notifyFn) => notifyFn(value));
}
}

/**
* Observe changes to the attribute by invoking the [notifyFn] function. On
* registration the [notifyFn] function gets invoked synchronize with the
* current value.
* Observes changes to the attribute by invoking the [notifyFn]
* function. On registration the [notifyFn] function gets invoked in order to
* synchronize with the current value.
*
* When an observed is registered on an attributes any existing
* [_observerListeners] will be called with the first parameter set to
* [:true:]
*/
observe(String attrName, notifyFn(String value)) {
if (_observers == null) _observers = <String, List<_AttributeChanged>>{};
Expand All @@ -61,14 +69,19 @@ class NodeAttrs {

Iterable<String> get keys => element.attributes.keys;

/**
* Registers a listener to be called when the attribute [attrName] becomes
* observed. On registration [notifyFn] function gets invoked with [:false:]
* as the first argument.
*/
void listenObserverChanges(String attrName, Mustache notifyFn) {
_mustacheAttrs[attrName] = new _MustacheAttr(notifyFn);
notifyFn(false);
}
}

/**
* TemplateLoader is an asynchronous access to ShadowRoot which is
* [TemplateLoader] is an asynchronous access to ShadowRoot which is
* loaded asynchronously. It allows a Component to be notified when its
* ShadowRoot is ready.
*/
Expand Down
63 changes: 45 additions & 18 deletions lib/core_dom/ng_element.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,65 @@ part of angular.core.dom_internal;

@NgInjectableService()
class NgElement {
static const _TO_BE_REMOVED = const Object();

final dom.Element node;
final Scope _scope;
final NgAnimate _animate;
final _classes = new Map<String, bool>();

NgElement(this.node, this._scope, NgAnimate this._animate);
final _classesToUpdate = <String, bool>{};
final _attributesToUpdate = <String, dynamic>{};

addClass(String className) {
if(_classes.isEmpty) {
_listenOnWrite();
}
_classes[className] = true;
bool _writeScheduled = false;

NgElement(this.node, this._scope, this._animate);

void addClass(String className) {
_scheduleDomWrite();
_classesToUpdate[className] = true;
}

removeClass(String className) {
if(_classes.isEmpty) {
_listenOnWrite();
}
_classes[className] = false;
void removeClass(String className) {
_scheduleDomWrite();
_classesToUpdate[className] = false;
}

// todo(vicb) add tests
void setAttribute(String attrName, [value = '']) {
_scheduleDomWrite();
_attributesToUpdate[attrName] = value == null ? '' : value;
}

_listenOnWrite() {
_scope.rootScope.domWrite(() => flush());
void removeAttribute(String attrName) {
_scheduleDomWrite();
_attributesToUpdate[attrName] = _TO_BE_REMOVED;
}

flush() {
_classes.forEach((className, status) {
status == true
_scheduleDomWrite() {
if (!_writeScheduled) {
_writeScheduled = true;
_scope.rootScope.domWrite(() {
_writeToDom();
_writeScheduled = false;
});
}
}

_writeToDom() {
_classesToUpdate.forEach((String className, bool toBeAdded) {
toBeAdded
? _animate.addClass(node, className)
: _animate.removeClass(node, className);
});
_classes.clear();
_classesToUpdate.clear();

_attributesToUpdate.forEach((String attrName, value) {
if (value == _TO_BE_REMOVED) {
node.attributes.remove(attrName);
} else {
node.attributes[attrName] = value;
}
});
_attributesToUpdate.clear();
}
}
2 changes: 1 addition & 1 deletion lib/core_dom/view_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ class _AnchorAttrs extends NodeAttrs {

_AnchorAttrs(DirectiveRef this._directiveRef): super(null);

operator [](name) => name == '.' ? _directiveRef.value : null;
String operator [](name) => name == '.' ? _directiveRef.value : null;

void observe(String attributeName, _AttributeChanged notifyFn) {
notifyFn(attributeName == '.' ? _directiveRef.value : null);
Expand Down
8 changes: 3 additions & 5 deletions lib/directive/input_select.dart
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,7 @@ class OptionValueDirective implements NgAttachAware,
}

attach() {
if (_inputSelectDirective != null) {
_inputSelectDirective.dirty();
}
if (_inputSelectDirective != null) _inputSelectDirective.dirty();
}

detach() {
Expand Down Expand Up @@ -151,8 +149,8 @@ class _SingleSelectMode extends _SelectMode {
dom.SelectElement select,
NgModel model,
this._nullOption,
this._unknownOption
): super(expando, select, model) {
this._unknownOption)
: super(expando, select, model) {
}

onViewChange(event) {
Expand Down
4 changes: 2 additions & 2 deletions lib/directive/ng_bind_html.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ class NgBindHtmlDirective {
* expression is innerHTML'd according to the rules specified in this class'
* documentation.
*/
set value(value) => element.setInnerHtml(value == null ? '' : value.toString(),
validator: validator);
void set value(value) => element.setInnerHtml(
value == null ? '' : value.toString(), validator: validator);
}
Loading