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

Commit

Permalink
feat(ngElement): add support for attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
vicb authored and mhevery committed Apr 4, 2014
1 parent a36d2f2 commit 581861e
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 55 deletions.
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();
}
}
11 changes: 11 additions & 0 deletions test/_specs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,15 @@ class Expect {
toHaveClass(cls) => unit.expect(actual.classes.contains(cls), true,
reason: ' Expected ${actual} to have css class ${cls}');

toHaveAttribute(name, [value = null]) {
unit.expect(actual.attributes.containsKey(name), true,
reason: 'Epxected $actual to have attribute $name');
if (value != null) {
unit.expect(actual.attributes[name], value,
reason: 'Epxected $actual attribute "$name" to be "$value"');
}
}

toEqualSelect(options) {
var actualOptions = [];

Expand Down Expand Up @@ -164,6 +173,8 @@ class NotExpect {

toHaveClass(cls) => unit.expect(actual.classes.contains(cls), false,
reason: ' Expected ${actual} to not have css class ${cls}');
toHaveAttribute(name) => unit.expect(actual.attributes.containsKey(name),
false, reason: ' Expected $actual to not have attribute "$name"');
toBe(expected) => unit.expect(actual,
unit.predicate((actual) => !identical(expected, actual), 'not $expected'));
toEqual(expected) => unit.expect(actual,
Expand Down
142 changes: 105 additions & 37 deletions test/core_dom/ng_element_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,75 +6,143 @@ import 'dart:html' as dom;
void main() {
describe('ngElement', () {

it('should add classes on domWrite to the element',
(TestBed _, NgAnimate animate) {
describe('classes', () {
it('should add classes to the element on domWrite',
(TestBed _, NgAnimate animate) {

var scope = _.rootScope;
var element = e('<div></div>');
var ngElement = new NgElement(element, scope, animate);

ngElement..addClass('one')..addClass('two three');

['one', 'two', 'three'].forEach((className) {
expect(element).not.toHaveClass(className);
});

scope.apply();

['one', 'two', 'three'].forEach((className) {
expect(element).toHaveClass(className);
});
});

it('should remove classes from the element on domWrite',
(TestBed _, NgAnimate animate) {

var scope = _.rootScope;
var element = e('<div class="one two three four"></div>');
var ngElement = new NgElement(element, scope, animate);

ngElement..removeClass('one')
..removeClass('two')
..removeClass('three');

['one', 'two', 'three', 'four'].forEach((className) {
expect(element).toHaveClass(className);
});

scope.apply();

['one', 'two', 'three'].forEach((className) {
expect(element).not.toHaveClass(className);
});
expect(element).toHaveClass('four');
});

it('should always apply the last dom operation on the given className',
(TestBed _, NgAnimate animate) {

var scope = _.rootScope;
var element = e('<div></div>');
var ngElement = new NgElement(element, scope, animate);

ngElement..addClass('one')
..addClass('one')
..removeClass('one');

expect(element).not.toHaveClass('one');

scope.apply();

expect(element).not.toHaveClass('one');

ngElement..removeClass('one')
..removeClass('one')
..addClass('one');

scope.apply();

expect(element.classes.contains('one')).toBe(true);
});
});
});

describe('attributes', () {
it('should set attributes on domWrite to the element',
(TestBed _, NgAnimate animate) {

var scope = _.rootScope;
var element = _.compile('<div></div>');
var element = e('<div></div>');
var ngElement = new NgElement(element, scope, animate);

ngElement.addClass('one');
ngElement.addClass('two three');
ngElement.setAttribute('id', 'foo');
ngElement.setAttribute('title', 'bar');

['one','two','three'].forEach((className) {
expect(element.classes.contains(className)).toBe(false);
['id', 'title'].forEach((name) {
expect(element).not.toHaveAttribute(name);
});

scope.apply();

['one','two','three'].forEach((className) {
expect(element.classes.contains(className)).toBe(true);
});
expect(element).toHaveAttribute('id', 'foo');
expect(element).toHaveAttribute('title', 'bar');
});

it('should remove classes on domWrite to the element',
(TestBed _, NgAnimate animate) {
it('should remove attributes from the element on domWrite ',
(TestBed _, NgAnimate animate) {

var scope = _.rootScope;
var element = _.compile('<div class="one two three four"></div>');
var element = e('<div id="foo" title="bar"></div>');
var ngElement = new NgElement(element, scope, animate);

ngElement.removeClass('one');
ngElement.removeClass('two');
ngElement.removeClass('three');
ngElement..removeAttribute('id')
..removeAttribute('title');

['one','two','three'].forEach((className) {
expect(element.classes.contains(className)).toBe(true);
});
expect(element.classes.contains('four')).toBe(true);
expect(element).toHaveAttribute('id', 'foo');
expect(element).toHaveAttribute('title', 'bar');

scope.apply();

['one','two','three'].forEach((className) {
expect(element.classes.contains(className)).toBe(false);
});
expect(element.classes.contains('four')).toBe(true);
expect(element).not.toHaveAttribute('id');
expect(element).not.toHaveAttribute('title');
});

it('should always apply the last dom operation on the given className',
(TestBed _, NgAnimate animate) {
it('should always apply the last operation on the attribute',
(TestBed _, NgAnimate animate) {

var scope = _.rootScope;
var element = _.compile('<div></div>');
var element = e('<div></div>');
var ngElement = new NgElement(element, scope, animate);

ngElement.addClass('one');
ngElement.addClass('one');
ngElement.removeClass('one');
ngElement..setAttribute('id', 'foo')
..setAttribute('id', 'foo')
..removeAttribute('id');

expect(element.classes.contains('one')).toBe(false);
expect(element).not.toHaveAttribute('id');

scope.apply();

expect(element.classes.contains('one')).toBe(false);
expect(element).not.toHaveAttribute('id');

ngElement..removeAttribute('id')
..setAttribute('id', 'foobar')
..setAttribute('id', 'foo');

element.classes.add('one');
scope.apply();

ngElement.removeClass('one');
ngElement.removeClass('one');
ngElement.addClass('one');
expect(element).toHaveAttribute('id', 'foo');

expect(element.classes.contains('one')).toBe(true);
});
});
}

0 comments on commit 581861e

Please sign in to comment.