برنامه نویسی

ابرقدرت ها با دستورالعمل ها و تزریق وابستگی: قسمت 2

عکس روی جلد اصلی توسط Markus Spiske در Unsplash.

در پست قبلی نگاهی انداختیم به اینکه چگونه می‌توانیم از تزریق وابستگی + دستورالعمل‌ها برای ساده‌سازی قالب‌هایمان استفاده کنیم. و دستیابی به قابلیت استفاده مجدد در این مقاله، ما قصد داریم دستورالعمل‌های ساختاری را بررسی کنیم و اینکه چگونه می‌توانیم اجزاء و دستورالعمل‌ها را با یکدیگر هماهنگ کنیم و به هم ریختگی را در خود کاهش دهیم. .html فایل های حتی بیشتر.

بیایید یک کامپوننت لودر بسازیم!

مورد استفاده

سناریوی زیر را تصور کنید: ما مؤلفه‌ای داریم که برخی از داده‌ها را بارگیری می‌کند، و می‌خواهیم نشانگر بارگیری را در حین واکشی داده‌ها نشان دهیم. معیارهای زیر باید رعایت شود:

  1. ما باید بتوانیم هر قالبی را داخل مولفه لودر خود بپیچانیم و در صورت نیاز یک اسپینر نمایش می دهد
  2. کامپوننت باید یک ویژگی ورودی دریافت کند که نشان دهد داده در حال بارگذاری است یا خیر
  3. الگو باید توسط یک پوشش پوشانده شود، به طوری که کاربر نتواند در هنگام بارگیری داده ها تعامل داشته باشد (و احتمالاً تماس های HTTP دیگر را راه اندازی کند).

در اینجا یک پیاده سازی بسیار ساده است:

@Component({
  selector: 'app-loader',
  template: `
    <div class="loading-container">
      <ng-content/>
      <div *ngIf="loading" class="blocker">
        <p-progressSpinner/>
      </div>
    </div>`,
  standalone: true,
  styles: [
    `
      .loading-container {
        position: relative;
      }
      .blocker {
        background-color: black;
        position: absolute;
        top: 0;
        z-index: 9999;
        width: 100%;
        height: 100%;
        opacity: 0.4;
      }
    `,
  ],
  imports: [NgIf, ProgressSpinnerModule],
})
export class LoaderComponent {
  @Input() loading = false;
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

توجه داشته باشید: من از PrimeNG برای مثال های این مقاله استفاده می کنم، اما شما به راحتی می توانید آنها را با هر پیاده سازی دیگری دوباره استفاده کنید

بنابراین، در اینجا ما فقط هر محتوایی را که دریافت می کنیم را در آن طرح می کنیم ng-content و بقیه چند CSS ساده + PrimeNG هستند ProgressSpinner جزء. را loading ویژگی ورودی برای روشن و خاموش کردن اسپینر استفاده می شود.

اکنون می توانیم از آن در قالب به صورت زیر استفاده کنیم:

<app-loader [loading]="loading">
  <p>Some content</p>
</app-loader>
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

صبر کنید، من فکر کردم این مقاله در مورد دستورالعمل است؟

خوب، خبر خوب: آن است است در مورد بخشنامه ها اما مشکل کامپوننت چیست؟ خوب، مثالی از استفاده از آن که دیدیم کاملاً خوش بینانه بود: سناریوهای زندگی واقعی معمولاً به این سادگی نیستند. این قطعه از الگو را در نظر بگیرید:

<app-loader [loading]="loading">
  <div class="p-grid">
    <div class="p-col-12">
      <p>Some content</p>
    </div>
    <app-loader [loading]="otherLoading">
        <div class="p-col-12">
            <p>Some other content</p>
            <app-loader [loading]="evenMoreLoading">
                <div class="p-col-12">
                    <p>Even more content</p>
                </div>
            </app-loader>
        </div>
    </app-loader>
  </div>
</app-loader>
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اکنون، در اینجا، زمانی که ما چند عنصر تودرتو داریم، و الگو به طور غیر ضروری رشد می‌کند، سطوح تورفتگی بیشتر، تگ‌های بسته شدن بیشتر، و غیره و غیره را اضافه می‌کند. کاری که من شخصاً واقعاً دوست دارم بتوانم انجام دهم موارد زیر است:

<p *loading="loading">Some content</p>
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اما چگونه می توانیم به این امر برسیم؟ خوب، ما به یک دستورالعمل نیاز داریم که کارهای زیر را انجام دهد:

  1. a را ایجاد می کند LoaderComponent نمونه به صورت پویا
  2. به نوعی قالب تودرتو را در آن پروژه می دهد
  3. آنها را در همگام نگه می دارد – زمانی که loading ویژگی های ورودی تغییر می کند، دستورالعمل باید به روز شود LoaderComponent نمونه بر این اساس
  4. همه چیز را رندر کنید

بیایید در آن شیرجه بزنیم!

دستورالعمل های ساختاری

دستورالعمل‌های ساختاری واقعاً جالب هستند، زیرا به ما اجازه می‌دهند از طریق برخی از الگوها ارجاع دهیم TemplateRef و انواع جادوها را با آن انجام دهید.

همچنین می توانیم از ViewContainerRef برای ایجاد اجزا به صورت پویا چیزی که برای ما باقی می ماند این است که قالب را در کامپوننت طرح ریزی کنیم، و بله، امکانش وجود دارد! بیایید ساده شروع کنیم:

@Directive({
  selector: '[loading]',
  standalone: true,
})
export class LoaderDirective {
  private readonly templateRef = inject(TemplateRef);
  private readonly vcRef = inject(ViewContainerRef);
  @Input() loading = false;
  templateView: EmbeddedViewRef<any>;
  loaderRef: ComponentRef<LoaderComponent>;
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در اینجا ما چیزهایی را که نیاز داریم تزریق کردیم (TemplateRef و ViewContainerRef) افزود loading ورودی، به طور طبیعی، و ایجاد دو ویژگی: templateView و loaderRef. اولین مورد برای ذخیره ارجاع به قالبی که دریافت می کنیم استفاده می شود و مورد دوم برای ذخیره ارجاع به الگو استفاده می شود. ComponentRef نمونه ای که می خواهیم ایجاد کنیم – باید هر دو را ذخیره کنیم.

در مرحله بعد، به سمت چپ، مقداری وزنه برداری اولیه را انجام دهید تا کل کار را تنظیم کنید:

@Directive({
  selector: '[loading]',
  standalone: true,
})
export class LoaderDirective implements OnInit {
  private readonly templateRef = inject(TemplateRef);
  private readonly vcRef = inject(ViewContainerRef);
  @Input() loading = false;
  templateView: EmbeddedViewRef<any>;
  loaderRef: ComponentRef<LoaderComponent>;

  ngOnInit() {
    this.templateView = this.templateRef.createEmbeddedView({});
    this.loaderRef = this.vcRef.createComponent(LoaderComponent, {
      injector: this.vcRef.injector,
      projectableNodes: [this.templateView.rootNodes],
    });

    this.loaderRef.setInput('loading', this.loading);
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اینجا، ما ngOnInit روش چرخه حیات چهار کار را انجام می دهد:

  1. قالب را به یک نمای تعبیه شده تبدیل کنید تا بتوانیم آن را به صورت پویا رندر کنیم
  2. ایجاد یک LoaderComponent نمونه، مثال
  3. قالب را در LoaderComponent نمونه از طریق projectableNodes – اینجاست که جادو اتفاق می افتد!
  4. را تنظیم کنید loading ویژگی ورودی در LoaderComponent نمونه، مثال

حالا، به این ترتیب به نوعی کار خواهد کرد، اما ما به دو چیز دیگر نیاز داریم تا آن را به درستی کار کنیم:

  1. ما باید به روز رسانی کنیم LoaderComponent به عنوان مثال زمانی که loading ویژگی ورودی تغییر می کند
  2. ما باید اطمینان حاصل کنیم که تشخیص تغییر همچنان روی الگوی پیش‌بینی‌شده کار می‌کند، علی‌رغم اینکه از نمای والد جدا شده و به یک جزء جدید نمایش داده می‌شود. ما استفاده خواهیم کرد ngDoCheck برای این

بیایید اجرای را نهایی کنیم:

@Directive({
  selector: '[loading]',
  standalone: true,
})
export class LoaderDirective implements OnInit, DoCheck, OnChanges {
  private readonly templateRef = inject(TemplateRef);
  private readonly vcRef = inject(ViewContainerRef);
  @Input() loading = false;
  templateView: EmbeddedViewRef<any>;
  loaderRef: ComponentRef<LoaderComponent>;

  ngOnInit() {
    this.templateView = this.templateRef.createEmbeddedView({});
    this.loaderRef = this.vcRef.createComponent(LoaderComponent, {
      injector: this.vcRef.injector,
      projectableNodes: [this.templateView.rootNodes],
    });

    this.loaderRef.setInput('loading', this.loading);
  }

  ngOnChanges() {
    this.loaderRef?.setInput('loading', this.loading);
  }

  ngDoCheck() {
    this.templateView?.detectChanges();
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این اضافات نسبتاً ساده هستند: وقتی که loading ویژگی کامپوننت تغییر می کند، ما آن را به روز می کنیم LoaderComponent بر این اساس، و هنگامی که تشخیص تغییر برای نمونه دستورالعمل اجرا می شود، ما همچنین به الگوی فرزند در ngDoCheck روش چرخه حیات از طریق templateView.detectChanges(). اگر با نحوه آن آشنا نیستید ngDoCheck کار می کند یا چرا از آن استفاده می شود، می توانید اسناد رسمی یا این آموزش را بخوانید.

اکنون می توانیم به سادگی از آن در قالب استفاده کنیم، حتی زمانی که چندین عنصر تودرتو داریم:

<p *loading="loading">
    Some content
    <span *loading="otherLoading">
        Some other content
    </span>
    <p *loading="evenMoreLoading">
        Even more content
    </p>
</p>
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و بنابراین، هیچ الگوی تودرتو، هیچ تورفتگی غیر ضروری، و هیچ برچسب بستن غیر ضروری وجود ندارد. این فقط یک دستورالعمل ساده است که کار را انجام می دهد.

می توانید نمونه کامل را با یک دمو زنده در StackBlitz مشاهده کنید:

نتیجه

همانطور که قبلا ذکر شد، من معتقدم دستورالعمل ها بسیار، خیلی قدرتمند، اما متأسفانه در جامعه گسترده تر استفاده نشده است. با این سری از مقاله‌ها، می‌خواهم موارد استفاده مختلف را بررسی کنیم که در آن دستورالعمل‌ها به ما کمک می‌کنند الگوهایمان را ساده کنیم و خوانایی را بهبود ببخشیم. در قسمت بعدی، استفاده از دستورالعمل‌ها برای هک کردن اجزای موجود را بررسی خواهیم کرد. گوش به زنگ باشید!

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا