Wie wende ich CSS-Klassen auf eine andere Komponente in AngularDart an?

8

Angenommen, es gibt ein einfaches Framework zum Anzeigen von 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);
}

Dies kann dann folgendermaßen verwendet werden:

// 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);
  }
}
...

Gibt es eine Möglichkeit, Stile von nach weiterzuleiten <some-other-component>, <happy-popup>ohne sie im Stammverzeichnis der App definieren zu müssen?

Sergiy Belozorov
quelle

Antworten:

5

Sie können dies erreichen, indem Sie Ihren Komponentencode in separate Dateien aufteilen - oder in Ihrem Fall in eine separate CSS-Datei.

Anstatt den Stil direkt in die Komponente zu schreiben styles, importieren Sie die CSS-Datei mithilfe von styleUrls. Auf diese Weise können Sie eine Datei mit Ihren Stilen übergeben und die Datei kann von mehreren Komponenten gemeinsam genutzt werden.

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

Beachten Sie, dass die URLs in styleUrls relativ zur Komponente sind.

Ein weiterer Vorteil des Imports des CSS mit styleUrlsbesteht darin, dass Sie auch Importe in dieser Datei verwenden können.

hero1.css

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

Zu Ihrer Information: Es ist gängige Praxis, den Code Ihrer Komponenten in separate Dateien aufzuteilen.

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

Und dann verweisen Sie in Ihrer Dartdatei auf Folgendes:

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

Weitere Informationen hierzu finden Sie unter AngularDart - Komponentenstile .

Dino
quelle
Vielen Dank. Ich war mir der Tatsache bewusst, dass ich Stylesheets in eine separate Datei trennen konnte, dachte aber nicht ganz darüber nach, sie für mehrere Komponenten wiederzuverwenden. Ich bin mir nicht sicher, ob diese Lösung universell genug ist, z. B. könnte eine Popup-Komponente Teil des Frameworks sein, und das Importieren von Teilen der App in das Framework kann gegen die guten Software-Praktiken verstoßen. Im Idealfall bietet das Framework die Möglichkeit, eine beliebige CSS-Datei zu importieren, die über das bereitgestellt wird PopupConfig. Ist es möglich, die styleUrlsEigenschaft für eine Komponente zur Laufzeit anzupassen ?
Sergiy Belozorov
Was ist auch, wenn mehrere Komponenten dasselbe Popup öffnen, es aber anders gestalten möchten?
Sergiy Belozorov
1
Ja, Sie können das styleUrlszur Laufzeit dynamisch einstellen . Es gibt mehrere Möglichkeiten, dies zu tun. Ich empfehle Ihnen, nach Angular dynamic styleUrlsIdeen zu suchen und herauszufinden, welche am besten zu Ihnen passt. Zur zweiten Frage. In diesem Fall würde ich die freigegebene Popup-Styling-Datei + die Styling-Datei importieren, in der ich die Styles überschreibe, die ich für eine bestimmte Komponente ändern möchte.
Dino
Ich bin mir nicht sicher, ob ich Ihren Vorschlag verstehe, wie zwei Popups unterschiedlich gestaltet werden sollen. Beide Popups würden dieselbe Komponente verwenden. Die einzige Möglichkeit, die Stile aus einer freigegebenen Datei zu überschreiben styleUrls, besteht darin , zur Laufzeit einen neuen Eintrag an anzuhängen, nicht wahr? Was ist, wenn zwei Popups gleichzeitig angezeigt werden, z. B. eines das andere öffnet und unterschiedlich gestaltet werden muss? Wenn ich das ändere styleUrls, würde es nicht beides stylen? Schließlich konnte ich keinen guten Artikel darüber finden, wie man styleUrlsohne Hacks dynamisch modifiziert . Es scheint von Angular nativ nicht unterstützt zu werden.
Sergiy Belozorov
1

Da Ihr Popup kein untergeordnetes Element der Komponente ist, die es geöffnet hat, können Sie :: ng-deep nicht verwenden

Das einzige, was meiner Meinung nach funktionieren wird, ist das Entfernen der Ansichtskapselung vom Host, dem Popup und der Komponente, die das Popup öffnet (versuchen Sie nur das Popup und die Komponente, die das Popup zuerst öffnet. Wenn dies nicht funktioniert, entfernen Sie die Kapselung des Hosts auch.)

