Disclaimer for the mods: This does not ask for an opinion on the best way to do this, this asks for a way, how to solve this issue at all.
Let's imagine, I want to write a material design style button component for my projects. (I know, this is already available, but this is only for illustration purposes.)
I have the button component setup as normal dart library project, which works fine. Now I want to use this material button component in another angular2 app, so i add it to pubspec.yaml and insert it as directive dependency to my new app-component.
The question is, how can I change, for example, the hover color of this button from the outside?
Since my component should use style encapsulation (emulated styles), one cannot simply set the styles globally.
This is good in theory, however, they're not shipped yet in all browsers and there are no production ready shims. For this to work, someone would define custom css properties inside of the component and set their value outside.
Simply give my button component input properties for the styles which should be changed. This does work, but only for those styles, which do not incorporate css pseudo selectors like :hover, since it's not possible to change :hover styles per javaScript and therefore not in dart too. Someone could write the :hover function in javascript/dart to allow this style change, but this seems bad, because it simply duplicates available code from the css engine.
This one would work for native shadow dom, since a style link tag inside the component, filled by a customCssPath property of the button component would load the styles correctly into the component. However, since shadowdom is not natively available in all browsers, we cannot use it reliably yet. So we are forced to use emulated encapsulation now.
When asking at the Angular2 Repo directly, Matan Lurey, one of the Angular2 Developers for dart provided some guide on how to use Sass mixins to allow the desired styling operations.
One can find the original thread here: https://github.com/dart-lang/angular2/issues/154
However, since I found it not very straightforward to implement, here is a little more detail on how to make that work:
Imagine, we have the following file internal_component.dart
:
import 'package:angular2/angular2.dart';
@Component(
selector: 'internal-component',
styleUrls: const ['internal_component.css'],
templateUrl: 'internal_component.html',
)
class InternalComponent {}
This component has this template file internal_component.html
:
<div>
Some Text
</div>
An this sass style file internal_component.sass
:
div {
background-color: #ffff00;
}
@mixin internal-component-background($selector, $color) {
polyfill-unscoped-rule {
background-color: $color;
content: '#{$selector} > div'
}
}
Include the internal-component
in some other component (here app_component.dart
):
import 'package:angular2/core.dart';
import 'package:sass_internal_styles/internal_component/internal_component.dart';
@Component(
selector: 'my-app',
styleUrls: const ['app_component.css'],
templateUrl: 'app_component.html',
directives: const [InternalComponent],
)
class AppComponent {}
Inside of the template file app_component.html
:
<internal-component id="comp1"></internal-component>
<internal-component id="comp2"></internal-component>
And finally, inside the `app_component.scss:
@import "../lib/internal_component/internal_component.scss";
@include internal-component-background($selector: '#comp2', $color: red);
internal_component.html
and twice as seen in app_component.html
internal_component.scss
. This can be used for defining default values inside the components!
app_component.scss
. The selector given to the mixin, must appear inside of the template, where your inner component is used. In our case, this is the id=comp2
attribute inside app_component.html
Use the [attribute] css selector instead of a .class selector and be aware of the css selector strength hirarchy!
The normal component styles are 'scoped' by applying an attribute to the component's tag and apply the styles of this component only to the component selector in combination with the attribute for scoping. Since a elem[attribute]-css rule has more strength than a simple class selector, the class selector will not work. However, you can try to specify an element name before the class to fix this.
You could also use a .class selector in combination with !important. However, do this only if you know the implications! For example the :hover selector for the same element will not work properly anymore, since !important takes precedence.
The polyfill-unscoped-rule
block gets compiled into
#comp2 > div {
background-color: red;
}
inside the style block for the app_component.css
file. (As stylesheet will be moved to the headers in emulation mode by angular2)
Since the style-encapsulation is only emulated, this child selector for an inner div
is working perfectly.
Update 1: The polyfill-unscoped-rule
block compiling is done by webcomponents.js! :)
One might argue, that this trick could be also done from the outside without sass. That's correct, however, with using the mixin techique, the user of your component does not need to dig into the internal structure of your component. He can use some nice Sass mixins instead, which should be documented in the readme of your component.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With