Framework: Custom attribute causes custom element styles to be re-injected into head

Created on 24 Jul 2017  路  3Comments  路  Source: aurelia/framework

I'm submitting a bug report

From stack overflow by request of @AshleyGrant.

  • Library Version:

    • bootstrapper: ^2.0.1

    • framework: 1.0.8

Please tell us about your environment:

  • Operating System:
    Windows 10
  • Node Version:
    7.7.3

  • NPM Version:
    4.1.2

  • JSPM
    JSPM 0.17.0-beta.42

  • Browser:
    Chrome 59 (no other supported browser supports Shadow DOM)

  • Language:
    ESNext

Current behavior:
Shadow DOM scoped CSS includes are injected into the shadow root _and into <head>_.

Expected/desired behavior:
Shadow DOM scoped CSS includes are injected _only_ into the shadow root.


From Stack Overflow:
In my aurelia application, one of my custom elements panel (shadow DOM) includes another custom element widget (shadow DOM) that has a custom attribute own-attribute (template controller) on it. panel has a scoped stylesheet (<require from="./panel.css" as="scoped"></require>). While compiling, aurelia correctly injects ./panel.css into the shadow root of panel. However, in a separate step, while compiling own-context on widget within panel, aurelia injects ./panel.css into <head>, thus breaking the abstraction of custom element stylesheets and generally playing havoc with my app.

How do I fix this?

I tried making a plunker here but it doesn't work. I'm using ES2017 so that makes life more complicated.


This is an issue. HtmlBehaviorResource.compile()'s call to ViewResource.compile() does not pass a compile instruction, so the latter uses the default. Thus the CSS hooks don't get the message that they should be injecting into the shadow root. Not to mention this is double-injecting these styles for no good reason.

at eval (eval at injectStyles (http://example.com/jspm/npm/[email protected]/aurelia-pal-browser.js), <anonymous>:1:11)
at Object.injectStyles (http://example.com/jspm/npm/[email protected]/aurelia-pal-browser.js:434:27)
at ViewCSS.beforeCompile (http://example.com/jspm/npm/[email protected]/css-resource.js:105:25)
at ViewResources._invokeHook (http://example.com/jspm/npm/[email protected]/aurelia-templating.js:1281:25)
at ViewCompiler.compile (http://example.com/jspm/npm/[email protected]/aurelia-templating.js:2509:17)
at HtmlBehaviorResource.compile (http://example.com/jspm/npm/[email protected]/aurelia-templating.js:3972:46)
at ViewCompiler._compileElement (http://example.com/jspm/npm/[email protected]/aurelia-templating.js:2813:40)
at ViewCompiler._compileNode (http://example.com/jspm/npm/[email protected]/aurelia-templating.js:2543:23)
at ViewCompiler._compileNode (http://example.com/jspm/npm/[email protected]/aurelia-templating.js:2565:33)
at ViewCompiler.compile (http://example.com/jspm/npm/[email protected]/aurelia-templating.js:2512:12)

panel.js

import { useShadowDOM, customElement } from 'aurelia-templating';

export { PanelModule };

@useShadowDOM
@customElement('panel-elem')
class PanelModule {}

panel.html

<template>
    <require from="./panel.css" as="scoped"></require>

    <require from="lib:widget"></require>
    <require from="lib:own-context"></require>

    <widget-elem own-context>
        <content>
            some content
        </content>
    </widget-elem>
</template>

widget.js

import { useShadowDOM, customElement } from 'aurelia-templating';

export { WidgetModule };

@useShadowDOM
@customElement('widget-elem')
class WidgetModule {}

widget.html

<template>
    <require from="./widget.css" as="scoped"></require>

    <div id="thing">
        <slot></slot>
    </div
</template>

own-context.js

import { inject, templateController, BoundViewFactory, ViewSlot } from 'aurelia-framework';
import { createOverrideContext } from 'aurelia-binding';

// from https://github.com/aurelia/templating/issues/411
@templateController
@inject(BoundViewFactory, ViewSlot)
class OwnContextCustomAttribute {
    constructor(factory, slot) {
        this.factory = factory; 
        this.slot = slot;
    }

    bind(bindingContext, overrideContext) {
        let newContext = { };
        overrideContext = createOverrideContext(newContext, overrideContext); 

        if (!this.view) {
            this.view = this.factory.create(); 
            this.view.bind(newContext, overrideContext); 
            this.slot.add(this.view); 
        } else { 
            this.view.bind(newContext, overrideContext); 
        } 
    }

    unbind() {
        if (this.view)
            this.view.unbind();
    }
}
bug help wanted

Most helpful comment

I wish all issues were this thorough!

All 3 comments

From stack overflow:
I seem to have things working. I patched aurelia/templating-resources/css-resource.js:

 beforeCompile(content: DocumentFragment, resources: ViewResources, instruction: ViewCompileInstruction): void {
+  if (this.done) {
+      return
+  } else {
+      this.done = true
+  }
   if (instruction.targetShadowDOM) {
     DOM.injectStyles(this.css, content, true);
   } else if (FEATURE.scopedCSS) {
     let styleNode = DOM.injectStyles(this.css, content, true);
     styleNode.setAttribute('scoped', 'scoped');
   } else if (!this.owner._alreadyGloballyInjected) {
     DOM.injectStyles(this.css);
     this.owner._alreadyGloballyInjected = true;
   }
 }

I have been running with this patch for a week plus and nothing weird has happened.

I wish all issues were this thorough!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ghost picture ghost  路  4Comments

rdelhommer picture rdelhommer  路  4Comments

danfma picture danfma  路  5Comments

bsrdjan picture bsrdjan  路  5Comments

jfbaquerocelis picture jfbaquerocelis  路  3Comments