Angular4 ng-content wird erstellt, wenn ngIf falsch ist

Lesezeit: 7 Minuten

Benutzer-Avatar
Cabadath

Ich habe ein Problem mit dem neuen ng-content Transklusion.

Nehmen wir an, ich habe eine Komponente my-component das, in seiner ngOnInit() Funktion führt einige schwere Operationen unter Last aus (vorerst nur a console.log()).

Ich habe einen Wrapper, der den Inhalt per Transklusion anzeigt (my-wrapper.component.html).

<ng-content></ng-content>

Wenn ich die Umgebung so einrichte, wird die Protokollaussage nicht angezeigt:

<my-wrapper *ngIf="false">
    <my-component></my-component>
</my-wrapper>

Ich nehme an, die my-wrapper Die Komponente wird nicht erstellt, daher wird der Inhalt ignoriert.

Aber wenn ich versuche, die Logik in die zu verschieben my-wrapper Komponente wie diese (my-wrapper.component.html):

<ng-container *ngIf="false">
    <ng-content></ng-content>
</ng-container>

Ich sehe immer die console.log() Ausgang. Ich vermute, die my-component wird gebaut und dann eingelagert, bis die *ngIf wird true Innerhalb my-wrapper.

Die Absicht war, eine generische “Listenelement + Detail”-Komponente zu erstellen. Angenommen, ich habe eine Liste von N Übersichtselementen (my-wrapper), die in a gerendert werden *ngFor Schleife. Jedes dieser Elemente hat seine eigene Detailkomponente (my-component), die ihre eigenen Daten laden soll, sobald ich mich entscheide, mehr Infos zu einem bestimmten Artikel anzuzeigen.

Übersicht.html:

<ng-container *ngFor="let item of items">
    <my-wrapper>
        <my-component id="item.id"></my-component>
    </my-wrapper>
</ng-container>

mein-wrapper.component.html:

<div (click)="toggleDetail()">Click for more</div>
<div *ngIf="showDetail">
    <ng-content></ng-content>
</div>

Gibt es eine Möglichkeit, Angular anzuweisen, den eingeschlossenen Inhalt zu ignorieren, bis er der Seite hinzugefügt werden muss? Analog wie es in AngularJS war.

  • Ich würde vorschlagen, anstatt dem Wrapper eine Bedingung hinzuzufügen, die Bedingung der untergeordneten Komponente hinzuzufügen, um die Ausführung zu stoppen

    – Jameel Moideen

    5. Juli 2017 um 15:02 Uhr

  • stackoverflow.com/questions/41652919/…

    – Viktor Lova

    5. Juli 2017 um 15:04 Uhr

Basierend auf dem Kommentar von @nsinreal habe ich eine Antwort gefunden. Ich finde es ein bisschen abstrus, deshalb versuche ich es hier zu posten:

Die Antwort ist, damit zu arbeiten ng-template und *ngTemplateOutlet.

In dem my-wrapper Komponente, richten Sie die Vorlage wie folgt ein (my-wrapper.component.html):

<div (click)="toggleDetail()">Click for more</div>
<div *ngIf="showDetail" [hidden]="!isInitialized">
    <ng-container *ngTemplateOutlet="detailRef"></ng-container>
</div>

Notiere dass der [hidden] Es ist nicht wirklich notwendig, es versteckt die “rohe” Vorlage des Kindes, bis es entscheidet, dass es geladen ist. Stellen Sie nur sicher, dass Sie es nicht in einen stecken *ngIfansonsten der *ngTemplateOutlet wird niemals ausgelöst, was dazu führt, dass überhaupt nichts passiert.

Zum Einstellen der detailReffügen Sie dies in den Komponentencode ein (my-wrapper.component.ts):

import { ContentChild, TemplateRef } from '@angular/core';

@Component({ ... })
export class MyWrapperComponent {
    @ContentChild(TemplateRef) detailRef;

    ...
}

Jetzt können Sie den Wrapper wie folgt verwenden:

<my-wrapper>
    <ng-template>
        <my-component></my-component>
    </ng-template>
</my-wrapper>

Ich bin mir nicht sicher, warum es so komplizierte “Workarounds” braucht, wenn es früher so einfach war, dies in AngularJS zu tun.

Dadurch:

<my-wrapper *ngIf="false">
    <my-component></my-component>
</my-wrapper>

Sie rufen nicht an MyComponent Komponente, weil die *ngIf ist false. Das heißt, wenn Sie es nicht aufrufen, instanzieren Sie es nicht und gehen daher nicht durch es ngOnInit. Und deshalb erhalten Sie das Konsolenprotokoll nicht.

Dadurch:

<ng-container *ngIf="false">
    <ng-content></ng-content>
</ng-container>

Sie befinden sich innerhalb der Komponente, Sie schränken nur ein, was in Ihrer Vorlage gerendert werden soll, aber Sie haben Ihre Komponente bereits instanziiert und haben daher Ihre durchlaufen ngOnInit und Sie erhalten Ihr Konsolenprotokoll fertig.

Wenn Sie etwas einschränken möchten (Komponentenaufruf mit Selektor oder a ng-content oder sogar ein div) bis Sie einige Daten verfügbar haben, können Sie Folgendes tun:

datasLoaded: Promise<boolean>;