@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 {
Ron
quelle
Können Sie bitte erklären, was Sie unter "Ansichtskapselung entfernen" verstehen?
Sergiy Belozorov
@SergiyBelozorov Fertig :). Lassen Sie mich wissen, ob es Ihnen geholfen hat
Ron
Es hat mir sicherlich geholfen, etwas Neues über Angular zu lernen und wie man Komponenten konfigurieren kann :-), aber ich bin mir nicht sicher, ob dies der beste Ansatz ist. Mit generischen Klassennamen wie header, mainund footerman kann sich leicht Stil solche Komponenten übrigens. Vielleicht kann man versuchen, spezifischere Namen zu verwenden, um Konflikte zu vermeiden, aber ich würde eine Lösung bevorzugen, die die Kapselung beibehält.
Sergiy Belozorov
Ich verstehe Ihr Anliegen, aber das ist die einzige Lösung für Ihre Anforderungen. Wie Sie sagten, kann man versuchen, spezifischere Namen und Klassen zu verwenden, vielleicht den BEM-Ansatz
Ron
1
Das Deaktivieren ViewEncapsulationist ein großes Nein-Nein für mittelgroße oder große Anwendungen. Es ist besondere Sorgfalt geboten, da sonst Ihr gesamtes Styling auf lange Sicht durcheinander gerät. (Beispiel: generische Klassennamen). Und ich bin nicht der Meinung, dass dies die einzige Lösung für seine Anforderungen ist.
Dino
0

Sie können scss mit Ihrem Code kombinieren

Zuerst müssen Sie die scss-Datei, die Sie für die App freigeben möchten, trennen

Zum Beispiel:

In style1.scss

.header { font-weight: bold }

Dann in style2.scss

@import "style1"

Oder Sie können die Liste der scss-Dateien in Ihrem Komponentencode kombinieren, indem Sie sie in der Liste der Arrays definieren

styleUrls: ['./style1.scss', './style2.scss'],
  • Hinweis: Bitte ändern Sie den Pfad entsprechend Ihrem Dateipfad. Dies funktioniert auch nur, wenn Sie scss und nicht css verwenden

Auf diese Weise können Sie die Unterstützung von config scss für Angular Cli manuell konfigurieren

In angular.json hinzufügen

"projects": {
        "app": {
            "root": "",
            "sourceRoot": "src",
            "projectType": "application",
            "prefix": "app",
            "schematics": { // add this config
                "@schematics/angular:component": {
                    "style": "scss"
                }
            },
Tony Ngo
quelle
Ja, wir verwenden tatsächlich scss und @importAnweisungen. Aber genau wie die Antwort von @Dino frage ich mich, ob es möglich ist, die Stil-URL nicht fest in die Popup-Komponente zu codieren, sondern sie aus der zu lesen PopupConfigund sie zur Laufzeit irgendwie zuzuweisen.
Sergiy Belozorov
0

Sie können den Prop-Wert an die Komponente senden, die eine benutzerdefinierte Klasse sein kann, und ihn in Ihr Popop-HTML einfügen. Fügen Sie dann in der scss-Datei zusätzliche CSS-Überschreibungen für eine bestimmte Klasse hinzu. Sie können also für jede benutzerdefinierte Komponente einen benutzerdefinierten CSS-Code haben.

PS: Und ja, ich würde vorschlagen, scss-Datei zu importieren wie:

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

Es ist einfach besser, CSS von JS + Ihrem CSS-Code zu trennen, dann kann es viel länger sein und alle Styling-Fälle enthalten.

Andris
quelle
0

Zusätzlich zu den bereits erwähnten Methoden würde ich zwei weitere Möglichkeiten vorschlagen, die es zu erkunden gilt:

  1. Da Angular CSS auf Komponenten abdeckt und wir dies auf diese Weise beibehalten möchten, können Sie Ihre Komponentengrenze überschreiten, indem Sie herausfinden, welchen Bereich Angular ihm zugewiesen hat, und CSS mit manuellem Bereich in das globale <style>Tag auf der Seite einfügen :

    @ViewChild ('popupRef') popupRef; ngAfterViewInit () {this.popupRef.nativeElement.attributes [0] .name // Dies hat einen ähnlichen Wert, mit _ngcontent-tro-c1dem Sie Ihr gesamtes benutzerdefiniertes CSS erweitern müssen. }}

Ein offensichtlicher Nachteil dieses Ansatzes besteht darin, dass Angular das CSS-Management verbirgt und Sie daher auf einfaches JS zurückgreifen müssen, um es zu verwalten. ( ein Beispiel hier )

  1. Sie können versuchen, das CSS in @Component zu definieren, bevor Sie es erstellen, indem Sie die benutzerdefinierte Dekorationsfactory wie in dieser Antwort verwenden

Persönlich würde ich die zweite Option untersuchen, da sie weniger hackig zu sein scheint

timur
quelle