تحية للجميع.منذ وقت ليس ببعيد ، انتهى السباق التالي ، وكان لدي بعض الوقت لأجعله ليس من الضروري للمستخدمين ، ولكن في نفس الوقت ميزة مثيرة للاهتمام - دليل تفاعلي للعمل مع تطبيقنا.هناك الكثير من الحلول الجاهزة على الإنترنت - يمكن أن تكون جميعها مناسبة لهذه المهمة بالتأكيد ، لكننا سنرى كيفية القيام بذلك بنفسك.هندسة معمارية
بنية هذا المكون بسيطة للغاية.لدينا عنصر مهم في شجرة 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.هناك العديد من الاستراتيجيات الجاهزة:- FlexibleConnectedPositionStr Strategy
- الاستراتيجية العالمية
بكلمات بسيطة ، ثمFlexibleConnectedPositionStrategy - سيتبع عنصرًا معينًا ، واعتمادًا على التكوين ، سوف يلتزم به عند تغيير حجم المتصفح أو التمرير ، وهو مثال واضح على استخدام القوائم المنسدلة والقوائم.GlobalPositionStrategy - كما يقول الاسم ، يتم إنشاؤه عالميًا ، ولا يحتاج إلى أي عنصر للعمل ، ومن الأمثلة الواضحة على الاستخدام النوافذ المشروطة.أضف طريقة لإنشاء إستراتيجية نافذة عائمة حول العنصر قيد التحقيق.{
...
private getPositionStrategy(elementRef: ElementRef): PositionStrategy {
return this.overlay
.position()
.flexibleConnectedTo(elementRef)
.withViewportMargin(8)
.withGrowAfterOpen(true)
.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'
}
]);
}
...
}
أضف طريقة لإنشاء OverlayRefcreateOverlay(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)
);
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 يسمح لنا برسم العديد من الطبقات كما نشاء. ستكون مرنة وقابلة للتكيف. لا داعي للقلق بشأن تغيير حجم الشاشة ، وإلا ستتغير العناصر لبعض الأسباب الطبيعية ، وسيفكر التراكب في ذلك بالنسبة لنا.لقد فهمنا كيفية العمل مع الطبقات ، وأدركنا أن مهمة إنشاء دليل خطوة بخطوة ليست صعبة للغاية.يمكنك تعديل المكتبة بإضافة القدرة على التبديل بين العناصر ، والخروج من وضع العرض وعدد من الحالات الزاوية الأخرى.شكرا للانتباه.