this.getData().subscribe(
       (data) => {
            this.datasLoaded = Promise.resolve(true); // Setting the Promise as resolved after I have the needed data
       }
);

Und in deiner Vorlage:

<ng-container *ngIf="datasLoaded | async">
   // stuff here
</ng-container>

Oder:

<my-component *ngIf="datasLoaded | async">
   // Didn't test this one, but should follow the same logic. If it doesn't, wrap it and add the ngIf to the wrapper
</my-component>

  • Ich glaube nicht, dass das in meinem Fall funktioniert. Die Absicht war folgende: Ich habe eine Liste von Übersichtsdaten, die in a gerendert werden *ngFor-Schleife (my-wrapper). Für jedes Übersichtselement habe ich eine zweite “Detail”-Komponente (my-component), das seine eigenen Daten laden soll (nach ID des Übersichtselements), sobald ich das übergeordnete Element (die *ngIf). Wenn ich Ihren Ansatz ausprobiere, werden alle Details sofort ausgelöst, was Hunderte von teuren API-Aufrufen verursacht. Ich denke, ich könnte das integrieren my-component mehr hinein my-wrapperaber das macht es weniger generisch, was enttäuschend ist.

    – Cabadath

    6. Juli 2017 um 5:30 Uhr

Dies liegt daran, dass Ng-Inhalte zur Build-Zeit passieren und wenn Sie den Inhalt übergeben, wird er tatsächlich nicht entfernt oder mit der ngIf-Direktive neu erstellt. Es wird nur verschoben und die Komponente wird instanziiert.

Ich bin kürzlich auch auf dieses Problem gestoßen, habe mich aber für eine andere Lösung als die derzeit akzeptierte entschieden.

Lösung (TL;DR)

(Lösung ist für AngularDart; ich denke aber, dass es in Angular ähnlich ist)

Verwenden Sie eine Strukturdirektive; Tutorials unten verlinkt.

Anstatt von:

<my-wrapper>
  <my-contents></my-contents>
</my-wrapper>

Ihre Nutzung wird:

<div *myWrapper>
  <my-contents></my-contents>
</div>

Dies ist eine Abkürzung für Folgendes (in AngularDart; ich denke, Angular verwendet <ng-template>)

<template myWrapper>
  <div>
    <my-contents></my-contents>
  </div>
</template>

Das MyWrapper direktive Logik ist ähnlich wie NgIf außer es hat seine eigene Logik, um die Bedingung zu berechnen. In den beiden folgenden Tutorials wird erläutert, wie Sie eine NgIf-ähnliche Direktive und wie Sie Ihre eigenen Eingaben mit der speziellen Mikrosyntax übergeben (z *myWrapper="myInput: expression"). Beachten Sie, dass die Mikrosyntax keine Ausgaben unterstützt (@Output), aber Sie können eine Ausgabe imitieren, indem Sie eine Eingabe verwenden, die eine Funktion ist.

Anleitung für Angular

Tutorial für AngularDart

Vorbehalt: Da dies nur eine Direktive ist, sollte sie nichts Komplizierteres bewirken, als eine Template-Referenz zum richtigen Zeitpunkt zu instanziieren und möglicherweise einige DI-Anbieter anzugeben. Zum Beispiel würde ich vermeiden, Stile anzuwenden oder einen komplexen Baum von Komponenten in der Direktive zu instanziieren. Wenn ich eine Listenkomponente erstellen wollte, würde ich wahrscheinlich die nehmen @ContentChild(TemplateRef) in einer anderen Antwort beschriebener Ansatz; Sie würden die Sternchen-Kurzform für das Erstellen verlieren <template> aber Sie würden die volle Leistung der Komponenten erhalten.

Mein Problem

Mein Team besitzt eine App, die Teil einer größeren Webanwendung mit anderen Apps ist, die anderen Teams gehören. Unsere Komponenten gehen davon aus, dass sie einen einspritzen können MyAppConfiguration -Objekt, aber dieses Objekt kann erst eingefügt werden, nachdem es mit einer asynchronen Anforderung geladen wurde. In unserer App ist das kein Problem: Wir haben eine „Shell“-Komponente, die alles hinter einem verbirgt ngIf bis die Konfiguration geladen ist.

Das Problem ist, wenn andere Teams auf unsere Komponenten verweisen wollen. Wir wollen nicht, dass sie die Logik „Warten, bis die Konfiguration geladen ist“ jedes Mal duplizieren, also habe ich versucht, eine Wrapper-Komponente zu erstellen, die wie folgt verwendet werden kann:

<my-app-wrapper>
  <my-app-component></my-app-component>
</my-app-wrapper>

Der Wrapper fügt ein Dienstobjekt ein und verbirgt seinen Inhalt hinter einem ngIf bis der Dienst sagt, dass die Konfiguration geladen ist.

Wie das Fragenposter entdeckte ich, dass die ng-content Ansatz funktioniert nicht wie beabsichtigt: Während der Inhalt korrekt vor dem DOM verborgen ist, instanziiert Angular immer noch die Komponenten, die dazu führen, dass die Abhängigkeitsinjektion fehlschlägt.

Die Lösung, für die ich mich entschieden habe, bestand darin, die Wrapper-Komponente als strukturelle Direktive neu zu schreiben.

1142450cookie-checkAngular4 ng-content wird erstellt, wenn ngIf falsch ist

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy