Sergiy Belozorov
Sergiy Belozorov

Reputation: 6074

How to apply CSS classes to another component in AngularDart?

Let's say there is a simple framework to display popups:

@Component(
  selector: 'popup-host',
  template: '''
      <div class="popup-container">
        <ng-template #popupRef></ng-template>
      </div>
  ''',
  styles: ['.popup-container { position: absolute; top: 100; left: 100; z-index: 100; }'],
)
class PopupContainerComponent {
  final PopupController _controller;
  final ComponentLoader _loader;

  PopupContainerComponent(this._controller, this._loader);

  void ngOnInit() {
    _controller.container = this;
  }

  @ViewChild('popupRef', read: ComponentRef)
  ComponentRef popupRef;

  void render(PopupConfig config) {
    final componentRef = _loader.loadNextTo(config.factory, popupRef);
    if (componentRef.instance is HasValueSetter) {
      componentRef.instance.value = config.value;
    }
  }
}

@Injectable()
class PopupController {
  PopupContainerComponent _container;
  set container(PopupContainerComponent container) => _container = container;

  void showPopup(PopupConfig config) {
    container.render(config);
  }
  ...
}

class PopupConfig {
  final ComponentFactory factory;
  final dynamic value;
  PopupConfig(this.factory, [this.value]);
}

abstract class HasValueSetter {
  set value(dynamic value);
}

This can then be used like this:

// Somewhere in the root template
<popup-host></popup-host>

// In popup.dart
@Component(
  selector: 'happy-popup',
  template: '''
      <div class="header">This is the popup content.</div>
      <div class="main">The value is {{value}}.</div>
      <div class="footer">I am happy!</div>
  ''',
)
class HappyPopupComponent implements HasValueSetter {
  @override
  dynamic value;
}

// In some_other.dart
@Component(
  ...
  styles: [
    '.header { font-weight: bold }',
    '.main { color: red }',
    '.footer { color: green; font-style: italic }',
  ],
  ...
)
class SomeOtherComponent {
  final PopupController _popupController;
  ...
  SomeOtherComponent(this._popupController, ...) ...;

  void displayPopup() {
    _popupController.showPopup(HappyPopupComponentNgFactory, 42);
  }
}
...

Is there a way to forward styles from <some-other-component> to <happy-popup> without having to define them at the root of the app?

Upvotes: 8

Views: 5182

Answers (5)

timur
timur

Reputation: 14577

In addition to methods already mentioned I would suggest two more avenues to explore:

  1. since Angular scopes CSS to components and we'd like to keep it this way your crossing component boundary can be done by finding out what scope Angular assigned to it and adding manually scoped CSS into global <style> tag on the page:

    @ViewChild('popupRef') popupRef; ngAfterViewInit() { this.popupRef.nativeElement.attributes[0].name // this will have a value similar to _ngcontent-tro-c1 which you will need to scope all your custom CSS with. }

one apparent drawback of this approach is that Angular conceals CSS management and therefore you'll have to resort to plain JS to manage it. (one example here)

  1. you can try defining the CSS in @Component before creating it by utilising custom decorator factory like in this answer

Personally I'd explore the second option as it seems to be less hacky

Upvotes: 0

Andris
Andris

Reputation: 4193

You can send prop value to component, which can be custom class and put it in your popop html. And then in scss file add extra css overrides for specific class. So for each custom component you can have custom css code.

PS: And yes, i would suggest to import scss file like:

@Component(
      styleUrls: ['./hero1.css'],
)

It is just better to seperate css from js + your css code then can be much longer, containing all styling cases.

Upvotes: 0

Ron
Ron

Reputation: 904

As your popup isn't a child of the component that opened it you cannot use ::ng-deep

the only thing I think that will work is to remove view encapsulation from the host, the popup and the component that opens the popup (try only the popup and component that opens the popup first, if that doesn't work, remove the host's encapsulation as well.)

@Component(
  selector: 'happy-popup',
  template: '''
      <div class="header">This is the popup content.</div>
      <div class="main">The value is {{value}}.</div>
      <div class="footer">I am happy!</div>
  ''',
  encapsulation: ViewEncapsulation.None // <=== no encapsulation at all
)
class HappyPopupComponent implements HasValueSetter {

Upvotes: 1

Dino
Dino

Reputation: 8292

You could achieve this by splitting your components code into separate files - or separate CSS file in your case.

Instead of writing the style straight in the component's - styles, you would import the CSS file by using the styleUrls. That way you can pass a file(s) with your styles, and the file can be shared amongst multiple components.

@Component(
      styleUrls: ['./hero1.css', './folder/hero2.css'],
)

Bear in mind that the URLs in styleUrls are relative to the component.

Another benefit of importing the css with styleUrls is that it also grants you the ability to use imports within that file.

hero1.css

@import './folder/hero2.css';

FYI: It's a common practice to split your components code into separate files.

  • hero.dart
  • hero.css
  • hero.html

And then in your dart file reference them as:

@Component(
      templateUrl: './hero.html',
      styleUrls: ['./hero.css'],
)

Please refer to AngularDart - Component Styles for brief information about this.

Upvotes: 6

Tony
Tony

Reputation: 20102

You can combine scss with your code

First you need to seperate the scss file you want to share accross the app

For example:

In style1.scss

.header { font-weight: bold }

Then in style2.scss

@import "style1"

Or you can combine list of scss file in your component code by define in the list of array

styleUrls: ['./style1.scss', './style2.scss'],
  • Note: Please change the path acordingly with your file path and also this only work when you use scss not css

This is how you can manual config scss support for angular cli

In angular.json add

"projects": {
        "app": {
            "root": "",
            "sourceRoot": "src",
            "projectType": "application",
            "prefix": "app",
            "schematics": { // add this config
                "@schematics/angular:component": {
                    "style": "scss"
                }
            },

Upvotes: 0

Related Questions