So erstellen Sie eine schrittweise Anleitung für Ihre Anwendung (wenn sich Ihr Projekt in Angular befindet)

Hallo alle zusammen.

Vor nicht allzu langer Zeit endete der nächste Sprint, und ich hatte einige Zeit, um meinen Benutzern nicht die notwendigste, aber gleichzeitig interessante Funktion zu bieten - eine interaktive Anleitung für die Arbeit mit unserer Anwendung.

Es gibt viele vorgefertigte Lösungen im Internet - alle können sicherlich für diese Aufgabe geeignet sein, aber wir werden sehen, wie es selbst geht.

Die Architektur


Die Architektur dieser Komponente ist recht einfach.

Wir haben ein wichtiges Element im DOM-Baum, das wir dem Benutzer mitteilen möchten, beispielsweise eine Schaltfläche.

Wir müssen eine abgedunkelte Ebene um dieses Element zeichnen und die Aufmerksamkeit darauf lenken.
Sie müssen neben diesem Element eine Karte mit einer wichtigen Nachricht ziehen.

Bild

Um dieses Problem zu lösen, hilft uns @ angle / cdk . In meinem letzten Artikel habe ich bereits @ angle / material gelobt , was vom CDK abhängt. Für mich bleibt es immer noch ein Beispiel für hochwertige Komponenten, die mit allen Funktionen des Frameworks erstellt wurden.

Komponenten wie Menüs, Dialogfelder, Symbolleisten aus der Bibliothek @ angle / materialerstellt mit der Komponente von CDK - Overlay.

Über die einfache Oberfläche dieser Komponente können Sie schnell eine Ebene über unserer Anwendung erstellen, die sich unabhängig an Änderungen der Bildschirmgröße und des Bildlaufs anpasst. Wie Sie bereits verstanden haben, wird die Verwendung dieser Komponente zur Lösung unseres Problems sehr einfach.

Installieren Sie zuerst die Bibliotheken

npm i @angular/cdk @angular/material -S

Vergessen Sie nach der Installation nicht, style.css Stile hinzuzufügen

@import '~@angular/cdk/overlay-prebuilt.css';
@import '~@angular/material/prebuilt-themes/deeppurple-amber.css';

Erstellen Sie nun ein neues Modul in unserem Projekt:

ng generate library intro-lib

Und generieren Sie sofort eine Vorlage für die Direktive:

ng generate directive intro-trigger

Diese Anweisung fungiert als Auslöser für unseren zukünftigen Leitfaden, hört Klicks auf ein Element und hebt es auf der Seite hervor.

@Directive({
  selector: '[libIntroTrigger]'
})
export class IntroTriggerDirective {
  @Input() libIntroTrigger: string;

  constructor(private introLibService: IntroLibService, private elementRef: ElementRef) {}

  @HostListener('click') showGuideMessage(): void {
    this.introLibService.show$.emit([this.libIntroTrigger, this.elementRef]);
  }
}

Hier wenden wir uns dem Service zu, der auch beim Erstellen der Bibliothek generiert wurde. Alle Hauptarbeiten werden von ihm ausgeführt.

Deklarieren Sie zunächst eine neue Eigenschaft im Service

show$ = new EventEmitter<[string, ElementRef]>();

Wie wir diese Werte bereits gesehen haben, sendet uns die Direktive, im Array ist das erste Element die Beschreibung und das zweite das von uns beschriebene DOM-Element (ich habe diese Lösung der Einfachheit halber gewählt).

@Injectable({
  providedIn: 'root'
})
export class IntroLibService {
  private overlayRef: OverlayRef;
  show$ = new EventEmitter<[string, ElementRef]>();

  constructor(private readonly overlay: Overlay, private readonly ngZone: NgZone, private readonly injector: Injector) {
    this.show$.subscribe(([description, elementRef]: [string, ElementRef]) => {
      this.attach(elementRef, description);
    });
  }
}

Aktualisieren Sie den Service-Designer, indem Sie Updates von EventEmitter abonnieren. Die Anhängefunktion empfängt Updates und erstellt Layer.

Um die Ebene zu erstellen, benötigen wir Overlay, Injector und NgZone.

Die folgenden Aktionen können in mehrere Phasen unterteilt werden:

  • Aktuelle Überlagerung schließen (falls vorhanden)
  • Erstellen Sie PositionStrategy
  • Erstellen Sie OverlayRef
  • Erstellen Sie PortalInjector
  • Befestigen Sie die Komponente an der Ebene

Mit den ersten Punkten ist klar, dafür haben wir die Immobilie bereits im Service deklariert. PositionStrategy - ist dafür verantwortlich, wie unsere Ebene im DOM-Baum positioniert wird.

Es gibt mehrere vorgefertigte Strategien:

  1. FlexibleConnectedPositionStrategy
  2. GlobalPositionStrategy

In einfachen Worten, dann

folgt FlexibleConnectedPositionStrategy - einem bestimmten Element und bleibt je nach Konfiguration beim Ändern der Größe des Browsers oder des Bildlaufs dabei, ein klares Beispiel für die Verwendung von Dropdown-Listen und Menüs.

GlobalPositionStrategy - wie der Name schon sagt, wird es global erstellt, es benötigt kein Element, um zu funktionieren. Ein offensichtliches Beispiel für die Verwendung sind modale Fenster.

Fügen Sie eine Methode hinzu, um eine Floating-Window-Strategie um das zu untersuchende Element zu erstellen.

{
 ...
private getPositionStrategy(elementRef: ElementRef): PositionStrategy {
    return this.overlay
      .position()
      .flexibleConnectedTo(elementRef)
      .withViewportMargin(8) //     
      .withGrowAfterOpen(true) //           (, exspansion panel  )
      .withPositions([ //   ,            
        {
          originX: 'start',
          originY: 'bottom',
          overlayX: 'start',
          overlayY: 'top'
        },
        {
          originX: 'start',
          originY: 'top',
          overlayX: 'start',
          overlayY: 'bottom'
        },
        {
          originX: 'end',
          originY: 'bottom',
          overlayX: 'end',
          overlayY: 'top'
        },
        {
          originX: 'end',
          originY: 'top',
          overlayX: 'end',
          overlayY: 'bottom'
        }
      ]);
  }
...
}

Fügen Sie eine Methode zum Erstellen von OverlayRef hinzu

createOverlay(elementRef: ElementRef): OverlayRef {
    const config = new OverlayConfig({
      positionStrategy: this.getPositionStrategy(elementRef),
      scrollStrategy: this.overlay.scrollStrategies.reposition()
    });

    return this.overlay.create(config);
  }

Fügen Sie eine Methode hinzu, um unsere Komponente an die Ebene zu binden:

  attach(elementRef: ElementRef, description: string): void {
    if (this.overlayRef && this.overlayRef.hasAttached()) {
      this.overlayRef.dispose();
    }

    this.overlayRef = this.createOverlay(elementRef);

    const dataRef = this.ngZone.run(
      () => new DataRef(this.overlay, this.injector, this.overlayRef, elementRef, description)
    ); //   ,      ,    ,  CD    -  

    const injector = new PortalInjector(this.injector, new WeakMap([[DATA_TOKEN, dataRef]]));

    dataRef.overlayRef.attach(new ComponentPortal(IntroLibComponent, null, injector));
  }

So sieht die Komponente aus, die die Nachricht anzeigt

@Component({
  selector: 'lib-intro-lib',
  template: `
    <mat-card>
      <mat-card-content> {{ data.description }}</mat-card-content>
    </mat-card>
  `,
  styles: ['mat-card {width: 300px; margin: 32px;}']
})
export class IntroLibComponent {
  constructor(@Inject(DATA_TOKEN) public data: DataRef) {}
}

Bereits über alles erzählt, was zu Auflistungen führt, außer DataRef.

DataRef ist eine einfache Klasse, die wir dem Injektor für Komponenten hinzufügen, um Daten zum Rendern zu übertragen - zum Beispiel eine Beschreibung.

Außerdem habe ich beschlossen, eine weitere Ebene zu zeichnen, um das Element abzudunkeln und hervorzuheben. In diesem Fall verwenden wir bereits die globale Strategie zur Ebenenerstellung.

export class DataRef {
  shadowOverlayRef: OverlayRef;

  constructor(
    private overlay: Overlay,
    private injector: Injector,
    public overlayRef: OverlayRef,
    public elementRef: ElementRef,
    public description: string
  ) {
    const config = new OverlayConfig({
      positionStrategy: this.overlay.position().global(),
      scrollStrategy: this.overlay.scrollStrategies.block()
    });

    this.shadowOverlayRef = this.overlay.create(config);

    this.shadowOverlayRef.attach(
      new ComponentPortal(
        ShadowOverlayComponent,
        null,
        new PortalInjector(this.injector, new WeakMap([[DATA_TOKEN, this.elementRef]]))
      )
    );
  }
}


ShadowOverlayComponent - Zeichnet eine Komponente und erhält im Injektor das gleiche Token, nur mit dem Element, um das Sie hervorheben müssen.

Wie ich das implementiert habe, sehen Sie in den Quellen auf Github , ich möchte mich nicht separat darauf konzentrieren.

Ich sage nur, dass ich dort die Leinwand im Vollbildmodus zeichne, eine Form um das Element zeichne und den Kontext mit der Füllmethode ('evenodd') fülle.

Gesamt


Das Coolste ist, dass wir mit @ angle / cdk / overlay so viele Ebenen zeichnen können, wie wir möchten . Sie werden anpassungsfähig und flexibel sein. Wir müssen uns keine Gedanken über das Ändern der Bildschirmgröße machen, da sich die Elemente sonst aus natürlichen Gründen verschieben und die Überlagerung für uns darüber nachdenkt.

Wir haben verstanden, wie man mit Ebenen arbeitet, und festgestellt, dass die Erstellung einer Schritt-für-Schritt-Anleitung nicht so schwierig ist.

Sie können die Bibliothek ändern, indem Sie die Möglichkeit hinzufügen, zwischen Elementen zu wechseln, den Ansichtsmodus zu verlassen und eine Reihe anderer Eckfälle.

Vielen Dank für Ihre Aufmerksamkeit.

Source: https://habr.com/ru/post/undefined/


All Articles