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

Commit

Permalink
feat(components): change shadow boundary to ignore duplicate styles
Browse files Browse the repository at this point in the history
Closes #1423
  • Loading branch information
vsavkin authored and Victor Savkin committed Sep 11, 2014
1 parent a943370 commit a3bc7d4
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 14 deletions.
16 changes: 6 additions & 10 deletions lib/core_dom/directive_injector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -169,18 +169,11 @@ class DirectiveInjector implements DirectiveBinder {
static Binding _tempBinding = new Binding();

DirectiveInjector(DirectiveInjector parent, appInjector, this._node, this._nodeAttrs,
this._eventHandler, this.scope, this._animate, [View view, ShadowBoundary boundary])
this._eventHandler, this.scope, this._animate, [View view, this._shadowBoundary])
: _parent = parent,
_appInjector = appInjector,
_view = view == null && parent != null ? parent._view : view,
_constructionDepth = _NO_CONSTRUCTION,
_shadowBoundary = _getShadowBoundary(boundary, parent);

static _getShadowBoundary(ShadowBoundary boundary, DirectiveInjector parent) {
if (boundary != null) return boundary;
if (parent != null) return parent._shadowBoundary;
return new DefaultShadowBoundary();
}
_constructionDepth = _NO_CONSTRUCTION;

void bind(key, {dynamic toValue: DEFAULT_VALUE,
Function toFactory: DEFAULT_VALUE,
Expand Down Expand Up @@ -317,7 +310,7 @@ class DirectiveInjector implements DirectiveBinder {
case ELEMENT_PROBE_KEY_ID: return elementProbe;
case NG_ELEMENT_KEY_ID: return ngElement;
case EVENT_HANDLER_KEY_ID: return _eventHandler;
case SHADOW_BOUNDARY_KEY_ID: return _shadowBoundary;
case SHADOW_BOUNDARY_KEY_ID: return _findShadowBoundary();
case DESTINATION_LIGHT_DOM_KEY_ID: return _destLightDom;
case SOURCE_LIGHT_DOM_KEY_ID: return _sourceLightDom;
case VIEW_KEY_ID: return _view;
Expand Down Expand Up @@ -408,6 +401,9 @@ class DirectiveInjector implements DirectiveBinder {
}

DestinationLightDom get _destLightDom => _parent == null ? null : _parent.lightDom;

ShadowBoundary _findShadowBoundary() =>
_shadowBoundary != null ? _shadowBoundary : getFromParentByKey(SHADOW_BOUNDARY_KEY);
}

class TemplateDirectiveInjector extends DirectiveInjector {
Expand Down
1 change: 1 addition & 0 deletions lib/core_dom/module_internal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ class CoreDomModule extends Module {
bind(ElementBinderFactory);
bind(NgElement);
bind(EventHandler);
bind(ShadowBoundary, toImplementation: DefaultShadowBoundary);
// TODO(rkirov): remove this once clients have stopped relying on it.
bind(DirectiveInjector, toValue: null);
}
Expand Down
26 changes: 22 additions & 4 deletions lib/core_dom/shadow_boundary.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,43 @@ part of angular.core.dom_internal;
* [ShadowBoundary] is responsible for inserting style elements.
*/
abstract class ShadowBoundary {
Set<dom.StyleElement> _insertedStyles;

void insertStyleElements(List<dom.StyleElement> elements);

Iterable<dom.StyleElement> _newStyles(Iterable<dom.StyleElement> elements) {
if (_insertedStyles == null) return elements;
return elements.where((el) => !_insertedStyles.contains(el));
}

void _addInsertedStyles(Iterable<dom.StyleElement> elements) {
if (_insertedStyles == null) _insertedStyles = new Set();
_insertedStyles.addAll(elements);
}
}

@Injectable()
class DefaultShadowBoundary implements ShadowBoundary {
class DefaultShadowBoundary extends ShadowBoundary {
void insertStyleElements(List<dom.StyleElement> elements) {
final cloned = elements.map((el) => el.clone(true));
final newStyles = _newStyles(elements);
final cloned = newStyles.map((el) => el.clone(true));
dom.document.head.nodes.addAll(cloned);
_addInsertedStyles(newStyles);
}
}

@Injectable()
class ShadowRootBoundary implements ShadowBoundary {
class ShadowRootBoundary extends ShadowBoundary {
final dom.ShadowRoot shadowRoot;
dom.StyleElement _lastStyleElement;

ShadowRootBoundary(this.shadowRoot);

void insertStyleElements(List<dom.StyleElement> elements) {
if (elements.isEmpty) return;
final cloned = elements.map((el) => el.clone(true));

final newStyles = _newStyles(elements);
final cloned = newStyles.map((el) => el.clone(true));

cloned.forEach((style) {
if (_lastStyleElement != null) {
Expand All @@ -37,5 +53,7 @@ class ShadowRootBoundary implements ShadowBoundary {
_lastStyleElement = shadowRoot.append(style);
}
});

_addInsertedStyles(newStyles);
}
}
30 changes: 30 additions & 0 deletions test/core_dom/directive_injector_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ library directive_injector_spec;
import '../_specs.dart';
import 'package:angular/core_dom/directive_injector.dart';
import 'package:angular/core_dom/static_keys.dart';
import 'dart:html' as dom;

void main() {
describe('DirectiveInjector', () {
Expand Down Expand Up @@ -108,6 +109,35 @@ void main() {
});
});

describe("returning ShadowBoundary", () {
it('should return the shadow bounary of the injector', () {
final root = new dom.DivElement().createShadowRoot();
final boundary = new ShadowRootBoundary(root);
final childInjector = new DirectiveInjector(injector, null, null, null, null, null,
null, null, boundary);

expect(childInjector.getByKey(SHADOW_BOUNDARY_KEY)).toBe(boundary);
});

it('should return the shadow bounary of the parent injector', () {
final root = new dom.DivElement().createShadowRoot();
final boundary = new ShadowRootBoundary(root);
final parentInjector = new DirectiveInjector(injector, null, null, null, null, null,
null, null, boundary);
final childInjector = new DirectiveInjector(parentInjector, null, null, null, null, null,
null, null, null);

expect(childInjector.getByKey(SHADOW_BOUNDARY_KEY)).toBe(boundary);
});

it('should throw we cannot find a shadow boundary', () {
final childInjector = new DirectiveInjector(injector, null, null, null, null, null,
null, null, null);

expect(() => childInjector.getByKey(SHADOW_BOUNDARY_KEY)).toThrow("No provider found");
});
});

describe('error handling', () {
it('should throw circular dependency error', () {
addDirective(_TypeC0);
Expand Down
12 changes: 12 additions & 0 deletions test/core_dom/shadow_boundary_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@ main() {

expect(root).toHaveText(".style1{}adiv");
});

it("should not insert the same style element twice", () {
final root = new dom.DivElement().createShadowRoot();
final boundary = new ShadowRootBoundary(root);

final s = new dom.StyleElement()..text = ".style1{}";

boundary.insertStyleElements([s]);
boundary.insertStyleElements([s]);

expect(root).toHaveText(".style1{}");
});
});
});
}

0 comments on commit a3bc7d4

Please sign in to comment.