diff --git a/lib/core_dom/directive.dart b/lib/core_dom/directive.dart index 8738fe273..aa32724ce 100644 --- a/lib/core_dom/directive.dart +++ b/lib/core_dom/directive.dart @@ -22,7 +22,7 @@ class NodeAttrs { NodeAttrs(this.element); - operator [](String attrName) => element.attributes[attrName]; + operator [](String attrName) => element.getAttribute(attrName); void operator []=(String attrName, String value) { if (_mustacheAttrs.containsKey(attrName)) { @@ -31,7 +31,7 @@ class NodeAttrs { if (value == null) { element.attributes.remove(attrName); } else { - element.attributes[attrName] = value; + element.setAttribute(attrName, value); } if (_observers != null && _observers.containsKey(attrName)) { @@ -86,9 +86,18 @@ class NodeAttrs { * ShadowRoot is ready. */ class TemplateLoader { - final async.Future template; + async.Future _template; + List _futures; + final dom.Node _shadowRoot; - TemplateLoader(this.template); + TemplateLoader(this._shadowRoot, this._futures); + + async.Future get template { + if (_template == null) { + _template = async.Future.wait(_futures).then((_) => _shadowRoot); + } + return _template; + } } class _MustacheAttr { diff --git a/lib/core_dom/shadow_boundary.dart b/lib/core_dom/shadow_boundary.dart index 3512998e0..a7e937b4e 100644 --- a/lib/core_dom/shadow_boundary.dart +++ b/lib/core_dom/shadow_boundary.dart @@ -7,8 +7,29 @@ part of angular.core.dom_internal; */ abstract class ShadowBoundary { Set _insertedStyles; + final dom.Node root; + dom.StyleElement _lastStyleElement; + + ShadowBoundary(this.root); - void insertStyleElements(List elements); + void insertStyleElements(List elements, {bool prepend: false}) { + if (elements.isEmpty) return; + + final newStyles = _newStyles(elements); + final cloned = newStyles.map((el) => el.clone(true)); + + cloned.forEach((style) { + if (_lastStyleElement != null && !prepend) { + _lastStyleElement = root.insertBefore(style, _lastStyleElement.nextNode); + } else if (root.hasChildNodes()) { + _lastStyleElement = root.insertBefore(style, root.firstChild); + } else { + _lastStyleElement = root.append(style); + } + }); + + _addInsertedStyles(newStyles); + } Iterable _newStyles(Iterable elements) { if (_insertedStyles == null) return elements; @@ -23,37 +44,15 @@ abstract class ShadowBoundary { @Injectable() class DefaultShadowBoundary extends ShadowBoundary { - void insertStyleElements(List elements) { - final newStyles = _newStyles(elements); - final cloned = newStyles.map((el) => el.clone(true)); - dom.document.head.nodes.addAll(cloned); - _addInsertedStyles(newStyles); - } + DefaultShadowBoundary() + : super(dom.document.head); + + DefaultShadowBoundary.custom(dom.Node node) + : super(node); } @Injectable() class ShadowRootBoundary extends ShadowBoundary { - final dom.ShadowRoot shadowRoot; - dom.StyleElement _lastStyleElement; - - ShadowRootBoundary(this.shadowRoot); - - void insertStyleElements(List elements) { - if (elements.isEmpty) return; - - final newStyles = _newStyles(elements); - final cloned = newStyles.map((el) => el.clone(true)); - - cloned.forEach((style) { - if (_lastStyleElement != null) { - _lastStyleElement = shadowRoot.insertBefore(style, _lastStyleElement.nextNode); - } else if (shadowRoot.hasChildNodes()) { - _lastStyleElement = shadowRoot.insertBefore(style, shadowRoot.firstChild); - } else { - _lastStyleElement = shadowRoot.append(style); - } - }); - - _addInsertedStyles(newStyles); - } + ShadowRootBoundary(dom.ShadowRoot shadowRoot) + : super(shadowRoot); } diff --git a/lib/core_dom/shadow_dom_component_factory.dart b/lib/core_dom/shadow_dom_component_factory.dart index 148f15dc7..d548b5469 100644 --- a/lib/core_dom/shadow_dom_component_factory.dart +++ b/lib/core_dom/shadow_dom_component_factory.dart @@ -11,10 +11,9 @@ abstract class BoundComponentFactory { List get callArgs; Function call(dom.Element element); - static async.Future _viewFuture( + static async.Future _viewFactoryFuture( Component component, ViewCache viewCache, DirectiveMap directives, TypeToUriMapper uriMapper, ResourceUrlResolver resourceResolver, Type type) { - if (component.template != null) { // TODO(chirayu): Replace this line with // var baseUri = uriMapper.uriForType(type); @@ -77,71 +76,95 @@ class BoundShadowDomComponentFactory implements BoundComponentFactory { Component get _component => _ref.annotation as Component; String _tag; - async.Future> _styleElementsFuture; - async.Future _viewFuture; + async.Future> _styleElementsFuture; + List _styleElements; + async.Future _shadowViewFactoryFuture; + ViewFactory _shadowViewFactory; BoundShadowDomComponentFactory(this._componentFactory, this._ref, DirectiveMap directives, this._injector) { _tag = _ref.annotation.selector.toLowerCase(); - _styleElementsFuture = _componentFactory.cssLoader(_tag, _component.cssUrls, type: _ref.type); + _styleElementsFuture = _componentFactory.cssLoader(_tag, _component.cssUrls, type: _ref.type) + .then((styleElements) => _styleElements = styleElements); final viewCache = new ShimmingViewCache(_componentFactory.viewCache, _tag, _componentFactory.platformShim); - _viewFuture = BoundComponentFactory._viewFuture(_component, viewCache, directives, - _componentFactory.uriMapper, _componentFactory.resourceResolver, _ref.type); + + _shadowViewFactoryFuture = BoundComponentFactory._viewFactoryFuture(_component, + viewCache, directives, _componentFactory.uriMapper, + _componentFactory.resourceResolver, _ref.type); + + if (_shadowViewFactoryFuture != null) { + _shadowViewFactoryFuture.then((viewFactory) => _shadowViewFactory = viewFactory); + } } List get callArgs => _CALL_ARGS; static final _CALL_ARGS = [DIRECTIVE_INJECTOR_KEY, SCOPE_KEY, VIEW_KEY, NG_BASE_CSS_KEY, SHADOW_BOUNDARY_KEY]; + Function call(dom.Element element) { return (DirectiveInjector injector, Scope scope, View view, NgBaseCss baseCss, ShadowBoundary parentShadowBoundary) { var s = traceEnter(View_createComponent); try { - var shadowDom = element.createShadowRoot(); + var shadowRoot = element.createShadowRoot(); var shadowBoundary; if (_componentFactory.platformShim.shimRequired) { shadowBoundary = parentShadowBoundary; } else { - shadowBoundary = new ShadowRootBoundary(shadowDom); + shadowBoundary = new ShadowRootBoundary(shadowRoot); } - //_styleFuture(cssUrl, resolveUri: false) var shadowScope = scope.createChild(new HashMap()); // Isolate - ComponentDirectiveInjector shadowInjector; - - final baseUrls = (_component.useNgBaseCss) ? baseCss.urls : []; - final baseUrlsFuture = _componentFactory.cssLoader(_tag, baseUrls); - final cssFuture = mergeFutures(baseUrlsFuture, _styleElementsFuture); - - async.Future initShadowDom(_) { - if (_viewFuture == null) return new async.Future.value(shadowDom); - return _viewFuture.then((ViewFactory viewFactory) { - if (shadowScope.isAttached) { - shadowDom.nodes.addAll( - viewFactory.call(shadowInjector.scope, shadowInjector).nodes); - } - return shadowDom; - }); - } - - TemplateLoader templateLoader = new TemplateLoader( - cssFuture.then(shadowBoundary.insertStyleElements).then(initShadowDom)); + List futures = []; + TemplateLoader templateLoader = new TemplateLoader(shadowRoot, futures); var probe; var eventHandler = new ShadowRootEventHandler( - shadowDom, injector.getByKey(EXPANDO_KEY), injector.getByKey(EXCEPTION_HANDLER_KEY)); - shadowInjector = new ComponentDirectiveInjector(injector, _injector, eventHandler, shadowScope, - templateLoader, shadowDom, null, view, shadowBoundary); + shadowRoot, injector.getByKey(EXPANDO_KEY), injector.getByKey(EXCEPTION_HANDLER_KEY)); + final shadowInjector = new ComponentDirectiveInjector(injector, _injector, eventHandler, shadowScope, + templateLoader, shadowRoot, null, view, shadowBoundary); + shadowInjector.bindByKey(_ref.typeKey, _ref.factory, _ref.paramKeys, _ref.annotation.visibility); + if (_component.useNgBaseCss && baseCss.urls.isNotEmpty) { + if (baseCss.styles == null) { + final f = _componentFactory.cssLoader(_tag, baseCss.urls).then((cssList) { + baseCss.styles = cssList; + shadowBoundary.insertStyleElements(cssList, prepend: true); + }); + futures.add(f); + } else { + shadowBoundary.insertStyleElements(baseCss.styles, prepend: true); + } + } - shadowInjector.bindByKey(_ref.typeKey, _ref.factory, _ref.paramKeys, _ref.annotation.visibility); + if (_styleElementsFuture != null) { + if (_styleElements == null) { + final f = _styleElementsFuture.then(shadowBoundary.insertStyleElements); + futures.add(f); + } else { + shadowBoundary.insertStyleElements(_styleElements); + } + } + + if (_shadowViewFactoryFuture != null) { + if (_shadowViewFactory == null) { + final f = _shadowViewFactoryFuture.then((ViewFactory viewFactory) => + _insertView(viewFactory, shadowRoot, shadowScope, shadowInjector)); + futures.add(f); + } else { + final f = new Future.microtask(() { + _insertView(_shadowViewFactory, shadowRoot, shadowScope, shadowInjector); + }); + futures.add(f); + } + } if (_componentFactory.config.elementProbeEnabled) { - probe = _componentFactory.expando[shadowDom] = shadowInjector.elementProbe; - shadowScope.on(ScopeEvent.DESTROY).listen((ScopeEvent) => _componentFactory.expando[shadowDom] = null); + ElementProbe probe = _componentFactory.expando[shadowRoot] = shadowInjector.elementProbe; + shadowScope.on(ScopeEvent.DESTROY).listen((ScopeEvent) => _componentFactory.expando[shadowRoot] = null); } var controller = shadowInjector.getByKey(_ref.typeKey); @@ -155,6 +178,16 @@ class BoundShadowDomComponentFactory implements BoundComponentFactory { } }; } + + dom.Node _insertView(ViewFactory viewFactory, + dom.ShadowRoot shadowRoot, + Scope shadowScope, + ComponentDirectiveInjector shadowInjector) { + if (shadowScope.isAttached) { + shadowRoot.nodes.addAll( + viewFactory.call(shadowInjector.scope, shadowInjector).nodes); + } + } } @Injectable() diff --git a/lib/core_dom/transcluding_component_factory.dart b/lib/core_dom/transcluding_component_factory.dart index 551a66765..63d18a194 100644 --- a/lib/core_dom/transcluding_component_factory.dart +++ b/lib/core_dom/transcluding_component_factory.dart @@ -34,25 +34,30 @@ class BoundTranscludingComponentFactory implements BoundComponentFactory { String _tag; async.Future> _styleElementsFuture; + List _styleElements; Component get _component => _ref.annotation as Component; - async.Future _viewFuture; + async.Future _viewFactoryFuture; + ViewFactory _viewFactory; BoundTranscludingComponentFactory(this._f, this._ref, this._directives, this._injector) { - _viewFuture = BoundComponentFactory._viewFuture( + _tag = _ref.annotation.selector.toLowerCase(); + _styleElementsFuture = _f.cssLoader(_tag, _component.cssUrls, type: _ref.type) + .then((styleElements) => _styleElements = styleElements); + + final viewCache = new ShimmingViewCache(_f.viewCache, _tag, _f.platformShim); + + _viewFactoryFuture = BoundComponentFactory._viewFactoryFuture( _component, - _f.viewCache, + viewCache, _directives, _f.uriMapper, _f.resourceResolver, _ref.type); - - _tag = _ref.annotation.selector.toLowerCase(); - _styleElementsFuture = _f.cssLoader(_tag, _component.cssUrls, type: _ref.type); - final viewCache = new ShimmingViewCache(_f.viewCache, _tag, _f.platformShim); - _viewFuture = BoundComponentFactory._viewFuture(_component, viewCache, _directives, - _f.uriMapper, _f.resourceResolver, _ref.type); + if (_viewFactoryFuture != null) { + _viewFactoryFuture.then((viewFactory) => _viewFactory = viewFactory); + } } List get callArgs => _CALL_ARGS; @@ -60,6 +65,7 @@ class BoundTranscludingComponentFactory implements BoundComponentFactory { VIEW_CACHE_KEY, HTTP_KEY, TEMPLATE_CACHE_KEY, DIRECTIVE_MAP_KEY, NG_BASE_CSS_KEY, EVENT_HANDLER_KEY, SHADOW_BOUNDARY_KEY]; + Function call(dom.Node node) { var element = node as dom.Element; return (DirectiveInjector injector, Scope scope, View view, @@ -67,56 +73,60 @@ class BoundTranscludingComponentFactory implements BoundComponentFactory { DirectiveMap directives, NgBaseCss baseCss, EventHandler eventHandler, ShadowBoundary shadowBoundary) { - DirectiveInjector childInjector; - var childInjectorCompleter; // Used if the ViewFuture is available before the childInjector. - - var component = _component; final shadowRoot = new EmulatedShadowRoot(element); - var lightDom = new LightDom(element, scope)..pullNodes(); + final lightDom = new LightDom(element, scope)..pullNodes(); - final baseUrls = (_component.useNgBaseCss) ? baseCss.urls : []; - final baseUrlsFuture = _f.cssLoader(_tag, baseUrls); - final cssFuture = mergeFutures(baseUrlsFuture, _styleElementsFuture); + final shadowScope = scope.createChild(new HashMap()); + List futures = []; + TemplateLoader templateLoader = new TemplateLoader(shadowRoot, futures); - initShadowDom(_) { - if (_viewFuture != null) { - return _viewFuture.then((ViewFactory viewFactory) { - lightDom.clearComponentElement(); - if (childInjector != null) { - lightDom.shadowDomView = viewFactory.call(childInjector.scope, childInjector); - return shadowRoot; - } else { - childInjectorCompleter = new async.Completer(); - return childInjectorCompleter.future.then((childInjector) { - lightDom.shadowDomView = viewFactory.call(childInjector.scope, childInjector); - return shadowRoot; - }); - } + final childInjector = new ComponentDirectiveInjector(injector, this._injector, + eventHandler, shadowScope, templateLoader, shadowRoot, lightDom, view); + childInjector.bindByKey(_ref.typeKey, _ref.factory, _ref.paramKeys, _ref.annotation.visibility); + + if (_component.useNgBaseCss && baseCss.urls.isNotEmpty) { + if (baseCss.styles == null) { + final f = _f.cssLoader(_tag, baseCss.urls).then((cssList) { + baseCss.styles = cssList; + shadowBoundary.insertStyleElements(cssList, prepend: true); }); + futures.add(f); } else { - return new async.Future.microtask(lightDom.clearComponentElement); + shadowBoundary.insertStyleElements(baseCss.styles, prepend: true); } } - TemplateLoader templateLoader = new TemplateLoader( - cssFuture.then(shadowBoundary.insertStyleElements).then(initShadowDom)); - - Scope shadowScope = scope.createChild(new HashMap()); - - childInjector = new ComponentDirectiveInjector(injector, this._injector, - eventHandler, shadowScope, templateLoader, shadowRoot, lightDom, view); - - childInjector.bindByKey(_ref.typeKey, _ref.factory, _ref.paramKeys, _ref.annotation.visibility); + if (_styleElementsFuture != null) { + if (_styleElements == null) { + final f = _styleElementsFuture.then(shadowBoundary.insertStyleElements); + futures.add(f); + } else { + shadowBoundary.insertStyleElements(_styleElements); + } + } - if (childInjectorCompleter != null) { - childInjectorCompleter.complete(childInjector); + if (_viewFactoryFuture != null) { + if (_viewFactory == null) { + final f = _viewFactoryFuture.then((ViewFactory viewFactory) { + lightDom.clearComponentElement(); + lightDom.shadowDomView = viewFactory.call(childInjector.scope, childInjector); + }); + futures.add(f); + } else { + final f = new Future.microtask(() { + lightDom.clearComponentElement(); + lightDom.shadowDomView = _viewFactory.call(childInjector.scope, childInjector); + }); + futures.add(f); + } } var controller = childInjector.getByKey(_ref.typeKey); - shadowScope.context[component.publishAs] = controller; + shadowScope.context[_component.publishAs] = controller; if (controller is ScopeAware) controller.scope = shadowScope; BoundComponentFactory._setupOnShadowDomAttach(controller, templateLoader, shadowScope); + return controller; }; } -} +} \ No newline at end of file diff --git a/lib/core_dom/view_factory.dart b/lib/core_dom/view_factory.dart index c85dee3ce..740bf0df7 100644 --- a/lib/core_dom/view_factory.dart +++ b/lib/core_dom/view_factory.dart @@ -84,10 +84,12 @@ class ViewFactory implements Function { } elementInjectors[elementBinderIndex] = elementInjector; - if (tagged.textBinders != null) { - for (var k = 0; k < tagged.textBinders.length; k++) { - TaggedTextBinder taggedText = tagged.textBinders[k]; - var childNode = boundNode.childNodes[taggedText.offsetIndex]; + var textBinders = tagged.textBinders; + if (textBinders != null && textBinders.length > 0) { + var childNodes = boundNode.childNodes; + for (var k = 0; k < textBinders.length; k++) { + TaggedTextBinder taggedText = textBinders[k]; + var childNode = childNodes[taggedText.offsetIndex]; taggedText.binder.bind(view, scope, elementInjector, childNode); } } @@ -102,15 +104,6 @@ class ViewFactory implements Function { dom.Node node = nodeList[i]; NodeLinkingInfo linkingInfo = nodeLinkingInfos[i]; - // if node isn't attached to the DOM, create a parent for it. - var parentNode = node.parentNode; - var fakeParent = false; - if (parentNode == null) { - fakeParent = true; - parentNode = new dom.DivElement(); - parentNode.append(node); - } - if (linkingInfo.isElement) { if (linkingInfo.containsNgBinding) { var tagged = elementBinders[elementBinderIndex]; @@ -136,11 +129,6 @@ class ViewFactory implements Function { } elementBinderIndex++; } - - if (fakeParent) { - // extract the node from the parentNode. - nodeList[i] = parentNode.nodes[0]; - } } return view; } diff --git a/lib/core_dom/web_platform.dart b/lib/core_dom/web_platform.dart index 9a97276da..4b173d0b4 100644 --- a/lib/core_dom/web_platform.dart +++ b/lib/core_dom/web_platform.dart @@ -42,7 +42,7 @@ class WebPlatform { // // TODO Remove the try-catch once https://github.com/angular/angular.dart/issues/1189 is fixed. try { - root.querySelectorAll("*").forEach((n) => n.attributes[selector] = ""); + root.querySelectorAll("*").forEach((dom.Element n) => n.setAttribute(selector, "")); } catch (e, s) { print("WARNING: Failed to set up Shadow DOM shim for $selector.\n$e\n$s"); } @@ -72,6 +72,7 @@ class PlatformViewCache implements ViewCache { if (selector != null && selector != "" && platform.shadowDomShimRequired) { // By adding a comment with the tag name we ensure the template html is unique per selector // name when used as a key in the view factory cache. + //TODO(misko): This will always be miss, since we never put it in cache under such key. viewFactory = viewFactoryCache.get("$html"); } else { viewFactory = viewFactoryCache.get(html); diff --git a/lib/directive/ng_base_css.dart b/lib/directive/ng_base_css.dart index 74e41406d..34b9eade6 100644 --- a/lib/directive/ng_base_css.dart +++ b/lib/directive/ng_base_css.dart @@ -12,10 +12,14 @@ part of angular.directive; selector: '[ng-base-css]', visibility: Visibility.CHILDREN) class NgBaseCss { + List styles; List _urls = const []; @NgAttr('ng-base-css') - set urls(v) => _urls = v is List ? v : [v]; + set urls(v) { + _urls = v is List ? v : [v]; + styles = null; + } List get urls => _urls; } diff --git a/lib/ng_tracing_scopes.dart b/lib/ng_tracing_scopes.dart index ba70c0861..ba77b27e3 100644 --- a/lib/ng_tracing_scopes.dart +++ b/lib/ng_tracing_scopes.dart @@ -171,6 +171,13 @@ final View_create = traceCreateScope('View#create(ascii html)'); */ final View_createComponent = traceCreateScope('View#createComponent()'); +/** + * Name: `View#styles()` + * + * Designates where styles are inserted into component. + */ +final View_styles = traceCreateScope('View#styles()'); + /** * Name: `Directive#create(ascii name)` * diff --git a/test/core_dom/compiler_spec.dart b/test/core_dom/compiler_spec.dart index b8e8c5048..90c89440d 100644 --- a/test/core_dom/compiler_spec.dart +++ b/test/core_dom/compiler_spec.dart @@ -583,8 +583,8 @@ void main() { it('should not pass null to a inner directives', async((Logger logger) { _.compile('
' - '' - '
'); + '' + ''); _.rootScope.context['nn'] = [1]; _.rootScope.apply(); @@ -1041,7 +1041,7 @@ void main() { })); /* - This test is dissabled becouse I (misko) thinks it has no real use case. It is easier + This test is disabled because I (misko) thinks it has no real use case. It is easier to understand in terms of ng-repeat @@ -1055,7 +1055,7 @@ void main() { the DirectChild between tabs and pane. It is not clear to me (misko) that there is a use case for getting hold of the - tranrscluding directive such a ng-repeat. + transcluding directive such a ng-repeat. */ xit('should reuse controllers for transclusions', async((Logger log) { _.compile('
view
'); diff --git a/test/core_dom/default_platform_shim_spec.dart b/test/core_dom/default_platform_shim_spec.dart index 206581f7f..cc6dbc8e4 100644 --- a/test/core_dom/default_platform_shim_spec.dart +++ b/test/core_dom/default_platform_shim_spec.dart @@ -50,8 +50,8 @@ main() { microLeap(); backend - ..flushGET('${TEST_SERVER_BASE_PREFIX}test/core_dom/template.html').respond(200, 'foo') - ..flushGET('${TEST_SERVER_BASE_PREFIX}test/core_dom/style.css').respond(200, 'span { background-color: red; }'); + ..flushGET('${TEST_SERVER_BASE_PREFIX}test/core_dom/style.css').respond(200, 'span { background-color: red; }') + ..flushGET('${TEST_SERVER_BASE_PREFIX}test/core_dom/template.html').respond(200, 'foo'); microLeap(); try { diff --git a/test/core_dom/shadow_boundary_spec.dart b/test/core_dom/shadow_boundary_spec.dart index 6073a9aeb..084eaf6b4 100644 --- a/test/core_dom/shadow_boundary_spec.dart +++ b/test/core_dom/shadow_boundary_spec.dart @@ -5,44 +5,66 @@ import 'dart:html' as dom; main() { describe('ShadowBoundary', () { - describe("ShadowRootBoundary", () { - it("should insert style elements in order", () { - final root = new dom.DivElement().createShadowRoot(); - final boundary = new ShadowRootBoundary(root); + forShadowBoundary(String name, {boundaryFactory, rootFactory}) { + describe(name, () { + ShadowBoundary boundary; + var root; - final s1 = new dom.StyleElement()..text = ".style1{}"; - final s2 = new dom.StyleElement()..text = ".style2{}"; - final s3 = new dom.StyleElement()..text = ".style3{}"; + beforeEach(() { + root = rootFactory(); + boundary = boundaryFactory(root); + }); - boundary.insertStyleElements([s1,s2]); - boundary.insertStyleElements([s3]); + it("should insert style elements in order", () { + final s1 = new dom.StyleElement()..text = ".style1{}"; + final s2 = new dom.StyleElement()..text = ".style2{}"; + final s3 = new dom.StyleElement()..text = ".style3{}"; - expect(root).toHaveText(".style1{}.style2{}.style3{}"); - }); + boundary.insertStyleElements([s1,s2]); + boundary.insertStyleElements([s3]); - it("should insert style elements before other content", () { - final root = new dom.DivElement().createShadowRoot(); - final boundary = new ShadowRootBoundary(root); + expect(root).toHaveText(".style1{}.style2{}.style3{}"); + }); - root.appendHtml("
adiv
"); - final s = new dom.StyleElement()..text = ".style1{}"; + it("should prepend style elements before other style elements", () { + final s1 = new dom.StyleElement()..text = ".style1{}"; + final s2 = new dom.StyleElement()..text = ".style2{}"; + final s3 = new dom.StyleElement()..text = ".style3{}"; - boundary.insertStyleElements([s]); + boundary.insertStyleElements([s1,s2]); + boundary.insertStyleElements([s3], prepend: true); - expect(root).toHaveText(".style1{}adiv"); - }); + expect(root).toHaveText(".style3{}.style1{}.style2{}"); + }); + + it("should insert style elements before other content", () { + root.appendHtml("
adiv
"); + final s = new dom.StyleElement()..text = ".style1{}"; - it("should not insert the same style element twice", () { - final root = new dom.DivElement().createShadowRoot(); - final boundary = new ShadowRootBoundary(root); + boundary.insertStyleElements([s]); - final s = new dom.StyleElement()..text = ".style1{}"; + expect(root).toHaveText(".style1{}adiv"); + }); - boundary.insertStyleElements([s]); - boundary.insertStyleElements([s]); + it("should not insert the same style element twice", () { + final s = new dom.StyleElement()..text = ".style1{}"; - expect(root).toHaveText(".style1{}"); + boundary.insertStyleElements([s]); + boundary.insertStyleElements([s]); + + expect(root).toHaveText(".style1{}"); + }); }); - }); + } + + forShadowBoundary("ShadowRootBoundary", + rootFactory: () => new dom.DivElement().createShadowRoot(), + boundaryFactory: (root) => new ShadowRootBoundary(root) + ); + + forShadowBoundary("DefaultShadowBounary", + rootFactory: () => new dom.DivElement(), + boundaryFactory: (root) => new DefaultShadowBoundary.custom(root) + ); }); } \ No newline at end of file diff --git a/test/directive/ng_base_css_spec.dart b/test/directive/ng_base_css_spec.dart index b210eb6bd..b64f416eb 100644 --- a/test/directive/ng_base_css_spec.dart +++ b/test/directive/ng_base_css_spec.dart @@ -23,19 +23,33 @@ main() => describe('NgBaseCss', () { ..bind(_NoBaseCssComponent); }); - it('should load css urls from ng-base-css', async((TestBed _, MockHttpBackend backend) { - var element = e('
ignore
'); - _.compile(element); + it('should load css urls from ng-base-css', async((TestBed _, MockHttpBackend backend, + DirectiveMap directiveMap) { + NgBaseCss ngBaseCss = new NgBaseCss(); + ngBaseCss.urls = 'base.css'; + DirectiveInjector directiveInjector = new DirectiveInjector( + null, _.injector, null, null, null, null, null); + directiveInjector.bind(NgBaseCss, toValue: ngBaseCss, visibility: Visibility.CHILDREN); + var elements = es('
ignore
'); + ViewFactory viewFactory = _.compiler(elements, directiveMap); + View view = viewFactory.call(_.rootScope, directiveInjector); + microLeap(); backend - ..flushGET('${TEST_SERVER_BASE_PREFIX}test/directive/simple.css').respond(200, '.simple{}') - ..flushGET('${TEST_SERVER_BASE_PREFIX}test/directive/simple.html').respond(200, '
Simple!
') - ..flushGET('base.css').respond(200, '.base{}'); + ..flushGET('${TEST_SERVER_BASE_PREFIX}test/directive/simple.css').respond(200, '.simple{}') + ..flushGET('${TEST_SERVER_BASE_PREFIX}test/directive/simple.html').respond(200, '
Simple!
') + ..flushGET('base.css').respond(200, '.base{}'); microLeap(); - expect(element.children[0].shadowRoot).toHaveHtml( - '
Simple!
' - ); + expect((view.nodes[0].firstChild as Element).shadowRoot).toHaveHtml( + '
Simple!
'); + expect(ngBaseCss.styles.first.innerHtml).toEqual('.base{}'); + + // Now styles are loaded sync + view = viewFactory.call(_.rootScope, directiveInjector); + expect((view.nodes[0].firstChild as Element).shadowRoot).toHaveHtml( + ''); + })); it('ng-base-css should overwrite parent ng-base-csses', async((TestBed _, MockHttpBackend backend) { @@ -43,9 +57,9 @@ main() => describe('NgBaseCss', () { _.compile(element); backend - ..flushGET('${TEST_SERVER_BASE_PREFIX}test/directive/simple.css').respond(200, '.simple{}') - ..flushGET('${TEST_SERVER_BASE_PREFIX}test/directive/simple.html').respond(200, '
Simple!
') - ..flushGET('base.css').respond(200, '.base{}'); + ..flushGET('${TEST_SERVER_BASE_PREFIX}test/directive/simple.css').respond(200, '.simple{}') + ..flushGET('${TEST_SERVER_BASE_PREFIX}test/directive/simple.html').respond(200, '
Simple!
') + ..flushGET('base.css').respond(200, '.base{}'); microLeap(); expect(element.children[0].children[0].shadowRoot).toHaveHtml( @@ -116,14 +130,14 @@ main() => describe('NgBaseCss', () { microLeap(); backend - ..flushGET('${TEST_SERVER_BASE_PREFIX}test/directive/simple.html').respond(200, '
Simple!
') ..flushGET('${TEST_SERVER_BASE_PREFIX}test/directive/simple.css').respond(200, '.simple{}') + ..flushGET('${TEST_SERVER_BASE_PREFIX}test/directive/simple.html').respond(200, '
Simple!
') ..flushGET('base.css').respond(200, '.base{}'); microLeap(); final styleTags = document.head.querySelectorAll("style"); - expect(styleTags[styleTags.length - 2]).toHaveText(".base{}"); - expect(styleTags.last).toHaveText(".simple{}"); + expect(styleTags[0]).toHaveText(".base{}"); + expect(styleTags[1]).toHaveText(".simple{}"); })); it('should respect useNgBaseCss', async((TestBed _, MockHttpBackend backend) { @@ -131,8 +145,8 @@ main() => describe('NgBaseCss', () { microLeap(); backend - ..flushGET('${TEST_SERVER_BASE_PREFIX}test/directive/simple.html').respond(200, '
Simple!
') - ..flushGET('${TEST_SERVER_BASE_PREFIX}test/directive/simple.css').respond(200, '.simple{}'); + ..flushGET('${TEST_SERVER_BASE_PREFIX}test/directive/simple.css').respond(200, '.simple{}') + ..flushGET('${TEST_SERVER_BASE_PREFIX}test/directive/simple.html').respond(200, '
Simple!
'); microLeap(); expect(document.head.text).not.toContain(".base{}");