Reputation: 11
In an aurelia project I have several components that import additional stylesheets, e.g. from semantic-ui. After leaving the components page, the stylesheet is still active and not removed. Is it possible to 'unload' the stylesheets?
Upvotes: 1
Views: 360
Reputation: 1293
I have found a plugin to resolve the issue: https://github.com/jbockle/aurelia-useable-style-loader
But for the latest Webpack webpack.config.js
should be a little bit different than in a plugin readme.
You should load .css
files this way:
use: [
{ loader: 'style-loader', options: { injectType: 'lazyStyleTag' } },
'css-loader'
]
Instead of this:
use: ['style-loader/useable', 'css-loader']
Upvotes: 0
Reputation: 8047
I submitted a PR to enable this as an opt-in, you can keep track of it here: https://github.com/aurelia/templating-resources/pull/344
A word of warning, this is untested and aurelia-internals-hacky.
What you could do is override the default CSSViewEngineHooks
and CSSResource
classes to keep track of the style elements it injects, and then add an beforeUnbind
hook to remove the styles again.. before unbind (right after detached)
Unfortunately the CSSResource
class is not exported from aurelia-templating-resources
so we need to go one layer deeper and overwrite the existing style loader plugins that returns instances of CSSResource
.
Here's how:
First we grab the code from aurelia-templating-resources/src/css-resource.js
, put it in our own src/css-resource.js/ts
and make a few tweaks to it (don't think too much of the size, it's just a copy-paste with a few small tweaks, annotated with comments):
import {ViewResources, resource, ViewCompileInstruction} from 'aurelia-templating';
import {Loader} from 'aurelia-loader';
import {Container} from 'aurelia-dependency-injection';
import {relativeToFile} from 'aurelia-path';
import {DOM, FEATURE} from 'aurelia-pal';
let cssUrlMatcher = /url\((?!['"]data)([^)]+)\)/gi;
function fixupCSSUrls(address, css) {
if (typeof css !== 'string') {
throw new Error(`Failed loading required CSS file: ${address}`);
}
return css.replace(cssUrlMatcher, (match, p1) => {
let quote = p1.charAt(0);
if (quote === '\'' || quote === '"') {
p1 = p1.substr(1, p1.length - 2);
}
return 'url(\'' + relativeToFile(p1, address) + '\')';
});
}
class CSSResource {
constructor(address: string) {
this.address = address;
this._scoped = null;
this._global = false;
this._alreadyGloballyInjected = false;
}
initialize(container: Container, target: Function): void {
this._scoped = new target(this);
}
register(registry: ViewResources, name?: string): void {
if (name === 'scoped') {
registry.registerViewEngineHooks(this._scoped);
} else {
this._global = true;
}
}
load(container: Container): Promise<CSSResource> {
return container.get(Loader)
.loadText(this.address)
.catch(err => null)
.then(text => {
text = fixupCSSUrls(this.address, text);
this._scoped.css = text;
if (this._global) {
this._alreadyGloballyInjected = true;
// DOM.injectStyles(text); <- replace this
// this is one of the two possible moments where the style is injected
// _scoped is the CSSViewEngineHooks instance, and we handle the removal there
this._scoped.styleNode = DOM.injectStyles(text);
}
});
}
}
class CSSViewEngineHooks {
constructor(owner: CSSResource) {
this.owner = owner;
this.css = null;
}
beforeCompile(content: DocumentFragment, resources: ViewResources, instruction: ViewCompileInstruction): void {
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._global && !this.owner._alreadyGloballyInjected) {
// DOM.injectStyles(this.css); <- replace this
// save a reference to the node so we can easily remove it later
this.styleNode = DOM.injectStyles(this.css);
this.owner._alreadyGloballyInjected = true;
}
}
// this is the hook we add, here we remove the node again
beforeUnbind(): void {
if (this._global && this.owner._alreadyGloballyInjected) {
DOM.removeNode(this.styleNode);
this.owner._alreadyGloballyInjected = false;
}
}
}
export function _createCSSResource(address: string): Function {
@resource(new CSSResource(address))
class ViewCSS extends CSSViewEngineHooks {}
return ViewCSS;
}
Then, in our main.ts/js
we do the same thing aurelia-templating-resources.js
does, but with our own version.
So we do this after the call to aurelia.use.standardConfiguration()
etc, to override the existing one
let viewEngine = config.container.get(ViewEngine);
let styleResourcePlugin = {
fetch(address) {
return { [address]: _createCSSResource(address) };
}
};
['.css', '.less', '.sass', '.scss', '.styl'].forEach(ext => viewEngine.addResourcePlugin(ext, styleResourcePlugin));
And that should pretty much do the trick.. :)
Upvotes: 1