320 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			320 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
block includes
 | 
						|
  include ../_util-fns
 | 
						|
 | 
						|
:marked
 | 
						|
  Angular applications are styled with regular CSS. That means we can apply
 | 
						|
  everything we know about CSS stylesheets, selectors, rules, and media queries
 | 
						|
  to our Angular applications directly.
 | 
						|
  
 | 
						|
  On top of this, Angular has the ability to bundle *component styles*
 | 
						|
  with our components enabling a more modular design than regular stylesheets.
 | 
						|
  
 | 
						|
  In this chapter we learn how to load and apply these *component styles*.
 | 
						|
  
 | 
						|
  # Table Of Contents
 | 
						|
 | 
						|
  * [Using Component Styles](#using-component-styles)
 | 
						|
  * [Special selectors](#special-selectors)
 | 
						|
  * [Loading Styles into Components](#loading-styles)
 | 
						|
  * [Controlling View Encapsulation: Emulated, Native, and None](#view-encapsulation)
 | 
						|
  * [Appendix 1: Inspecting the generated runtime component styles](#inspect-generated-css)
 | 
						|
  * [Appendix 2: Loading Styles with Relative URLs](#relative-urls)
 | 
						|
 | 
						|
  Run the <live-example></live-example> of the code shown in this chapter.
 | 
						|
 | 
						|
.l-main-section
 | 
						|
:marked
 | 
						|
  ## Using Component Styles
 | 
						|
  
 | 
						|
  For every Angular component we write, we may define not only an HTML template,
 | 
						|
  but also the CSS styles that go with that template, 
 | 
						|
  specifying any selectors, rules, and media queries that we need.
 | 
						|
 | 
						|
  One way to do this is to set the `styles` property in the component metadata.
 | 
						|
  The `styles` property takes #{_an} #{_array} of strings that contain CSS code.
 | 
						|
  Usually we give it one string as in this example:
 | 
						|
  
 | 
						|
+makeExample('component-styles/ts/app/hero-app.component.ts')(format='.')
 | 
						|
 | 
						|
:marked
 | 
						|
  Component styles differ from traditional, global styles in a couple of ways.
 | 
						|
  
 | 
						|
  Firstly, the selectors we put into a component's styles *only apply within the template
 | 
						|
  of that component*. The `h1` selector in the example above only applies to the `<h1>` tag
 | 
						|
  in the template of `HeroAppComponent`. Any `<h1>` elements elsewhere in
 | 
						|
  the application are unaffected.
 | 
						|
  
 | 
						|
  This is a big improvement in modularity compared to how CSS traditionally works:
 | 
						|
  
 | 
						|
  1. We can use the CSS class names and selectors that make the most sense in the context of each component. 
 | 
						|
     
 | 
						|
  1. Class names and selectors are local to the component and won't collide with 
 | 
						|
  classes and selectors used elsewhere in the application.
 | 
						|
     
 | 
						|
  1. Our component's styles *cannot* be changed by changes to styles elsewhere in the application.
 | 
						|
     
 | 
						|
  1. We can co-locate the CSS code of each component with the TypeScript and HTML code of the component,
 | 
						|
     which leads to a neat and tidy project structure.
 | 
						|
     
 | 
						|
  1. We can change or remove component CSS code in the future without trawling through the
 | 
						|
     whole application to see where else it may have been used. We just look at the component we're in.
 | 
						|
 | 
						|
a(id="special-selectors")
 | 
						|
.l-main-section
 | 
						|
:marked
 | 
						|
  ## Special selectors
 | 
						|
  
 | 
						|
  Component styles have a few special *selectors* from the world of 
 | 
						|
  [shadow DOM style scoping](https://www.w3.org/TR/css-scoping-1):
 | 
						|
  
 | 
						|
  ### :host
 | 
						|
 | 
						|
  Use the `:host` pseudo-class selector to target styles in the element that *hosts* the component (as opposed to
 | 
						|
  targeting elements *inside* the component's template):
 | 
						|
  
 | 
						|
+makeExample('component-styles/ts/app/hero-details.component.css', 'host')(format='.')
 | 
						|
 | 
						|
:marked
 | 
						|
  This is the *only* way we can target the host element. We cannot reach
 | 
						|
  it from inside the component with other selectors, because it is not part of the
 | 
						|
  component's own template. It is in a parent component's template.
 | 
						|
  
 | 
						|
  Use the *function form* to apply host styles conditionally by 
 | 
						|
  including another selector inside parentheses after `:host`.
 | 
						|
  
 | 
						|
  In the next example we target the host element again, but only when it also has the `active` CSS class.
 | 
						|
  
 | 
						|
+makeExample('component-styles/ts/app/hero-details.component.css', 'hostfunction')(format=".")
 | 
						|
  
 | 
						|
:marked
 | 
						|
  ### :host-context
 | 
						|
  
 | 
						|
  Sometimes it is useful to apply styles based on some condition *outside* a component's view.
 | 
						|
  For example, there may be a CSS theme class applied to the document `<body>` element, and
 | 
						|
  we want to change how our component looks based on that.
 | 
						|
  
 | 
						|
  Use the `:host-context()` pseudo-class selector. It works just like the function
 | 
						|
  form of `:host()`. It looks for a CSS class in *any ancestor* of the component host element, all the way
 | 
						|
  up to the document root. It's useful when combined with another selector.
 | 
						|
  
 | 
						|
  In the following example, we apply a `background-color` style to all `<h2>` elements *inside* the component, only
 | 
						|
  if some ancestor element has the CSS class `theme-light`.
 | 
						|
  
 | 
						|
+makeExample('component-styles/ts/app/hero-details.component.css', 'hostcontext')(format='.')
 | 
						|
  
 | 
						|
:marked
 | 
						|
  ### /deep/
 | 
						|
  
 | 
						|
  Component styles normally apply only to the HTML in the component's own template. 
 | 
						|
  
 | 
						|
  We can use the `/deep/` selector to force a style down through the child component tree into all the child component views.
 | 
						|
  The `/deep/` selector works to any depth of nested components, and it applies *both to the view
 | 
						|
  children and the content children* of the component. 
 | 
						|
  
 | 
						|
  In this example, we target all `<h3>` elements, from the host element down 
 | 
						|
  through this component to all of its child elements in the DOM: 
 | 
						|
+makeExample('component-styles/ts/app/hero-details.component.css', 'deep')(format=".")
 | 
						|
 | 
						|
:marked
 | 
						|
  The `/deep/` selector also has the alias `>>>`. We can use either of the two interchangeably.
 | 
						|
 | 
						|
.alert.is-important
 | 
						|
  :marked
 | 
						|
    The `/deep/` and `>>>` selectors should only be used with **emulated** view encapsulation.
 | 
						|
    This is the default and it is what we use most of the time. See the
 | 
						|
    [Controlling View Encapsulation](#view-encapsulation)
 | 
						|
    section for more details.
 | 
						|
 | 
						|
a(id='loading-styles')
 | 
						|
.l-main-section
 | 
						|
:marked
 | 
						|
  ## Loading Styles into Components
 | 
						|
  
 | 
						|
  We have several ways to add styles to a component: 
 | 
						|
  * inline in the template HTML
 | 
						|
  * by setting `styles` or `styleUrls` metadata
 | 
						|
  * with CSS imports
 | 
						|
  
 | 
						|
  The scoping rules outlined above apply to each of these loading patterns.
 | 
						|
  
 | 
						|
  ### Styles in Metadata
 | 
						|
  
 | 
						|
  We can add a `styles` #{_array} property to the `@Component` #{_decorator}.
 | 
						|
  Each string in the #{_array} (usually just one string) defines the CSS.
 | 
						|
  
 | 
						|
+makeExample('component-styles/ts/app/hero-app.component.ts')
 | 
						|
  
 | 
						|
:marked
 | 
						|
  ### Template Inline Styles
 | 
						|
  
 | 
						|
  We can embed styles directly into the HTML template by putting them
 | 
						|
  inside `<style>` tags.
 | 
						|
  
 | 
						|
+makeExample('component-styles/ts/app/hero-controls.component.ts', 'inlinestyles')
 | 
						|
 | 
						|
:marked
 | 
						|
  ### Style URLs in Metadata
 | 
						|
  
 | 
						|
  We can load styles from external CSS files by adding a `styleUrls` attribute
 | 
						|
  into a component's `@Component` #{_decorator}:
 | 
						|
  
 | 
						|
+makeExample('component-styles/ts/app/hero-details.component.ts', 'styleurls')
 | 
						|
 | 
						|
block style-url
 | 
						|
  .alert.is-important
 | 
						|
    :marked
 | 
						|
      The URL is ***relative to the application root*** which is usually the
 | 
						|
      location of the `index.html` web page that hosts the application. 
 | 
						|
      The style file URL is *not* relative to the component file.
 | 
						|
      That's why the example URL begins `app/`.
 | 
						|
      See [Appendix 2](#relative-urls) to specify a URL relative to the 
 | 
						|
      component file.
 | 
						|
 | 
						|
block module-bundlers
 | 
						|
  .l-sub-section
 | 
						|
    :marked
 | 
						|
      Users of module bundlers like Webpack may also use the `styles` attribute
 | 
						|
      to load styles from external files at build time. They could write:
 | 
						|
 | 
						|
      `styles: [require('my.component.css')]`
 | 
						|
 | 
						|
      We set the `styles` property, **not** `styleUrls` property! The module 
 | 
						|
      bundler is loading the CSS strings, not Angular. 
 | 
						|
      Angular only sees the CSS strings *after* the bundler loads them. 
 | 
						|
      To Angular it is as if we wrote the `styles` array by hand. 
 | 
						|
      Refer to the module bundler's documentation for information on 
 | 
						|
      loading CSS in this manner.
 | 
						|
  
 | 
						|
:marked
 | 
						|
  ### Template Link Tags
 | 
						|
  
 | 
						|
  We can also embed `<link>` tags into the component's HTML template. 
 | 
						|
  
 | 
						|
  As with `styleUrls`, the link tag's `href` URL is relative to the 
 | 
						|
  application root, not relative to the component file.
 | 
						|
  
 | 
						|
+makeExample('component-styles/ts/app/hero-team.component.ts', 'stylelink')
 | 
						|
  
 | 
						|
:marked
 | 
						|
  ### CSS @imports
 | 
						|
  
 | 
						|
  We can also import CSS files into our CSS files by using the standard CSS
 | 
						|
  [`@import` rule](https://developer.mozilla.org/en/docs/Web/CSS/@import).
 | 
						|
  
 | 
						|
block css-import-url
 | 
						|
  :marked
 | 
						|
    In *this* case the URL is relative to the CSS file into which we are importing.
 | 
						|
  
 | 
						|
+makeExample('component-styles/ts/app/hero-details.component.css', 'import', 'app/hero-details.component.css (excerpt)')
 | 
						|
 | 
						|
a#view-encapsulation
 | 
						|
.l-main-section
 | 
						|
:marked
 | 
						|
  ## Controlling View Encapsulation: Native, Emulated, and None
 | 
						|
  
 | 
						|
  As discussed above, component CSS styles are *encapsulated* into the component's own view and do
 | 
						|
  not affect the rest of the application.
 | 
						|
  
 | 
						|
  We can control how this encapsulation happens on a *per
 | 
						|
  component* basis by setting the *view encapsulation mode* in the component metadata. There
 | 
						|
  are three modes to choose from:
 | 
						|
  
 | 
						|
  * `Native` view encapsulation uses the browser's native [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM)
 | 
						|
    implementation to attach a Shadow DOM to the component's host element, and then puts the component
 | 
						|
    view inside that Shadow DOM. The component's styles are included within the Shadow DOM.
 | 
						|
    
 | 
						|
  * `Emulated` view encapsulation (**the default**) emulates the behavior of Shadow DOM by preprocessing
 | 
						|
    (and renaming) the CSS code to effectively scope the CSS to the component's view.
 | 
						|
    See [Appendix 1](#inspect-generated-css) for details.
 | 
						|
    
 | 
						|
  * `None` means that Angular does no view encapsulation. 
 | 
						|
    Angular adds the CSS to the global styles. 
 | 
						|
    The scoping rules, isolations, and protections discussed earlier do not apply. 
 | 
						|
    This is essentially the same as pasting the component's styles into the HTML.
 | 
						|
    
 | 
						|
  Set the components encapsulation mode using the `encapsulation` property in the component metadata:
 | 
						|
  
 | 
						|
+makeExample('component-styles/ts/app/quest-summary.component.ts', 'encapsulation.native')(format='.')
 | 
						|
 | 
						|
:marked
 | 
						|
  `Native` view encapsulation only works on [browsers that have native support
 | 
						|
  for Shadow DOM](http://caniuse.com/#feat=shadowdom). The support is still limited,
 | 
						|
  which is why `Emulated` view encapsulation is the default mode and recommended
 | 
						|
  in most cases.
 | 
						|
 | 
						|
a#inspect-generated-css
 | 
						|
.l-main-section
 | 
						|
:marked
 | 
						|
  ## Appendix 1: Inspecting The CSS Generated in Emulated View Encapsulation
 | 
						|
  
 | 
						|
  When using the default emulated view encapsulation, Angular preprocesses
 | 
						|
  all component styles so that they approximate the standard Shadow CSS scoping rules.
 | 
						|
  
 | 
						|
  When we inspect the DOM of a running Angular application with emulated view
 | 
						|
  encapsulation enabled, we see that each DOM element has some extra attributes
 | 
						|
  attached to it:
 | 
						|
  
 | 
						|
code-example(format="").
 | 
						|
  <hero-details _nghost-pmm-5>
 | 
						|
    <h2 _ngcontent-pmm-5>Mister Fantastic</h2>
 | 
						|
    <hero-team _ngcontent-pmm-5 _nghost-pmm-6>
 | 
						|
      <h3 _ngcontent-pmm-6>Team</h3>
 | 
						|
    </hero-team>
 | 
						|
  </hero-detail>
 | 
						|
 | 
						|
:marked
 | 
						|
  We see two kinds of generated attributes:
 | 
						|
  * An element that would be a Shadow DOM host in native encapsulation has a
 | 
						|
    generated `_nghost` attribute. This is typically the case for component host elements.
 | 
						|
    
 | 
						|
  * An element within a component's view has a `_ngcontent` attribute 
 | 
						|
  that identifies to which host's emulated Shadow DOM this element belongs.
 | 
						|
  
 | 
						|
  The exact values of these attributes are not important. They are automatically
 | 
						|
  generated and we never refer to them in application code. But they are targeted
 | 
						|
  by the generated component styles, which we'll find in the `<head>` section of the DOM:
 | 
						|
  
 | 
						|
code-example(format="").
 | 
						|
  [_nghost-pmm-5] {
 | 
						|
    display: block;
 | 
						|
    border: 1px solid black;
 | 
						|
  }
 | 
						|
 | 
						|
  h3[_ngcontent-pmm-6] {
 | 
						|
    background-color: white;
 | 
						|
    border: 1px solid #777;
 | 
						|
  }
 | 
						|
  
 | 
						|
:marked
 | 
						|
  These are the styles we wrote, post-processed so that each selector is augmented
 | 
						|
  with `_nghost` or `_ngcontent` attribute selectors. 
 | 
						|
  These extra selectors enable the scoping rules described in this guide.
 | 
						|
  
 | 
						|
  We'll likely live with *emulated* mode until shadow DOM gains traction.
 | 
						|
  
 | 
						|
a#relative-urls
 | 
						|
.l-main-section
 | 
						|
:marked
 | 
						|
  ## Appendix 2: Loading Styles with Relative URLs
 | 
						|
  
 | 
						|
  It's common practice to split a component's code, HTML, and CSS into three separate files in the same directory:
 | 
						|
  
 | 
						|
code-example(format='').
 | 
						|
  quest-summary.component.ts
 | 
						|
  quest-summary.component.html
 | 
						|
  quest-summary.component.css
 | 
						|
 | 
						|
:marked
 | 
						|
  We include the template and CSS files by setting the `templateUrl` and `styleUrls` metadata properties respectively.
 | 
						|
  Because these files are co-located with the component,
 | 
						|
  it would be nice to refer to them by name without also having to specify a path back to the root of the application.
 | 
						|
 | 
						|
block module-id
 | 
						|
  :marked
 | 
						|
    We can change the way Angular calculates the full URL be setting the component metadata's `moduleId` property to `module.id`.
 | 
						|
 | 
						|
  +makeExample('component-styles/ts/app/quest-summary.component.ts','', 'app/quest-summary.component.ts')
 | 
						|
  :marked
 | 
						|
    Learn more about `moduleId` in the [Component-Relative Paths](../cookbook/component-relative-paths.html) chapter.
 | 
						|
 |