60
import {Component} from 'angular2/core';

@Component({
    selector: 'my-app',
    template: '<div></div>',
    styleUrls: [
        'http://example.com/external.css',
        'app/local.css'
    ]
})
export class AppComponent {}

The external.css does not load.

Is there any way to load the external CSS in an Angular 2 Component?

Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
CallMeLaNN
  • 8,328
  • 7
  • 59
  • 74

5 Answers5

107

See also https://angular.io/docs/ts/latest/guide/component-styles.html

View encapsulation

To allow external styles to affect the content of components you can change view encapsulation (that's what prevents styles to "bleed into" components).

@Component({
    selector: 'some-component',
    template: '<div></div>',
    styleUrls: [
        'http://example.com/external.css',
        'app/local.css'
    ], 
    encapsulation: ViewEncapsulation.None, 
})
export class SomeComponent {}

View encapsulation fulfills a purpose. The better way is to add styles directly to the component they should affect. ViewEncapsulation is set per component and may come handy in some situations.

"shadow piercing"

You can also use shadow piercing CSS combinator ::ng-deep (>>> and /deep/ are deprecated) to build selectors that cross component boundaries like

:host ::ng-deep .ng-invalid {
  border-bottom: solid 3px red;
}

which styles all tags with a class ng-invalid in the current component or any descendant with a red underline no matter if encapsulation is None or Emulated. It depends on browser support whether /deep/ works with Native (as far as I know this is not supported by any browser anymore).

Note

The shadow piercing CSS combinators are similar to the ones from the shadow DOM spec where they are deprecated since quite a while.

With the default ViewEncapsulation.Emulated Angulars own /deep/ and ::shadow implementation are used and they will work even when Chrome removes native support.

With ViewEncapsulation.Native Angular uses Chromes shadow DOM CSS combinators (only Chrome supported them at all anyway AFAIK). If Chrome finally removes them, then they won't work in Angular as well (again ViewEncapsulation.Native only).

Global styles

Styles added globally (index.html) don't consider component boundaries. Such styles are not rewritten by Angular2 and ViewEncapsulation.Emulated doesn't apply to them. Only if ViewEncapsulation.Native is set and the browser has native shadow DOM support, then global styles can't bleed in.

See also this related issue https://github.com/angular/angular/issues/5390

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 1
    P.S. When using encapsulation, a component selector is recommended to written into your Local.css to restrict the rules target. – Downhillski Jan 23 '16 at 12:58
  • @ZhenyangHua how can someone achieve that in terms of code?do you have an example? – Petros Kyriakou Feb 15 '16 at 19:17
  • 1
    Sorry, should've been more clear. "This" refers to your solution of setting `encapsulation` to none. I believe I'm having the same issue as OP in my Ng2 app, and external css files declared in `styleUrls` (and being served from a CDN in my case) are still not being loaded and applied to the component. No errors/warnings. Same calls work just fine with Angular1/jQuery so its not a CORS issue. Fun times. – filoxo May 10 '16 at 22:17
  • View encapsulation controls the behaviour of component styles bleeding *out* of the attached component and does not control external styles from bleeding *in* (they will always bleed in). – Michael Jun 15 '16 at 04:54
  • @Michael that's not true. Styles don't bleed in if `ViewEncapsulation.Emulated` is set. Only styles added to your index.html (which are not rewritten by Angular) bleed in. If you add HTML dynamically using `[innerHTML]="..."` then the HTML is also not rewritten by Angular, styles from outside will bleed in. – Günter Zöchbauer Jun 15 '16 at 05:04
  • 1
    @GünterZöchbauer Sorry, I should have clarified. By 'external' I mean external to the angular application (in index.html or equivalent) rather than external to the component. I guess we were referring to different things. – Michael Jun 15 '16 at 05:09
  • @Michael there are some limitations though. The emulated encapsulation is not perfect. I guess you are relating to selectors like `a b { ... }` where `a` only applies to elements that match in the current element but `b` also matches elements in child elements. I think this will be fixed eventually. – Günter Zöchbauer Jun 15 '16 at 05:10
  • @Michael the question is about styles added to `@Component({ styles: [...] })`. But you are definitely right about global styles. I added a section to my answer already. – Günter Zöchbauer Jun 15 '16 at 05:11
  • 2
    @GünterZöchbauer , this is not working https://plnkr.co/edit/fGvBAimMnjffp4CJnqu2?p=preview – Milad Dec 24 '16 at 01:08
  • Link to official angular documentation: https://angular.io/docs/ts/latest/guide/component-styles.html – koppor Apr 04 '17 at 17:14
14

First of all - The styles/styleUrls should only be used for any css rules that directly affect the style of the element from your template.

The reason your external.css doesn't get to applied to your component is because when you load these rules in external.css from styleUrls or styles, upon compiling, angular2 will append a unique component identifier like attribution selector to your original selectors.

For example, in your external.css, if there is a rule like div.container { /*some rules*/ }, it will become div.container[_ngcontent-cds-2] { /*some rules*/ }. So no matter how hard your try to force your rules become priority rules e.g. add !important tag, it is not going to work -- all selectors in your external.css have been restricted down one level to attribute selector, only the component element carries the same attribute. This is how angular2 restrict the styles to only current component.

Of course there is always a workaround.

Here is my solution -- I will add a external resource service, for all the js script, it will be using SystemJS to load either AMD or Globally, for all the css files, it will be using plain javascript to create a <link> element and append it to <head> element.

Here is a piece of my code for you consideration:

loadCSS(url) {
  // Create link
  let link = document.createElement('link');
  link.href = url;
  link.rel = 'stylesheet';
  link.type = 'text/css';
  
  let head = document.getElementsByTagName('head')[0];
  let links = head.getElementsByTagName('link');
  let style = head.getElementsByTagName('style')[0];
  
  // Check if the same style sheet has been loaded already.
  let isLoaded = false;  
  for (var i = 0; i < links.length; i++) {
    var node = links[i];
    if (node.href.indexOf(link.href) > -1) {
      isLoaded = true;
    }
  }
  if (isLoaded) return;
  head.insertBefore(link, style);
}
Downhillski
  • 2,555
  • 2
  • 27
  • 39
7

This could be late, hope this helps someone else. To use ViewEncapsulation, just use import { ViewEncapsulation } from '@angular/core';

Arun-
  • 846
  • 3
  • 22
  • 41
1

Better approach is already posted here : https://stackoverflow.com/a/36265072/5219412

You only have to add this in your component's declaration :

@Component({
    ...
    encapsulation: ViewEncapsulation.None,
})

As specified in the angular documentation : https://angular.io/guide/component-styles

Hope it helps.

Nils Renaud
  • 554
  • 4
  • 20
  • 1
    This is not good way. As commented above, a component should not handle global styles, not even with ViewEncapsulation.None. If component must affect anything outside of itself the service must be used. The reason is that we get a mess and conflicts if now we would have multiple CSS declarations on the same element – Hrvoje Golcic May 14 '18 at 06:58
-1

Lots of complicated answers here but I've always found that if I'm wanting to create some styling that affects more than one component it's in effect a global style, hence I add it to styles.css. This css file is special as it doesn't use the style encapsulation so affects every component.

This conceptually seems ok to me, just you have to use this sparingly else styles.css gets too big.

Rich

richardprocter
  • 125
  • 1
  • 2
  • You're not actually answering the question, just saying "don't do it this way, do it another way". The user clearly wants to load an external CSS. – Yserbius Oct 11 '19 at 15:25