كيفية عمل دليل خطوة بخطوة لتطبيقك (إذا كان مشروعك على Angular)

تحية للجميع.

منذ وقت ليس ببعيد ، انتهى السباق التالي ، وكان لدي بعض الوقت لأجعله ليس من الضروري للمستخدمين ، ولكن في نفس الوقت ميزة مثيرة للاهتمام - دليل تفاعلي للعمل مع تطبيقنا.

هناك الكثير من الحلول الجاهزة على الإنترنت - يمكن أن تكون جميعها مناسبة لهذه المهمة بالتأكيد ، لكننا سنرى كيفية القيام بذلك بنفسك.

هندسة معمارية


بنية هذا المكون بسيطة للغاية.

لدينا عنصر مهم في شجرة DOM نريد إخبار المستخدم بشيء ما ، على سبيل المثال ، زر.

نحتاج إلى رسم طبقة مظلمة حول هذا العنصر ، وتحويل الانتباه إليها.
تحتاج إلى رسم بطاقة بجانب هذا العنصر برسالة مهمة.

صورة

لحل هذه المشكلة ، سيساعدنا @ angular / cdk . في مقالتي الأخيرة ، سبق لي أن امتدحت @ الزاوي / المواد ، التي تعتمد على CDK ، بالنسبة لي ، لا يزال مثالًا للمكونات عالية الجودة التي تم إنشاؤها باستخدام جميع ميزات الإطار.

مكونات مثل القوائم ومربعات الحوار وأشرطة الأدوات من مكتبة @ الزاوي / الموادمصنوعة باستخدام المكون من CDK - تراكب.

تتيح لك الواجهة البسيطة لهذا المكون إنشاء طبقة بسرعة فوق تطبيقنا ، والتي سوف تتكيف بشكل مستقل مع التغييرات في حجم الشاشة والتمرير. كما فهمت بالفعل ، يصبح استخدام هذا المكون لحل مشكلتنا بسيطًا جدًا.

أولا ، قم بتثبيت المكتبات

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

بعد التثبيت ، لا تنس إضافة أنماط إلى style.css

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

الآن قم بإنشاء وحدة نمطية جديدة في مشروعنا:

ng generate library intro-lib

وقم على الفور بإنشاء قالب للتوجيه:

ng generate directive intro-trigger

يعمل هذا التوجيه كمحفز لدليلنا المستقبلي ، وسوف يستمع إلى النقرات على عنصر ويسلط الضوء عليه في الصفحة.

@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]);
  }
}

هنا ننتقل إلى الخدمة التي تم إنشاؤها أيضًا عند إنشاء المكتبة ، وسيتم إنجاز جميع الأعمال الرئيسية من قبله.

أولاً ، أعلن عن خاصية جديدة في الخدمة

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

كما رأينا بالفعل هذه القيم ، سوف يرسل لنا التوجيه ، في الصفيف ، العنصر الأول هو الوصف ، والثاني هو عنصر DOM الذي نصفه (لقد اخترت هذا الحل لبساطة المثال).

@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);
    });
  }
}

قم بتحديث مصمم الخدمة بإضافة اشتراك للتحديثات من EventEmitter ، وستتلقى وظيفة الإرفاق تحديثات وإنشاء طبقات.

لإنشاء الطبقة ، نحتاج إلى تراكب وحاقن و NgZone.

يمكن تقسيم الإجراءات التالية إلى عدة مراحل:

  • إغلاق التراكب الحالي (إن وجد)
  • إنشاء إستراتيجية PositionStrong
  • إنشاء OverlayRef
  • إنشاء PortalInjector
  • إرفاق مكون بطبقة

مع النقاط الأولى ، من الواضح أن هذا قد أعلنا بالفعل عن العقار في الخدمة. PositionStrategy - هي المسؤولة عن كيفية وضع الطبقة في شجرة DOM.

هناك العديد من الاستراتيجيات الجاهزة:

  1. FlexibleConnectedPositionStr Strategy
  2. الاستراتيجية العالمية

بكلمات بسيطة ، ثم

FlexibleConnectedPositionStrategy - سيتبع عنصرًا معينًا ، واعتمادًا على التكوين ، سوف يلتزم به عند تغيير حجم المتصفح أو التمرير ، وهو مثال واضح على استخدام القوائم المنسدلة والقوائم.

GlobalPositionStrategy - كما يقول الاسم ، يتم إنشاؤه عالميًا ، ولا يحتاج إلى أي عنصر للعمل ، ومن الأمثلة الواضحة على الاستخدام النوافذ المشروطة.

أضف طريقة لإنشاء إستراتيجية نافذة عائمة حول العنصر قيد التحقيق.

{
 ...
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'
        }
      ]);
  }
...
}

أضف طريقة لإنشاء OverlayRef

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

    return this.overlay.create(config);
  }

وأضف طريقة لربط مكوننا بالطبقة:

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

هذه هي الطريقة التي يبدو بها المكوِّن الذي يعرض الرسالة

@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) {}
}

أخبرنا بالفعل عن كل ما ينتج عن القوائم ، باستثناء DataRef.

DataRef هي فئة بسيطة نضيفها إلى الحاقن للمكونات ، في الواقع ، لنقل البيانات لتقديمها - على سبيل المثال ، وصف.

أيضًا ، قررت أن أرسم طبقة أخرى لتغميق العنصر وإبرازه. في هذه الحالة ، سنستخدم بالفعل استراتيجية إنشاء الطبقة العالمية.

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 - يرسم مكونًا ، وفي الحاقن يتلقى نفس الرمز المميز ، فقط مع العنصر الذي تحتاج إلى التأكيد عليه.

كيف قمت بتنفيذ هذا ، يمكنك أن ترى في المصادر على github ، لا أريد التركيز على هذا بشكل منفصل.

سأقول فقط إنني أرسم لوحة ملء الشاشة ، أرسم شكلًا حول العنصر ، واملأ السياق بطريقة التعبئة ('evenodd') ؛

مجموع


أروع شيء هو أن @ angular / cdk / overlay يسمح لنا برسم العديد من الطبقات كما نشاء. ستكون مرنة وقابلة للتكيف. لا داعي للقلق بشأن تغيير حجم الشاشة ، وإلا ستتغير العناصر لبعض الأسباب الطبيعية ، وسيفكر التراكب في ذلك بالنسبة لنا.

لقد فهمنا كيفية العمل مع الطبقات ، وأدركنا أن مهمة إنشاء دليل خطوة بخطوة ليست صعبة للغاية.

يمكنك تعديل المكتبة بإضافة القدرة على التبديل بين العناصر ، والخروج من وضع العرض وعدد من الحالات الزاوية الأخرى.

شكرا للانتباه.

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


All Articles