<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های مصطفی میری</title>
        <link>https://virgool.io/feed/@mostafamiri</link>
        <description>Angular Developer</description>
        <language>fa</language>
        <pubDate>2026-04-15 10:27:24</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/2489101/avatar/iBMQrq.jpg?height=120&amp;width=120</url>
            <title>مصطفی میری</title>
            <link>https://virgool.io/@mostafamiri</link>
        </image>

                    <item>
                <title>معماری Hexagonal در Angular</title>
                <link>https://virgool.io/@mostafamiri/hexagonal-in-angular-owozndsommb6</link>
                <description>فهرستمعرفییک مثال کامل و کاربردیانتخاب های مربوط به پیاده سازیمزایای استفاده از معماری hexagonal در Angularزمان استفاده از معماری hexagonal در Angularنتیجه گیری1. معرفیبرای مدت طولانی، Model View Controller معماری مورد علاقه توسعه دهندگان نرم افزار بود. از آن در back-end و همچنین در کدهای front-end استفاده شد. اما با توجه روزافزون جامعه به طراحی Domain-Driven، این معماری توسط معماری «hexagonal» (یا «ports و adapters») به چالش کشیده شده است.در مقایسه با MVC، معماری hexagonal از اصل جداسازی و همچنین abstraction (انتزاع) بیشتر استفاده می کند و کد domain در این معماری قسمت مرکزی برنامه ما است.اگر می خواهید اطلاعات بیشتری در مورد معماری hexagonal داشته باشید، در اینجا یک مقاله کامل  وجود دارد که توسط طراح آن، آلیستر کوکبرن نوشته شده است.در حال حاضر معماری hexagonal بیشتر در کدهای بک‌اند استفاده می‌شود و منابع ضعیفی در مورد آن در کدهای فرانت‌اند مخصوصا برای Angular وجود دارد.چگونه معماری hexagonal را با Angular تطبیق دهیم؟ آیا سودمند خواهد بود؟ اگر شما هم به این سوالات علاقه دارید، این مقاله را بخوانید.2 - یک مثال کامل و کاربردیتمام توضیحات زیر بر اساس برنامه ای است که من توسعه داده‌ام و در Github در دسترس است . این برنامه بر اساس برنامه تور قهرمانان Angular است . اگر برنامه را اجرا کنید، رابط نمایش داده شده مانند آموزش Angular است، اما تفاوت های زیادی در ساختار کد وجود دارد. اصل این برنامه کوچک نمایش لیستی از قهرمانان و مدیریت (create, delete, modify) آنها است. ماژول angular-in-memory-web-api برای شبیه سازی فراخوانی API های خارجی استفاده می شود.این یک نمای کلی از معماری این برنامه است:و ساختار کد مرتبط:دامنه (domain)در معماری hexagonal،منطق برنامه در domain نوشته می شود و کل کد مربوط به domain ایزوله شده است. برنامه Tour of Heroes اهداف زیر را دارد: نمایش لیستی از قهرمانان، نمایش جزئیات یک قهرمان خاص و نمایش logهای مربوط به اقدامات انجام شده توسط کاربر. کلاس های مرتبط با domain در این معماری به شکلی مرکزی هستند: HeroesDisplayer، HeoresDetailDisplayerو MessagesDisplayer.پورت ها (ports)همانطور که می توانید تصور کنید، کد مربوط به domain در برنامه قهرمانان ما تنها نیست. کدهای user interface مربوط به کامپوننت ها و نیز سرویس‌هایی برای فراخوانی‌های API خارجی وجود دارند. در معماری hexagonal، کدهای مربوط به domain مستقیماً با همه این کدها تعامل ندارند. در عوض،از اشیایی به نام port استفاده می شود و توسط کلاس های interface پیاده سازی می شوند. این باعث کم کردن وابستگی بین عناصر معماری ما می شود(مرتبط با اصول solid).در برنامه قهرمانان،در HeroesDisplayerو HeoresDetailDisplayerنیاز به تعامل با یک سرویس خارجی داریم که تعاملات مرتبط با قهرمانان را ذخیره می کند. برای این منظور پورتIManageHeroesرا ارائه میدهیم . برای هر یک از کلاس های domain خود، ما می خواهیم تعاملات هر کاربر را پیگیری کنیم،به همین دلیل است که آنها یک پورت IManageMessagesنیز دارند.کاربران از طریق interfaceهای نمایشگر برنامه ما درک می کنند. این interfaceها را می توان با توجه به هدفشان به چند دسته تقسیم کرد. برای تکمیل برنامه Angular tour of heroes، باید interfaceهایی داشته باشیم که قهرمانان را نمایش می‌دهد (لیست قهرمانان و داشبورد)، یک interface که جزئیات قهرمان را نشان می‌دهد و یک interface برای نمایش پیام‌ها. بنابراین، port های مرتبط باید به این ترتیب باشند: IDisplayHeroes، IDisplayHeroDetailو IDisplayMessages.آداپتورها (adapters)حالا که port های ما تعریف شده اند، باید adapterها را روی آن ها وصل کنیم. یکی از مزایای این معماری سهولت در جابجایی بین adapterها است. به عنوان مثال، adapter متصل به IManageHeroes می تواند adapterی باشد که REST API را فراخوانی می کند، و ما می توانیم آن را به راحتی با یک adapter با استفاده از GraphQL API جایگزین کنیم. در این مورد، ما می‌خواهیم برنامه ما با برنامه Google tour of heroes یکسان باشد. بنابراین ما یک سرویس angular را پیاده‌سازی می‌کنیم، که HeroAdapterServiceیک in-memory web API را فراخوانی می‌کند، و دیگری، MessageAdapterServiceپیام‌ها را به صورت محلی ذخیره می‌کند.آداپتورهای سه port دیگر adapterهای مربوط به رابط کاربری هستند. در برنامه ما، آنها توسط کامپوننت های Angular پیاده سازی خواهند شد. همانطور که می بینید پورت IDisplayHeroesتوسط سه adapter پیاده سازی می شود. جزئیات در ادامه در دسترس خواهد بود.همانطور که در بالا توضیح داده شد، به دلیل ماهیتadapterهای ما، عدم تقارن وجود دارد. نمودار معماری آن را به این روش نشان می دهد: adapterهای سمت چپ معماری برای تعاملات با کاربران طراحی شده اند، در حالی که adapterهای سمت راست برای تعاملات سرویس های خارجی طراحی شده اند.3- انتخاب های پیاده سازیاز آنجایی که معماری hexagonal برای برنامه های Back-end طراحی شده است، مقرراتی در پیاده سازی کد طراحی شده است. این انتخاب ها در قسمت زیر توضیح داده شده اند.اشیاء مرتبط با angular در کد domainیک روش خوب در معماری hexagonal این است که کد مربوط به domain را مستقل از هر framework نگه دارید تا از عملکرد آن برای هر نوع adapter اطمینان حاصل شود. اما در کد ما domain به شدت به اشیاء Angular و rxjs وابسته است. در واقع، باید مطمئن شویم که از چندین framework تایپ‌اسکریپت یا جاوا اسکریپت برای حفظ انسجام interface استفاده نخواهیم کرد. همچنین، سیستم تزریق وابستگی angular برای دستیابی به inversion of control principle بسیار مفید است. با این حال، باید بتوان از promiseهای جاوا اسکریپت به جای observableهای rxjs استفاده کرد، اما باید کدهای تکراری زیادی در کلاس‌های خود بنویسیم.نوع برگشتی observable در port های سمت چپ از آنجایی که منطق کد در Domain مدیریت می‌شود، ممکن است تعجب کنیم که چرا Observable از Port‌های IDisplayHeroDetail، IDisplayHeroes و IDisplayMessages برگردانده میشوند. در واقع، هر شی که توسط سرویس‌ها برگردانده می‌شود، در داخل کد Domain با استفاده از متد‌های Pipe و Tap مدیریت می‌شود. به عنوان مثال، نتیجه ذخیره جزئیات قهرمان که توسط HeroAdapterService بازگردانده شده است، مستقیماً در HeroDetailDisplayer مدیریت می‌شود:hero-detail-displayer.tsaskHeroNameChange(newHeroName: string): Observable&lt;void&gt; {
    [...]
    const updatedHero = {id: this.hero.id, name: newHeroName};
    return this._heroesManager.updateHero(updatedHero).pipe(
        tap(_ =&gt; this._messagesManager.add(`updated hero id=${this.hero ? this.hero.id : 0}`)),
        catchError(this._errorHandler.handleError&lt;any&gt;(`updateHero id=${this.hero.id}`, this.hero)),
        map(hero =&gt; {if(this.hero){this.hero.name = hero.name}})
    );
}با این حال، بازگرداندن یک observable خالی از متد askHeroNameChangeجالب است اگر هدف ما فعال کردن adapterهای interface برای اطلاع از زمان لود شدن داده ها باشد. به عنوان مثال، زمانی که تغییرات مربوط به جزئیات قهرمان لود شود، می‌توانیم به صفحه قبل برگردیم:hero-detail.component.tschangeName(newName: string): void {
    this.heroDetailDisplayer.askHeroNameChange(newName).pipe(
        finalize(() =&gt; this.goBack())
    ).subscribe();
}اشکال این انتخاب پیاده سازی، نیاز به subscribe کردن در هر فراخوانی تابع domain در adapterهای سمت چپ است:heroes.component.tsthis.heroesDisplayer.askHeroesList().subscribe();کلاس HeroesDisplayer دو بار نمونه سازی شد.در برنامه ما، تزریق وابستگی در app.module.tsمدیریت می شود. ما از injection tokens برای دسترسی به کلاس‌های domain در کامپوننت های Angular استفاده می‌کنیم. برای مثال تزریق IDisplayHeroDetail به کامپوننت HeroDetail به این صورت انجام می شود:app.module.tsimport HeroDetailDisplayer from &#039;../domain/hero-detail-displayer&#039;;

providers: [
    [...]
    {provide: &#039;IDisplayHeroDetail&#039;, useClass: HeroDetailDisplayer},
    [...]
}نمونه شی HeroesDetailDisplayer را به عنوان یک پیاده سازی IDisplayHeroDetail ایچاد می کند.hero-detail.component.tsimport IDisplayHeroDetail from &#039;src/app/domain/ports/i-display-hero-detail&#039;;

export class HeroDetailComponent implements OnInit {
    constructor(
        @Inject(&#039;IDisplayHeroDetail&#039;) public heroDetailDisplayer: IDisplayHeroDetail,
        [...]
    ) {}
}در اینجا HeroDetailDisplayer را داخل HeroDetailComponent تزریق می کند.با این حال، در جایی از کد یک نکته ظریف وجود دارد: دو injection token مختلف برای کلاس HeroesDisplayer تولید می‌شوند. علاوه بر این، HeroesComponentو DashboardComponentیک injection token یکسان را به اشتراک می گذارند، در حالی که HeroSearchComponent از token دیگری استفاده می کند.app.module.tsimport HeroesDisplayer from &#039;../domain/heroes-displayer&#039;;

providers: [
    // Used in HeroesComponent and in DashboardComponent 
    {provide: &#039;IDisplayHeroes&#039;, useClass: HeroesDisplayer},
    // Used in HeroSearchComponent
    {provide: &#039;IDisplayHeroesSearch&#039;, useClass: HeroesDisplayer},
]این به این دلیل است که HeroesComponentو DashboardComponentمی تواند نمونه مشابهی از HeroesDisplayer را به اشتراک بگذارند: آنها لیست یکسانی از قهرمانان را نمایش دهند. از سوی دیگر، اگر در HeroSearchComponentهمین نمونه وجود داشته باشد، هر جستجو بر قهرمان های نمایش داده شده تأثیر می گذارد، زیرا این ویژگی heroesبا متد askHeroesFiltered درHeroesDisplayer تغییر یافته است . اشتراک‌گذاری یک token برای سه کامپوننت، رفتار برنامه ما را تغییر می‌دهد:4 - مزایای معماری hexagonal در Angularماهیت اصلی معماری hexagonal شامل داشتن adapterهای قابل تعویض است. در برنامه ما، ما به شدت به چارچوب Angular وابسته هستیم، به این معنی که ما از این معماری بهره کامل نمی بریم.(زیرا کد domail باید فارغ از فریم ورک ها باشد) با این حال، من برخی از نکات امیدوارکننده را از تجربه آن در front-end پیدا کردم.جداسازی لایه ارائه ، لایه core و فراخوانی سرویس های خارجیکد domain، مربوط به لایه اصلی ما، به وضوح از  لایه ارائه(presentational) توسط port ها جدا می شود. به لطف همین port ها، خطر اضافه کردن کد ناخواسته به فراخوانی سرویس های خارجی کاهش می یابد. تمام منطق اصلی در کلاس های domain مدیریت می شود.heroes.component.tsconstructor(
    @Inject(&#039;IDisplayHeroes&#039;) public heroesDisplayer: IDisplayHeroes
) { }کلاس domain  را inject می کند.heroes.component.html &lt;li *ngFor=&amp;quotlet hero of heroesDisplayer.heroes&amp;quot&gt;
    [...]
&lt;/li&gt;از اطلاعات قهرمان‌ها که توسط کد domain در view، مربوط به لایه نمایشی مدیریت می‌شوند استفاده می‌کند.جداسازی کداگر به برنامه اصلی تور قهرمانان نگاه کنید، هدف اصلی هر سه کامپوننت HeroesComponent HeroSearchComponentو  DashboardComponentبسیار نزدیک است. همه آنها لیستی از قهرمانان را نمایش می دهند، اما تعاملات احتمالی بسته به کامپوننت ها متفاوت است. بنابراین کد اصلی مرتبط با سرویسی که اطلاعات مختص به آن را برمیگرداند، باید جدا شود. در کد ما، کد مربوط به domain برای سه کامپوننت جداسازی شده است: ما از قابلیت استفاده مجدد پورت hexagonal بهره می بریم.تست کردنگاهی اوقات، تست‌های Angular می‌تواند بسیار سخت باشد.حتی اگر کد قسمت core با کد ارائه در کامپوننت ها جدا شود، این کد با تکامل برنامه شما رشد می کند. جدا نگه داشتن کامپوننت های display، کد domain و سرویس های ما از یکدیگر، تست ها را ساده تر می کند. شما به راحتی می توانید لایه های دیگر را mock کنید و روی تست کلاس فعلی تمرکز کنید.hero-detail.component.spec.tsbeforeEach(async () =&gt; {
    spyIDisplayHeroDetail = jasmine.createSpyObj(
      &#039;IDisplayHeroDetail&#039;, 
      [&#039;askHeroDetail&#039;, &#039;askHeroNameChange&#039;],
      {hero: {name: &#039;&#039;, id: 0}}
    );
    spyIDisplayHeroDetail.askHeroDetail.and.returnValue(of());
    spyIDisplayHeroDetail.askHeroNameChange.and.returnValue(of());
    [...]
}تست های نمایش جزئیات قهرمان: کلاس domain و متد ها را می توان به راحتی mock کرد.5 — چه زمانی از معماری hexagonal در Angular استفاده کنیم.حتی اگر نتوانیم آن را کاملاً با کدهای بک‌اند مقایسه کنیم، معماری hexagonal می‌تواند مزایای بزرگی در برخی از برنامه‌های کاربردی front-end داشته باشد.در اینجا چند مورد را بررسی میکنیم:برنامه های مبتنی بر پروفایلهمانطور که لایه display را جدا کردیم، برنامه‌هایی که از منطق یکسانی در داخل interfaceهای مختلف استفاده می‌کنند، مانند برنامه‌های مبتنی بر پروفایل، نامزدهای خوبی برای معماری ما هستند. برنچ پنل مدیریت مثالی از است که اگر یک interface پنل مدیریت اضافه کنیم، برنامه چگونه به نظر می رسد. این interface، که برای کاربران ادمین طراحی شده است، به آنها اجازه می دهد تا هر اقدام اداری را در یک نمای واحد (single view) انجام دهند: افزودن، تغییر، حذف یا جستجوی قهرمانان. فقط AdminPanelComponentبه برنامه heroes اضافه می شود، هیچ تغییری در کد domain یا سرویس ها وجود ندارد و ویژگی قابل استفاده مجدد آنها را نشان می دهد.برای راه اندازی interface مدیر، npm run start:adminروی برنچadmin-panel اجرا کنید.برنامه هایی که چندین سرویس خارجی را فراخوانی می کنند.اگر مجبور به فراخوانی چندین سرویس خارجی با هدف مشابه باشید، معماری hexagonal با انگولار سازگار است. بار دیگر، استفاده مجدد از کد domain ساده می شود. فرض کنید می‌خواهیم به‌جای سرویس قهرمان‌های درون حافظه خود، یک سرویس آنلاین فراخوانی کنیم: superherso api توسط Yoann Cribier. همانطور که در برنچ superhero-api می بینید، SuperheroApiAdapterServiceتنها چیزیه که لازم است اضافه کنید .برای برقراری ارتباط برنامه با superhero-api،کد npm run start:superhero-apiروی برنچ superhero-api اجرا کنید. نکته: در مثال ما، اصلاح و حذف هیروها توسط سرویس آنلاین اجرا نمی شود.6. نتیجه گیریاین برنامه کوچک نشان می دهد که می توان معماری hexagonal را با یک برنامه Angular تطبیق داد. برخی از مشکلات که حتی توسط برنامه آموزشی تور قهرمانان مطرح نشده است، با استفاده از آن قابل حل است.این مقاله برگردان شده یک مقاله معتبر درباره این موضوع است.برای دسترسی به مقاله منبع اینجا کلیک کنید.برای مشاهده پست های بیشتر و ارتباط با من از طریق لینکدین اینجا کلیک کنید.امیدوارم براتون مفید واقع شده باشه.</description>
                <category>مصطفی میری</category>
                <author>مصطفی میری</author>
                <pubDate>Fri, 09 Feb 2024 13:16:15 +0330</pubDate>
            </item>
                    <item>
                <title>۲۰ نکته درباره Angular Best Practices در سال ۲۰۲۳</title>
                <link>https://virgool.io/@mostafamiri/angular-best-practice-in-2023-mddfhvw1tul6</link>
                <description>فریم ورک Angular که توسط گوگل توسعه یافته است، یک چارچوب قدرتمند برای برنامه نویسی پویای Single Page Application بر اساس TypeScript است.این یک چارچوب ساختاریافته است نه یک کتابخانه مانند React ، بنابراین توسعه دهندگان باید مثل Angular « فکر کنند ». در پشت صحنه، از بلوک‌های سازنده زیادی استفاده می‌کند: ماژول‌ها، کامپوننت ها، data binding، سرویس ها، directivesها و موارد دیگر. این مقاله می خواهد بهترین شیوه های Angular را برای اجرای بهینه با رعایت قوانین ساده توضیح دهد.1. از Angular CLI برای راه اندازی پروژه استفاده کنید.چه : همیشه از Angular CLI برای ایجاد و مدیریت پروژه های Angular خود استفاده کنید.چرا : Angular CLI یک راه استاندارد و کارآمد برای bootstrap، توسعه، آزمایش و استقرار برنامه های Angular ارائه می دهد.چگونه :# Install Angular CLI globally (if not already installed)
npm install -g @angular/cli

# Create a new Angular project
ng new my-angular-app

# Navigate to the project directory
cd my-angular-app

# Generate a component 
ng generate component my-component


# Run the development server
ng serve2. از اصل Single Responsibility یا (SRP) پیروی کنید.چه : هر کامپوننت، سرویس یا ماژول باید یک مسئولیت واحد داشته باشد.چرا : SRP قابلیت نگهداری، خوانایی و استفاده مجدد کد را ارتقا می دهد. درک و اشکال زدایی کد شما را نیز آسان تر می کند.چگونه :// Example of a component with a single responsibility
import { Component } from &#039;@angular/core&#039;;

@Component({
 selector: &#039;app-user&#039;,
 template: &#039;&lt;div&gt;{{ user.name }}&lt;/div&gt;&#039;,
})
export class UserComponent {
  user = { name: &#039;Marco Martorana&#039; };
}3. بهینه سازی عملکرد با OnPush Change Detection.چه : از ChangeDetectionStrategy.OnPushبرای بهینه سازی change detection در کامپوننت ها استفاده کنید.چرا : change detection OnPush تعداد بررسی ها را کاهش می دهد و عملکرد را به خصوص در برنامه های بزرگ افزایش می دهد.چگونه :import { Component, ChangeDetectionStrategy } from &#039;@angular/core&#039;;

@Component({
  selector: &#039;app-user&#039;,
  template: &#039;&lt;div&gt;{{ user.name }}&lt;/div&gt;&#039;,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class UserComponent {
  user = { name: &#039;Marco Martorana&#039; };
}4. از Reactive Forms بیشتر استفاده کنید.چه : از فرم‌های Reactive انگولار همراه با form validationها استفاده کنید.چرا : Angular دو نوع مختلف فرم را ارائه می دهد: Template Driven و فرم های Reactive . یک فرم Reactive روشی با قابلیت پیش بینی  و مدیریت بیشتری برای مدیریت داده های فرم، از جمله اعتبار سنجی و عملیات async ارائه می دهد. علاوه بر این فریم ورک Angular از کتابخانه RxJS استفاده می کند، که بر اساس برنامه نویسی reactive است و فرم های Reactive از این شیوه استفاده می کند.چگونه :import { Component } from &#039;@angular/core&#039;;
import { FormBuilder, FormGroup, Validators } from &#039;@angular/forms&#039;;

@Component({
  selector: &#039;app-registration&#039;,
  template: `
    &lt;form [formGroup]=&amp;quotregistrationForm&amp;quot (ngSubmit)=&amp;quotonSubmit()&amp;quot&gt;
      &lt;input formControlName=&amp;quotusername&amp;quot placeholder=&amp;quotUsername&amp;quot /&gt;
      &lt;button type=&amp;quotsubmit&amp;quot [disabled]=&amp;quotregistrationForm.invalid&amp;quot&gt;Register&lt;/button&gt;
    &lt;/form&gt;
  `,
})
export class RegistrationComponent {
  registrationForm: FormGroup;

 constructor(private fb: FormBuilder) {
 this.registrationForm = this.fb.group({
      username: [&#039;&#039;, [Validators.required, Validators.minLength(5)]],
    });
  }

  onSubmit() {
 // Manage submission here
  }
}5. ماژول های Lazy Loadچه : lazy load را برای ماژول های feature فعال کنید تا آنها را در زمان نیاز بارگیری کنید.چرا : lazy load حجم اولیه بسته برنامه را کاهش می دهد و سرعت بارگذاری اولیه برنامه را افزایش می دهد. این ماژولارسازی عملکرد را بهبود می‌بخشد.چگونه :import { NgModule } from &#039;@angular/core&#039;;
import { RouterModule, Routes } from &#039;@angular/router&#039;;

const routes: Routes = [
  {
 path: &#039;dashboard&#039;,
 loadChildren: () =&gt; import(&#039;./dashboard/dashboard.module&#039;).then(m =&gt; m.DashboardModule),
  },
 // Other routes...
];

@NgModule({
 imports: [RouterModule.forRoot(routes)],
 exports: [RouterModule],
})
export class AppRoutingModule { }6. از TrackBy با ngFor Directive استفاده کنید.چه : یک تابع برای trackBy در حلقه های ngFor بنویسید.چرا : عملکرد رندرینگ را برای لیست ها بهینه می کند.چگونه :// In component
trackByFn(index: number, item: any): number {
  return item.id;
}

&lt;!-- In template --&gt;
&lt;div *ngFor=&amp;quotlet item of items; trackBy: trackByFn&amp;quot&gt;{{ item.name }}&lt;/div&gt;7. مستندسازی کدچه چیزی : کامنت های معنی دار اضافه کنید و کد خود را مستند کنید.چرا : خوانایی کد را بهبود می بخشد و به توسعه دهندگان دیگر کمک می کند تا کد شما را درک کنند.چگونه :/**
   * This is an sample function that print a message in console and return length of input 
   parameter.
   * @param {string} param - Description of the parameter.
   * @returns {number} Description of the return value.
 */
function helloFunction(name: string): number {
   // Function logic here
   console.log(`Hello ${name} length: ${name.length}`);
   return name.length;
}به علاوه : به منظور تولید مستندات پروژه Angular خود می توانید از این ابزار Compodoc استفاده کنید.8. از Async Pipe برای Observablesها استفاده کنید.چه : از async pipe برای subscribe کردن به observableهای موجود در templatesها استفاده کنید.چرا : subscriptionها را به صورت خودکار مدیریت می کند و با از بین رفتن کامپوننت اتصال را از بین میبرد و از نشت حافظه(memory leak) جلوگیری می کند.چگونه :// In component
data$: Observable&lt;Data&gt;;&lt;!-- In template --&gt;
&lt;div *ngIf=&amp;quotdata$ | async as data&amp;quot&gt;{{ data }}&lt;/div&gt;9. راهنمای Angular Style را دنبال کنید.چه چیزی : برای استانداردهای کدنویسی ، به راهنمای رسمی Angular style پایبند باشید.چرا : سازگاری خوانایی و قابلیت نگهداری را بهبود می بخشد.چگونه :// Bad
function myComponent() {}

// Good
export class MyComponent {}10. از سرویس های Angular برای  نوشتن منطق یا logic استفاده کنید.چه : business logic را در سرویس های Angular قرار دهید، نه کامپوننت ها.چرا : جداسازی بخش ها و قابلیت استفاده مجدد از منطق.چگونه :@Injectable({
  providedIn: &#039;root&#039;,
})
export class MyService {
 getData(): Observable&lt;Data&gt; {
 // Write Service logic (business logic) here
  }
}@Component({
  selector: &#039;app-my-component&#039;,
  templateUrl: &#039;my-component.component.html&#039;,
})
export class MyComponent {
   currentValue: Data;
 
 constructor(private myService: MyService) {
      myService.getData().subscribe(v =&gt; {
 this.currentValue = v;
      });
   }
}11. از Lifecycle Hooks استفاده کنید.چه : انگولار lifecycle hook را برای مدیریت رفتار کامپوننت ها در لحظات کلیدی فراهم می کند. از متد هایی مانند ngOnInit، ngOnChangesو ngOnDestroyو غیره به درستی استفاده کنید. چرا : استفاده صحیح از متدهای initialization ، lifecycle hook کارآمد کامپوننت ها، اجرای بهتر change detection و پاکسازی بهتر کامپوننت ها را تضمین می کند.چگونه :import { Component, OnInit, OnDestroy } from &#039;@angular/core&#039;;

@Component({
 selector: &#039;app-example&#039;,
 template: &#039;...&#039;,
})
export class ExampleComponent implements OnInit, OnDestroy {
 constructor(){}

 ngOnInit() {
 // Initialization logic here
  }

 ngOnDestroy() {
 // Cleanup logic here
  }
}12. الگوهای مناسب Subscription/Unsubscription را دنبال کنید.چه : از memory leak با unsubscribe کردن observableها در هنگام از بین رفتن کامپوننت ها جلوگیری کنید.چرا : با unsubscribe کردن هنگام تخریب کامپوننت ها از subscribeهای طولانی مدت و بی استفاده جلوگیری می کنید، که می تواند منجر به نشت حافظه و رفتار غیرمنتظره شود.چگونه :import { Component, OnDestroy } from &#039;@angular/core&#039;;
import { Subscription } from &#039;rxjs&#039;;

@Component({
 selector: &#039;app-example&#039;,
 template: &#039;...&#039;,
})
export class ExampleComponent implements OnInit, OnDestroy {
 private subscription: Subscription;

 constructor(private dataService: DataService) {
  }

 ngOnInit() {
 this.subscription = this.dataService.getData().subscribe(data =&gt; {
 // Handle data
    });
  }

 ngOnDestroy() {
 this.subscription.unsubscribe();
  }
}13. بجای نوع «any» از interfaceها استفاده کنید.چه : از interface ها برای تعریف نوع اشیا به جای استفاده از نوع &quot;any&quot; استفاده کنید.چرا : interface ها ایمنی نوع داده ها و خوانایی بهتر کد را فراهم می کنند. هنگام نوشتن با IDE ترجیحی خود (VSCode، IntelliJ Idea یا موارد دیگر)، در طول توسعه راهنمایی می شویم. علاوه بر این ما از Angular استفاده می کنیم که بر اساس Typescript است که از برنامه نویسی شی گرا پشتیبانی می کند. OOP راهی برای سازماندهی کد و ایجاد برنامه های نرم افزاری قابل استفاده مجدد و ماژولار ارائه می دهد. برای انجام این کار، وراثت، encapsulation و polymorphism وجود دارند، اما اول از همه ما انواع داده ها را داریم. پس چرا به جای استفاده از &quot;any&quot; به درستی از آنها استفاده نمی کنیم؟چگونه :interface User {
 id: number;
 name: string;
}

function getUserInfo(user: User): string {
 // Access user properties safely
 return user.name + &amp;quot (&amp;quot + user.id + &amp;quot)&amp;quot
}14. سازگاری و استایل کدچه چیزی : یک استایل کدنویسی ثابت را دنبال کنید و به راهنمای استایل Angular پایبند باشید.چرا : کد یکنواخت خوانایی و همکاری بین توسعه دهندگان را افزایش می دهد.چگونه :برای اجرای استانداردهای کدنویسی از یک linter مانند ESLint یا TSLint استفاده کنید.راهنمای استایل Angular را دنبال کنید: https://angular.io/guide/styleguide15. تست واحد (Unit test) و تست e2eچه : تست های واحد را با استفاده از ابزارهایی مانند Jasmine و Karma بنویسید. با استفاده از یک ابزار خاص برای e2e مانند Cypress، تست end-to-end انجام دهید.چرا : تست قابلیت اطمینان کد را تضمین می کند، اشکالات را زودتر تشخیص می دهد و تغییرات کدهای آینده را تسهیل می کند.چگونه (Unit test):// example.component.spec.ts// example.e2e-spec.ts
describe(&#039;Example App&#039;, () =&gt; {
  it(&#039;should display welcome message&#039;, () =&gt; {
    cy.visit(&#039;/&#039;);
    cy.get(&#039;app-root&#039;).contains(&#039;Welcome to your Angular App!&#039;);
  });
});
describe(&#039;ExampleComponent&#039;, () =&gt; {
 it(&#039;should create&#039;, () =&gt; {
 const component = new ExampleComponent();
 expect(component).toBeTruthy();
  });
});چگونه (تست e2e):// example.e2e-spec.ts
describe(&#039;Example App&#039;, () =&gt; {
  it(&#039;should display welcome message&#039;, () =&gt; {
    cy.visit(&#039;/&#039;);
    cy.get(&#039;app-root&#039;).contains(&#039;Welcome to your Angular App!&#039;);
  });
});16. از عملگرهای RxJS به طور موثر استفاده کنید.چه : عملگرهای RxJS مانند map, filter, و mergeMapتبدیل و دستکاری observable را به طور موثر انجام می دهند.چرا : عملگرها عملیات پیچیده async را ساده می کنند و کد را مختصر و خوانا می کنند.چگونه :import { from } from &#039;rxjs&#039;;
import { map, filter } from &#039;rxjs/operators&#039;;

const numbers = from([1, 2, 3, 4, 5]);

numbers.pipe(
 filter(num =&gt; num % 2 === 0), // Filters even numbers
 map(num =&gt; num * 2) // Doubles the filtered numbers
).subscribe(result =&gt; {
 console.log(result); // Output: 4, 8
});import { Observable } from &#039;rxjs&#039;;
import { map, filter } from &#039;rxjs/operators&#039;;

// Example using map and filter operators
observable.pipe(
 filter(data =&gt; data !== null), // Filter out null values
 map(data =&gt; data.property),    // Extract a specific property
).subscribe(transformedData =&gt; {
 // Process transformed data
});17: از subscribeهای تودرتو خودداری کنید.چه : از subscribe کردن درون یک subscribe دیگر برای جلوگیری از callback hell  و بهبود خوانایی کد خودداری کنید.چرا : subscribeهای تودرتو می‌توانند به کدهایی منجر شوند که نگهداری آن دشوار است، که به عنوان pyramid of doom «هرم عذاب» شناخته می‌شود. همچنین در صورت عدم مدیریت صحیح می تواند باعث نشت حافظه شود.چگونه :import { Observable } from &#039;rxjs&#039;;
import { switchMap } from &#039;rxjs/operators&#039;;

// Bad Practice (Nested Subscriptions)
observable1.subscribe(data1 =&gt; {
  observable2.subscribe(data2 =&gt; {
 // Process data
  });
});

// Good Practice (Using switchMap)
observable1.pipe(
 switchMap(data1 =&gt; {
 return observable2;
  })
).subscribe(data2 =&gt; {
 // Process data
});18: استفاده مجدد از کامپوننت هاچه : کامپوننت های قابل استفاده مجدد را برای کپسوله کردن عملکردها و توابع خاص و عناصر UI مربوط به آنها ایجاد کنید.چرا : استفاده مجدد از کامپوننت ها قابلیت نگهداری را افزایش می دهد، تکرار را کاهش می دهد و ثبات کلی برنامه را افزایش می دهد.چگونه :// Reusable Component (Example)
@Component({
 selector: &#039;app-custom-button&#039;,
 template: &#039;&lt;button&gt;{{label}}&lt;/button&gt;&#039;
})
export class CustomButtonComponent {
 @Input() label: string;
 
 // Component logic and methods
}

// Implementation in Parent Component
&lt;app-custom-button [label]=&amp;quot&#039;Click me&#039;&amp;quot&gt;&lt;/app-custom-button&gt;19: از عملگرهای Mapping با مرتبه بالاتر استفاده کنید.چه : از عملگرهای mapping مرتبه بالاتر مانند mergeMap، concatMapیا exhaustMapبر اساس نیاز استفاده کنید.چرا : عملگرهای mapping درجه بالاتر به شما امکان می دهند همزمانی را مدیریت کنید و انتشار observable را به شیوه ای کنترل شده مدیریت کنید و از مسائلی مانند race جلوگیری کنید.چگونه :import { Observable, from } from &#039;rxjs&#039;;
import { mergeMap } from &#039;rxjs/operators&#039;;

// Using mergeMap to handle concurrent requests
observable.pipe(
 mergeMap(data =&gt; {
 return from(someAsyncOperation(data));
  })
).subscribe(result =&gt; {
 // Process the merged result
});20: از TakeUntil با Subject برای unsubscribe دستی استفاده کنید.چه:در صورت امکان از پایپ async استفاده کنید (این توسط Angular پیشنهاد شده است). هنگامی که نمی توانید از async pipe استفاده کنید، از  عملگرtakeUntil به همراه  Subjectبرایunsubscribe دستی از observableها استفاده کنید.چرا:عملگر takeUntilبه شما این امکان را می دهد تا در صورت وقوع یک رویداد خاص (که توسط Subject کنترل می شود) از observableها unsubscribe کنید و از نشت حافظه جلوگیری کنید.چگونه :import { Component, OnDestroy } from &#039;@angular/core&#039;;
import { Subject } from &#039;rxjs&#039;;
import { takeUntil } from &#039;rxjs/operators&#039;;

@Component({
 selector: &#039;app-example&#039;,
 template: &#039;Example Component&#039;
})
export class ExampleComponent implements OnDestroy {
 private unsubscribe$: Subject&lt;void&gt; = new Subject&lt;void&gt;();

 constructor(private dataService: DataService) {
 this.dataService.getData()
      .pipe(takeUntil(this.unsubscribe$))
      .subscribe(data =&gt; {
 // Handle data
      });
  }

 ngOnDestroy() {
 this.unsubscribe$.next();
 this.unsubscribe$.complete();
  }
}با پیروی از این best practiceها ، توسعه‌دهندگان Angular می‌توانند کدی کارآمدتر، با خوانایی و قابلیت نگهداری بیشتر را ایجاد کنند و در عین حال از ویژگی‌های قدرتمند ارائه‌شده توسطAngular و RxJS بهره‌مند شوند .این مقاله برگردان شده یک مقاله معتبر درباره این موضوع است.برای دسترسی به مقاله منبع اینجا کلیک کنید.برای مشاهده پست های بیشتر و ارتباط با من از طریق لینکدین اینجا کلیک کنید.امیدوارم براتون مفید واقع شده باشه.</description>
                <category>مصطفی میری</category>
                <author>مصطفی میری</author>
                <pubDate>Sat, 27 Jan 2024 13:37:25 +0330</pubDate>
            </item>
                    <item>
                <title>درک Memory Leak در Angular</title>
                <link>https://virgool.io/@mostafamiri/%D8%AF%D8%B1%DA%A9-memory-leak-%D8%AF%D8%B1-angular-gmoqqk0yqmjv</link>
                <description> نشت حافظه اگر به درستی مدیریت نشود، می تواند یک مشکل جدی در برنامه های Angular باشد. این اتفاق می تواند به تدریج عملکرد برنامه را کاهش دهد، باعث crashe های غیرمنتظره شود و منابع حافظه را بیش از حد مصرف کند. در این مقاله مباحثی مثل تعریف Memory Leak ، چرا در Angular رخ می دهد، و چگونه می توان آن را شناسایی و از آن جلوگیری کرد را بررسی خواهیم کرد.نشت حافظه (Memory Leak) چیست؟نشت حافظه زمانی اتفاق می‌افتد که حافظه تخصیص‌یافته ،دیگر مورد نیاز نباشد اما آزاد نشود، که منجر به مصرف حافظه می‌شود که در طول زمان افزایش می‌یابد. در Angular، نشت حافظه اغلب زمانی اتفاق می‌افتد که کامپوننت ها یا سایر اشیاء به درستی حذف نمی‌شوند و از عملکرد garbage collector در بازیابی حافظه جلوگیری می‌کنند.علل شایع Memory Leak در Angular:الف) Subscription ها : عدم unsubscribe کردن observable ها یا event listener ها می‌تواند reference ها را زنده نگه دارد و از بین نبرد و باعث نشت حافظه شود. اطمینان حاصل کنید که subscription خود را در چرخه حیات ngOnDestroy لغو می کنید.مثال:private subscription: Subscription;

ngOnInit() {
   this.subscription = someObservable.subscribe();
}

ngOnDestroy() {
   this.subscription.unsubscribe();
}ب) DOM Event Handlers : اتصال event handler ها به طور مستقیم به عناصر DOM بدون پاکسازی مناسب می تواند منجر به نشت حافظه شود. وقتی کامپوننت از بین رفت، event handler ها را حذف کنید.مثال:ngOnInit() {
   this.onClickHandler = this.onClick.bind(this);
   document.addEventListener(&#039;click&#039;, this.onClickHandler);
}

ngOnDestroy() {
   document.removeEventListener(&#039;click&#039;, this.onClickHandler);
}ج) حفظ منابع (References) : نگه داشتن منابع غیر ضروری، مانند ذخیره اشیاء در متغیرهای سراسری یا service هایی که به مدت طولانی درحال کارکرد هستند، می تواند از حذف صحیح آنها جلوگیری کند. لغو کردن آن ارجاع یا Null کردن آنها پیشنهاد می شود.مثال:@Injectable()
export class DataService {
   private cachedData: any;
  // ...
  storeData(data: any) {
      this.cachedData = data;    // از نگهداری غیر ضروری خودداری کنید
   }
}تشخیص Memory Leak :برای شناسایی نشت حافظه، می‌توانید از ابزارهای مرورگر مانند Chrome DevTools استفاده کنید. استفاده از حافظه را در طول زمان کنترل کنید و به دنبال افزایش مداوم یا ناگهانی آن باشید. برای یافتن مقصران احتمالی اشیاء باقی مانده را بازرسی کنید.ابزار Chrome DevTools چندین ویژگی را ارائه می دهد که به شناسایی نشت حافظه کمک می کند:پنل حافظه (Memory panel) : استفاده از حافظه را نظارت کنید و نشت های احتمالی حافظه را در طول زمان پیگیری کنید. در inspector کروم به تب performance بروید و ضبط را شروع کنید، اکنون برخی از فعالیت ها را در وبسایت انجام دهید و سپس ضبط را متوقف کنید تا نمودار مصرف حافظه را در طول زمان مشاهده کنید.استفاده از حافظه عکس‌های فوری (Heap snapshots) : عکس‌های فوری از پشته جاوا اسکریپت بگیرید و آنها را برای شناسایی اشیاء باقی مانده مقایسه کنید.عکس های فوری پشته ای - 1 عکس های فوری پشته ای - 2همچنین می توانید هنگام انتخاب هر شی از عکس فوری بالا، لیست نگهدارنده ها را مشاهده کنید. اگر object ای را پیدا کردید که اندازه بزرگی را که انتظارش را ندارید اشغال کرد، ممکن است مشکل از آن باشد و می توانید اینجا ببینید چه مرجعی مانع از پاکسازی آن شده است.لیست نگهدارنده ها شما می توانید مرجع node انتخاب شده را به دو صورت پیدا کنید.1 - فقط موس را روی node ببرید.شناور2 - به سادگی 0$ را در کنسول تایپ کنید تا مرجع node انتخاب شده را دریافت کنید.$0 را تایپ کنید جدول‌های زمانی تخصیص : ایجاد شی را تجزیه و تحلیل کنید و نحوه استفاده از حافظه را درک کنید.جدول زمانی تخصیصجلوگیری از Memory Leak :الف) مدیریت چرخه حیات کامپوننت ها : اطمینان حاصل کنید که subscribe های کامپوننت ها به درستی unsubscribe می‌ شوند، event handlerها به موقع حذف می شوند و هر گونه مرجع نگه‌داشته‌شده در چرخه حیات ngOnDestroy را پاکسازی می شوند.ngOnDestroy() {
    this.subscription.unsubscribe();
}ب) از عملگرهای RxJS استفاده کنید : از عملگرهایی مانند takeUntil یا takeWhile برای unsubscribe خودکار از observable ها زمانی که یک شرط خاص برآورده می‌شود یا زمانی که کامپوننت از بین می‌رود، استفاده کنید.مثال:import { Subject } from &#039;rxjs&#039;;
import { takeUntil } from &#039;rxjs/operators&#039;;

private unsubscribe$: Subject&lt;void&gt; = new Subject&lt;void&gt;();

ngOnInit() {
    someObservable.pipe(takeUntil(this.unsubscribe$)).subscribe();
}

ngOnDestroy() {
    this.unsubscribe$.next();
    this.unsubscribe$.complete();
}ج) تزریق وابستگی انگولار (Dependency Injection) : از DI برای مدیریت چرخه حیات سرویس ها استفاده کنید و اطمینان حاصل کنید که در صورت عدم نیاز به درستی تمیز می شوند.مثال:// service example
@Injectable({
    providedIn: &#039;root&#039;
})
class TestService {}
}‍‍@Component({ … })
class TestComponent {
     constructor(private service: TestService) {}
}نتیجه گیری :نشت حافظه اگر به درستی مدیریت نشود می تواند تأثیر نامطلوبی در برنامه های Angular بگذارد. با درک علل، تشخیص زودهنگام آنها و پیروی از بهترین شیوه ها برای مدیریت چرخه عمر کامپوننت ها، می توانید از نشت حافظه جلوگیری کنید و عملکرد و پایداری برنامه های Angular را بهبود دهید. به یاد داشته باشید، مدیریت فعال حافظه برای اجرای روان و کارآمد برنامه Angular شما بسیار مهم است.این مقاله برگردان شده یک مقاله معتبر درباره این موضوع است.برای دسترسی به مقاله منبع اینجا کلیک کنید.برای مشاهده پست های بیشتر و ارتباط با من از طریق لینکدین اینجا کلیک کنید.امیدوارم براتون مفید واقع شده باشه.</description>
                <category>مصطفی میری</category>
                <author>مصطفی میری</author>
                <pubDate>Tue, 16 Jan 2024 12:29:35 +0330</pubDate>
            </item>
                    <item>
                <title>سه ویژگی ng-template، ng-container و ngTemplateOutlet - راهنمای کامل الگوهای Angular</title>
                <link>https://virgool.io/@mostafamiri/ngtemplateoutlet-wd6g1jwps565</link>
                <description>احتمالاً قبلاً با دستورالعمل  ng-template از Angular core مواجه شده اید، مثلاً هنگام استفاده از ngIf/else یا ngSwitch.دستورالعمل ng-template و دستورالعمل ngTemplateOutlet ویژگی های بسیار قدرتمند Angular هستند که کاربرد های زیادی دارند.این دستورالعمل‌ها اغلب با ng-container استفاده می‌شوند، و از آنجایی که این دستورالعمل‌ها برای استفاده با هم طراحی شده‌اند، اگر همه آنها را یکجا یاد بگیریم، کمک بیشتری خواهند کرد.توجه: تمام کدهای این پست را می توانید در این مخزن Github پیدا کنید .فهرست مطالبدر این پست به موضوعات زیر می پردازیم:مقدمه ای بر دستورالعمل ng-templateمتغیرهای Template Inputاستفاده از دستورالعمل ng-template با ngIfاگر دستور و ng-template را حذف کنید.دستور template references ng-template و  TemplateRef قابل تزریقکامپوننت های قابل تنظیم با Template Partial @Inputsدستورالعمل ng-container ، چه زمانی از آن استفاده کنیم؟داینامیک کردن Template با دستورالعمل سفارشی ngTemplateOutletویژگی های Template outlet @Inputمثال ترکیبی نهاییخلاصه و نتیجه گیریمقدمه ای بر دستورالعمل ng-templateهمانطور که از نام آن مشخص می شود، دستورالعمل ng-template یک template انگولار را نشان می دهد: این بدان معنی است که محتوای این تگ بخشی از یک template خواهد بود، که می توان آن را با سایر template ها ترکیب کرد تا قالب نهایی کامپوننت را تشکیل دهد.انگولار در حال حاضر از ng-template در لایه های زیرین بسیاری از دستورالعمل‌های ساختاری استفاده می‌کند که ما همیشه از آن‌ها استفاده می‌کنیم مثلngIf، ngForو ngSwitch.بیایید با یک مثال شروع به یادگیری ng-template کنیم. در اینجا ما دو دکمه تب در یک کامپوننت را تعریف می کنیم :@Component({
  selector: &#039;app-root&#039;,
  template: `      
      &lt;ng-template&gt;
          &lt;button class=&amp;quottab-button&amp;quot 
                  (click)=&amp;quotlogin()&amp;quot&gt;{{loginText}}&lt;/button&gt;
          &lt;button class=&amp;quottab-button&amp;quot 
                  (click)=&amp;quotsignUp()&amp;quot&gt;{{signUpText}}&lt;/button&gt;
      &lt;/ng-template&gt;
  `})
export class AppComponent {
    loginText = &#039;Login&#039;;
    signUpText = &#039;Sign Up&#039;; 
    lessons = [&#039;Lesson 1&#039;, &#039;Lessons 2&#039;];

    login() {
        console.log(&#039;Login&#039;);
    }

    signUp() {
        console.log(&#039;Sign Up&#039;);
    }
}اگر مثال بالا را امتحان کنید، ممکن است تعجب کنید و متوجه شوید این مثال چیزی را روی صفحه نمایش نمی دهد !این طبیعی است و رفتار مورد انتظار ماست. این به این دلیل است که با تگ ng-template ما به سادگی یک الگو را تعریف می کنیم، اما هنوز از آن استفاده نمی کنیم.دستورالعمل‌ng-templateوngIfاحتمالاً برای اولین بار هنگام اجرای یک سناریوی if/else مانند این یکی، با ng-template مواجه شده اید:&lt;div class=&amp;quotlessons-list&amp;quot *ngIf=&amp;quotlessons else loading&amp;quot&gt;
  ... 
&lt;/div&gt;

&lt;ng-template #loading&gt;
    &lt;div&gt;Loading...&lt;/div&gt;
&lt;/ng-template&gt;این یک استفاده بسیار متداول از عملکرد ngIf/else است: ما یک الگوی جایگزینloadingرا در حالی که منتظر دریافت داده ها از backend هستیم نمایش می دهیم.همانطور که می بینیم، عبارت else به یک الگو اشاره می کند که loadingنام دارد . این نام از طریق یک template reference و با استفاده از loading# به آن اختصاص داده شد .اما در لایه های زیرین یک ng-template ضمنی دوم نیز ایجاد می شود! بیایید نگاهی بیندازیم :&lt;ng-template [ngIf]=&amp;quotlessons&amp;quot [ngIfElse]=&amp;quotloading&amp;quot&gt;
   &lt;div class=&amp;quotlessons-list&amp;quot&gt;
     ... 
   &lt;/div&gt;
&lt;/ng-template&gt;

&lt;ng-template #loading&gt;
    &lt;div&gt;Loading...&lt;/div&gt;
&lt;/ng-template&gt;این همان چیزی است که در داخل اتفاق می‌افتد، زیرا Angular دستور ساختاری مختصرتر ngIf*را ارائه می‌کند. عنصری که دستورالعمل ساختاری ngIf بر روی آن اعمال شد به یک ng-template منتقل شده است.عبارت ngIf* با استفاده از [ngIf]و[ngIfElse] به دو دستورالعمل جداگانه تقسیم شده و اعمال شده است.و این فقط یک نمونه از یک مورد خاص با ngIf است. اما با ngFor و ngSwitch یک فرآیند مشابه نیز رخ می دهد.همه این دستورالعمل ها بسیار رایج هستند، بنابراین به این معنی است که این الگوها در همه جا در Angular وجود دارند، چه به طور ضمنی یا صریح.اما با توجه به این مثال، ممکن است یک سوال به ذهن خطور کند:اگر دستورالعمل های ساختاری متعددی برای یک عنصر اعمال شود، چگونه کار می کند؟دستورالعمل های ساختاری متعددبیایید ببینیم چه اتفاقی می‌افتد اگر برای مثال بخواهیم از ngIfو ngForدر همان عنصر استفاده کنیم:&lt;div class=&amp;quotlesson&amp;quot *ngIf=&amp;quotlessons&amp;quot *ngFor=&amp;quotlet lesson of lessons&amp;quot&gt;
    &lt;div class=&amp;quotlesson-detail&amp;quot&gt;
        {{lesson | json}}
    &lt;/div&gt;
&lt;/div&gt;این کار نمی کند! در عوض، پیام خطای زیر را دریافت می کنیم:Uncaught Error: Template parse errors:
Can&#039;t have multiple template bindings on one element. Use only one attribute 
named &#039;template&#039; or prefixed with *این بدان معنی است که نمی توان دو دستورالعمل ساختاری را برای یک عنصر اعمال کرد. برای انجام این کار، باید کاری شبیه به این انجام دهیم:&lt;div *ngIf=&amp;quotlessons&amp;quot&gt;
    &lt;div class=&amp;quotlesson&amp;quot *ngFor=&amp;quotlet lesson of lessons&amp;quot&gt;
        &lt;div class=&amp;quotlesson-detail&amp;quot&gt;
            {{lesson | json}}
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;در این مثال، دستور ngIf را به یک div بیرونی منتقل کرده ایم، اما برای اینکه این کار عمل کند، باید آن عنصر div اضافی را ایجاد کنیم.این راه حل کار می کند، اما آیا راهی برای انجام این کار بدون نیاز به ایجاد یک عنصر (div) اضافی وجود دارد؟بله و این دقیقا همان چیزی است که دستورالعمل ساختاریng-containerبه ما اجازه می دهد!دستورالعمل ng-containerبرای جلوگیری از ایجاد آن div اضافی، می‌توانیم به جای آن از دستورالعمل ng-container استفاده کنیم:&lt;ng-container *ngIf=&amp;quotlessons&amp;quot&gt;
    &lt;div class=&amp;quotlesson&amp;quot *ngFor=&amp;quotlet lesson of lessons&amp;quot&gt;
        &lt;div class=&amp;quotlesson-detail&amp;quot&gt;
            {{lesson | json}}
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/ng-container&gt;همانطور که می بینیم، دستورالعمل ng-container عنصری را در اختیار ما قرار می دهد که می توانیم یک دستورالعمل ساختاری را به بخشی از صفحه متصل کنیم، بدون اینکه نیازی به ایجاد یک عنصر اضافی فقط برای آن باشد.یک مورد استفاده دیگر برای دستورالعمل ng-container وجود دارد و آن میتواند یک مکان نگهدار برای تزریق یک template به صورت پویا به صفحه ارائه دهد.ایجاد template پویا با دستورالعملngTemplateOutletتوانایی ایجاد template references و ارجاع آنها به دستورالعمل های دیگر مانند ngIf تنها آغاز کار است.همچنین می‌توانیم خود template را برداریم و با استفاده از دستورالعملngTemplateOutlet، آن را در هر نقطه از صفحه نمونه‌سازی کنیم :&lt;ng-container *ngTemplateOutlet=&amp;quotloading&amp;quot&gt;&lt;/ng-container&gt;ما در اینجا می‌توانیم ببینیم که ng-container چگونه به این مورد استفاده کمک می‌کند: ما از آن برای نمونه‌سازی قالب loadingکه در بالا تعریف کردیم استفاده می‌کنیم.ما از طریق loadingtemplate referencesبه قالب loading اشاره می کنیم و از دستورالعمل ساختاریngTemplateOutlet برای نمونه سازی الگو استفاده می کنیم.ما می‌توانیم به تعداد دلخواه تگngTemplateOutletبه صفحه اضافه کنیم و تعدادی قالب مختلف را نمونه‌سازی کنیم. مقدار ارسال شده به این دستورالعمل می تواند هر عبارتی باشد که در یک template reference ارزیابی می شود، در ادامه در این مورد بیشتر توضیح خواهیم داد.اکنون که می‌دانیم چگونه الگوها را نمونه‌سازی کنیم، بیایید در مورد آنچه که درون قالب در دسترس است صحبت کنیم.محتوای الگو ( Template Context )یک سوال کلیدی در مورد قالب ها این است که چه چیزی در داخل آنها قابل مشاهده است؟در داخل بدنه تگ ng-template، ما به همان context variables که در قالب بیرونی قابل مشاهده هستند، مانند متغیرlessonsدسترسی داریم.و این به این دلیل است که تمام نمونه های ng-template به همان زمینه ای که در آن تعبیه شده اند نیز دسترسی دارند.اما هر قالب می تواند مجموعه ای از متغیرهای ورودی خود را نیز تعریف کند! در واقع، هر الگو یک شی زمینه حاوی تمام متغیرهای ورودی خاص الگو را مرتبط کرده است.بیایید به یک مثال نگاه کنیم:@Component({
  selector: &#039;app-root&#039;,
  template: `      

&lt;ng-template #estimateTemplate let-lessonsCounter=&amp;quotestimate&amp;quot&gt;
    &lt;div&gt; Approximately {{lessonsCounter}} lessons ...&lt;/div&gt;
&lt;/ng-template&gt;

&lt;ng-container 
   *ngTemplateOutlet=&amp;quotestimateTemplate;context:ctx&amp;quot&gt;
&lt;/ng-container&gt;
`})
export class AppComponent {

    totalEstimate = 10;
    ctx = {estimate: this.totalEstimate};
  
}در اینجا به تفکیک این مثال آمده است:این الگو برخلاف قالب های قبلی دارای یک متغیر ورودی است (می تواند چندین متغیر نیز داشته باشد)متغیر ورودیlessonsCounterنام دارد و از طریق یک ویژگی ng-template با استفاده از پیشوندlet-تعریف می شود.متغیر lessonsCounter در داخل بدنه ng-template قابل مشاهده است، اما خارج از آن قابل مشاهده نیست.محتوای این متغیر با عبارتی که به ویژگی let-lessonsCounter اختصاص داده شده است تعیین می شود.این عبارت در برابر یک context object ارزیابی می شود که ngTemplateOutletهمراه با الگو برای نمونه سازی به آن ارسال می شودسپس این context object باید دارای یک ویژگی به نامestimate باشد تا هر مقداری در قالب نمایش داده شود.شی context از طریق ویژگی context به ngTemplateOutletارسال می شود، که می تواند هر عبارتی را که برای یک شی ارزیابی می کند دریافت کند.با توجه به مثال بالا، این چیزی است که به صفحه نمایش داده می شود:Approximately 10 lessons ...این به ما دید کلی خوبی از نحوه تعریف و نمونه سازی قالب های خودمان می دهد.کار دیگری که می توانیم انجام دهیم این است که با یک الگو در سطح خود کامپوننت تعامل داشته باشیم: بیایید ببینیم چگونه می توانیم این کار را انجام دهیم.مرجع الگو یا Template Referencesهمانطور که می‌توانیم با استفاده از یک template refrence به قالب loading مراجعه کنیم، می‌توانیم قالبی را نیز با استفاده از دکوراتورViewChild مستقیماً به کامپوننت خود تزریق کنیم :@Component({
  selector: &#039;app-root&#039;,
  template: `      
      &lt;ng-template #defaultTabButtons&gt;

          &lt;button class=&amp;quottab-button&amp;quot (click)=&amp;quotlogin()&amp;quot&gt;
            {{loginText}}
          &lt;/button&gt;

          &lt;button class=&amp;quottab-button&amp;quot (click)=&amp;quotsignUp()&amp;quot&gt;
            {{signUpText}}
          &lt;/button&gt;

      &lt;/ng-template&gt;
`})
export class AppComponent implements OnInit {

    @ViewChild(&#039;defaultTabButtons&#039;)
    private defaultTabButtonsTpl: TemplateRef&lt;any&gt;;

    ngOnInit() {
        console.log(this.defaultTabButtonsTpl);
    }

}همانطور که می بینیم، الگو را می توان مانند هر عنصر DOM یا کامپوننتی تزریق شود، با ارائه نام مرجع الگو defaultTabButtonsبه دکوراتورViewChildتزریق کرد.این بدان معناست که قالب ها در سطح کلاس کامپوننت نیز قابل دسترسی هستند و ما می توانیم کارهایی مانند انتقال آنها به اجزای فرزند انجام دهیم!مثالی از اینکه چرا می‌خواهیم این کار را انجام دهیم، ایجاد یک کامپوننت قابل تنظیم‌تر است، جایی که می‌توان نه تنها یک پارامتر پیکربندی یا شی پیکربندی را به آن ارسال کرد: ما همچنین می‌توانیم یک الگو را به عنوان پارامتر ورودی ارسال کنیم .کامپوننت های قابل تنظیم با Inputs@اجازه دهید به عنوان مثال یک tab container را در نظر بگیریم، جایی که می‌خواهیم به کاربر کامپوننت امکان پیکربندی ظاهر و احساس دکمه‌های تب را بدهیم.در اینجا به نظر می رسد، ما با تعریف الگوی سفارشی برای دکمه های کامپوننت والد شروع می کنیم:@Component({
  selector: &#039;app-root&#039;,
  template: `      
&lt;ng-template #customTabButtons&gt;
    &lt;div class=&amp;quotcustom-class&amp;quot&gt;
        &lt;button class=&amp;quottab-button&amp;quot (click)=&amp;quotlogin()&amp;quot&gt;
          {{loginText}}
        &lt;/button&gt;
        &lt;button class=&amp;quottab-button&amp;quot (click)=&amp;quotsignUp()&amp;quot&gt;
          {{signUpText}}
        &lt;/button&gt;
    &lt;/div&gt;
&lt;/ng-template&gt;
&lt;tab-container [headerTemplate]=&amp;quotcustomTabButtons&amp;quot&gt;&lt;/tab-container&gt;      
`})
export class AppComponent implements OnInit {

}و سپس در کامپوننت tab container، می‌توانیم یک ویژگی ورودی تعریف کنیم که آن نیز یک الگو به نام headerTemplate:@Component({
    selector: &#039;tab-container&#039;,
    template: `
    
&lt;ng-template #defaultTabButtons&gt;
    
    &lt;div class=&amp;quotdefault-tab-buttons&amp;quot&gt;
        ...
    &lt;/div&gt;
    
&lt;/ng-template&gt;

&lt;ng-container 
  *ngTemplateOutlet=&amp;quotheaderTemplate ? headerTemplate: defaultTabButtons&amp;quot&gt;
    
&lt;/ng-container&gt;

... rest of tab container component ...

`})
export class TabContainerComponent {
    @Input()
    headerTemplate: TemplateRef&lt;any&gt;;
}در این مثال ترکیبی نهایی، چند چیز در اینجا در حال انجام است. بیایید این را تجزیه کنیم:یک الگوی پیش فرض برای دکمه های برگه تعریف شده است کهdefaultTabButtons نامیده می شود.این الگو تنها در صورتی استفاده خواهد شد که ویژگی ورودی headerTemplateتعریف نشده ( undefined ) باقی بماند.اگر ویژگی تعریف شده باشد، در عوض از الگوی ورودی سفارشی ارسال شده headerTemplateبرای نمایش دکمه ها استفاده می شود.الگوی headers با استفاده از ویژگی ngTemplateOutletدر داخل یک ng-container نمونه سازی می شود.تصمیم در مورد استفاده از الگو (پیش‌فرض یا سفارشی) با استفاده از عبارت سه‌گانه گرفته می‌شود، اما اگر این منطق پیچیده بود، می‌توانیم آن را به  متد controller نیز واگذار کنیم.نتیجه نهایی این طراحی این است که در صورت عدم ارائه الگوی سفارشی، کانتینر تب ظاهر و حالت پیش فرض را برای دکمه های تب نمایش می دهد، اما در صورت موجود بودن از قالب سفارشی استفاده می کند.خلاصه و نتیجه گیریدستورالعمل های اصلی ng-container، ng-template و ngTemplateOutlet همگی با هم ترکیب می شوند تا به ما امکان ایجاد کامپوننت های بسیار پویا و قابل تنظیم را می دهند.ما حتی می‌توانیم ظاهر یک کامپوننت را بر اساس الگوهای ورودی کاملاً تغییر دهیم ، و می‌توانیم یک الگو تعریف کنیم و در مکان‌های مختلف برنامه نمونه‌سازی کنیم.و این تنها یکی از راه های ممکن برای ترکیب این ویژگی ها است!این مقاله برگردان شده یک مقاله معتبر درباره این موضوع است.برای دسترسی به مقاله منبع اینجا کلیک کنید.برای مشاهده پست های بیشتر و ارتباط با من از طریق لینکدین اینجا کلیک کنید.امیدوارم براتون مفید واقع شده باشه.</description>
                <category>مصطفی میری</category>
                <author>مصطفی میری</author>
                <pubDate>Fri, 17 Nov 2023 00:37:08 +0330</pubDate>
            </item>
                    <item>
                <title>تغییر در ورودی کامپوننت ها با تابع Transform در Angular 16</title>
                <link>https://virgool.io/@mostafamiri/transform-input-values-in-angular-vavoezr3sa9p</link>
                <description>ورودی های کامپوننت ها ( ()input@ ) روشی برای برقراری ارتباط بین کامپوننت ها هستند. آنها برای انتقال داده ها از یک کامپوننت والد به یک کامپوننت فرزند استفاده می شوند. مواقعی وجود دارد که می خواهیم مقدار یک ورودی کامپوننت را هنگامی که به کامپوننت فرزند ارسال می شود، تبدیل کنیم. برای مثال، می‌خواهیم یک تاریخ را قالب‌بندی کنیم، پیشوند یا پسوند به یک رشته اضافه کنیم، رشته را به عدد تبدیل کنیم یا یک رشته را به یک بولین تبدیل کنیم. این کار را می توانیم با استفاده از یک getter و یک setter این کار را انجام دهیم.بیایید تبدیل یک رشته به یک بولین را به عنوان مثال در نظر بگیریم، زیرا این یکی از پرکاربردترین هاست.const toBoolean = (value: boolean | string): boolean =&gt; {
 return value != null &amp;&amp; `${value}` !== &#039;false&#039;;
};

@Component({ selector: &#039;app-test&#039;, template: `` })
export class TestComponent {
  _show = false;

 @Input() set show(v: boolean | string) {
 this._show = toBoolean(v);
  }

 get show() {
 return this._show;
  }
}ما به این متد نیاز داریم، چون که می‌خواهیم از مقادیر بولین و رشته‌ای پشتیبانی کنیم، بنابراین می‌توان از آن به صورت های زیر استفاده کرد:&lt;app-test show /&gt;                 &lt;!-- just setting the attribute --&gt;
&lt;app-test show=&amp;quottrue&amp;quot /&gt;     &lt;!-- static attribute binding --&gt;
&lt;app-test [show]=&amp;quottrue&amp;quot /&gt;   &lt;!-- dynamic attribute binding --&gt;این یک الگوی رایج است که ما در کامپوننت های خود استفاده می کنیم. هم اکنون با آمدن انگولار ۱۶ این کار میتوانیم از ویژگی Input transform@ برای تبدیل مقدار ورودی استفاده کنیم:@Component({ selector: &#039;app-test&#039;, template: `` })
export class TestComponent {

 @Input({ transform: (v: boolean | string) =&gt; v != null &amp;&amp; `${v}` !== &#039;false&#039; }) 

 show: boolean = false;
}همچنین می‌توانیم تابع تبدیل را به یک تابع جداگانه منتقل کنیم تا بتوانیم آن را در سایر کامپوننت ها مجدداً استفاده کنیم:const toBoolean = (value: boolean | string): boolean =&gt; {
 return value != null &amp;&amp; `${value}` !== &#039;false&#039;;
};

@Component({ selector: &#039;app-test&#039;, template: `` })
export class TestComponent {
 @Input({ transform: toBoolean }) show: boolean = false;
}از آنجایی که تبدیل رشته ها به بولین یک چیز رایج است، Angular توابع داخلی را برای آن ها فراهم می کند. بنابراین ما قادر به انجام این کار به این صورت خواهیم بود:import { booleanAttribute, numberAttribute } from &#039;@angular/core&#039;;

@Component({ selector: &#039;app-test&#039;, template: `` })
export class TestComponent {
 @Input({ transform: booleanAttribute }) show: boolean = false;
 @Input({ transform: numberAttribute }) count: number = 0;
}و به این شکل میتوانیم از آن استفاده کنیم :&lt;app-test show count=&amp;quot1&amp;quot /&gt;                   &lt;!-- just setting the attribute --&gt;
&lt;app-test show=&amp;quottrue&amp;quot count=&amp;quot1&amp;quot /&gt;       &lt;!-- static attribute binding --&gt;
&lt;app-test [show]=&amp;quottrue&amp;quot [count]=&amp;quot1&amp;quot /&gt;   &lt;!-- dynamic attribute binding --&gt;همانطور که می بینیم، با این ویژگی جدید کارها بسیار آسان تر خواهد شد.این مقاله برگردان شده یک مقاله معتبر درباره این موضوع است.برای دسترسی به مقاله منبع اینجا کلیک کنید.برای مشاهده پست های بیشتر و ارتباط با من از طریق لینکدین اینجا کلیک کنید.امیدوارم براتون مفید واقع شده باشه.</description>
                <category>مصطفی میری</category>
                <author>مصطفی میری</author>
                <pubDate>Fri, 29 Sep 2023 02:26:01 +0330</pubDate>
            </item>
                    <item>
                <title>راهنمای گام به گام برای مدیریت State با NgRx در Angular 16</title>
                <link>https://virgool.io/@mostafamiri/ngrx-in-angular16-xrvr6el5wzaz</link>
                <description>کتابخانه NGRX یک کتابخانه محبوب برای مدیریت state ها در برنامه های Angular است. این کتابخانه کمک می کند تا وضعیت یک برنامه را به روشی قابل پیش بینی و مقیاس پذیر مدیریت کنید. در این راهنما، فرآیند پیاده سازی NGRX را با ساختن یک اپلیکیشن کوچک Todo به صورت گام به گام طی خواهیم کرد.در اینجا می توانید به کد کامل مراجعه کنید.مرحله 1: NGRX را نصب کنید.برای نصب NGRX، دستور زیر را در ترمینال خود اجرا کنید:npm install @ngrx/store @ngrx/effects --saveپکیج ngrx/store@ توابع اصلی مدیریت حالت را برای NGRX فراهم می کند. پکیجngrx/effectsراهی برای رسیدگی به اثرات جانبی در برنامه شما فراهم می کند.ساختار پوشه برنامه ما به این شکل خواهد بود.src/
   | store/
     | actions.ts
     | reducers.ts
     | selectors.ts
     | store.ts
     | todo.model.ts 
   | main.ts
   | todo.component.ts
   | todo.component.htmlمرحله 2: مدل Todo را تعریف کنید.اولین قدم این است که مدل داده های خود را در store/todo.model.tsرا تعریف کنید.export interface Todo {
  id: number | string;
  description: string;
  completed: boolean;
}در این مثال، interface Todo را با برخی ویژگی ها تعریف می کنیم.مرحله 3: Service و Actions را تعریف کنید.سرویس ما شامل سرویس api todo  با تابعgetAllخواهد بود. این به عنوان یک سرویس ، جواب بکند را شبیه سازی میکند . ما از Observable استفاده خواهیم کرد و از تأخیر یا delay برای نمایش لودر استفاده خواهیم کرد.سرویس todo را در store/service.tsتعریف کنید.@Injectable()
export class ToDoService {
 // fake backend
  getAll(): Observable&lt;Todo[]&gt; {
    return of(
      [{
        id: 1,
        description: &#039;description 1&#039;,
        completed: false
      },
      {
        id: 2,
        description: &#039;description 2&#039;,
        completed: false
      }]
    ).pipe(delay(2000))
  }
}اکشن ها پیام‌هایی هستند که تغییر حالت در برنامه شما را توصیف می‌کنند. آنها اشیاء جاوا اسکریپت هستند که دارای یک ویژگی نوع (type) و یک payload اختیاری هستند. action ها را در store/actions.tsتعریف کنید.export const loadTodos = createAction(&#039;[Todo] Load Todos&#039;);
export const loadTodosSuccess = createAction(&#039;[Todo] Load Todos Success&#039;, props&lt;{ todos: Todo[] }&gt;());
export const loadTodosFailure = createAction(&#039;[Todo] Load Todos Failure&#039;, props&lt;{ error: string }&gt;());
export const addTodo = createAction(&#039;[Todo] Add Todo&#039;, props&lt;{ todo: Todo }&gt;());
export const updateTodo = createAction(&#039;[Todo] Update Todo&#039;, props&lt;{ todo: Todo }&gt;());
export const deleteTodo = createAction(&#039;[Todo] Delete Todo&#039;, props&lt;{ id: string }&gt;());در این مثال چندین اکشن برای مدیریت حالتTodoتعریف می کنیم.  اکشن loadTodosبرای بارگیری لیست کارهای انجام شده از سرور استفاده می شود. اکشنloadTodosSuccessزمانی که کارها با موفقیت بارگیری شوند، ارسال می شود .اکشن loadTodosFailureزمانی که در بارگیری کارها خطایی وجود داشته باشد، ارسال می شود . از اکشن های addTodo, updateTodoو deleteTodoبه ترتیب برای به روز رسانی ، افزودن و حذف todoها استفاده می شود.مرحله 4: Reducer ها را تعریف کنید.کاهنده ها یا reducerها توابعی هستند که حالت فعلی و یک اکشن را می گیرند و حالت جدیدی را برمی گردانند. آنها مسئول رسیدگی به تغییرات حالت در برنامه شما هستند. آن ها را درstore/reducers.tsتعریف کنید.export interface TodoState {
todos: Todo[];
loading: boolean;
error: string;
}
export const initialState: TodoState = {
todos: [],
loading: false,
error: &#039;&#039;
};
export const todoReducer = createReducer(
initialState,

on(TodoActions.loadTodos, state =&gt; ({ ...state, loading: true })),

on(TodoActions.loadTodosSuccess, (state, { todos }) =&gt;({ ...state, todos, loading: false })),

on(TodoActions.loadTodosFailure, (state, { error }) =&gt; ({ ...state, error, loading: false })),

on(TodoActions.addTodo, (state, { todo }) =&gt; ({ ...state, todos: [...state.todos, todo] })),

on(TodoActions.updateTodo, (state, { todo }) =&gt; ({ ...state, todos: state.todos.map(t =&gt; t.id === todo.id ? todo : t) })),

on(TodoActions.deleteTodo, (state, { id }) =&gt; ({ ...state, todos: state.todos.filter(t =&gt; t.id !== id) })),
);در این مثال یک reducer برای مدیریت حالتTodoتعریف می کنیم. تابع  initialState،todoReducerو مجموعه ای از توابع reducerها را می گیرد که با استفاده از تابعonاز @ngrx/storeتعریف شده اند. هر تابع reducer یک عمل خاص را انجام می دهد و یک حالت جدید را برمی گرداند.مرحله 5: Effect ها را تعریف کنید.افکت‌ها سرویس‌هایی هستند که به اکشن ها گوش می‌دهند و اثرات جانبی مانند درخواست‌های HTTP یا تعامل با APIهای مرورگر را انجام می‌دهند.@Injectable()
export class TodoEffects {

  loadTodos$ = createEffect(() =&gt;
    this.actions$.pipe(
      ofType(TodoActions.loadTodos),
      mergeMap(() =&gt;
        this.todoService.getAll().pipe(
          map((todos) =&gt; TodoActions.loadTodosSuccess({ todos })),
          catchError((error) =&gt;
            of(TodoActions.loadTodosFailure({ error: error.message }))
          )
        )
      )
    )
  );
  constructor(private actions$: Actions, private todoService: ToDoService) {}
}در این مثال، یک افکت برای مدیریت اکشنloadTodosتعریف می کنیم. افکت loadTodos$به اکشنloadTodosبا استفاده از عملگرofTypeازngrx/effects@ گوش می دهد . هنگامی که اکشن ارسال می شود، افکت متدgetAllرا از TodoServiceفراخوانی می کند و بسته به نتیجه اکشنloadTodosSuccessیا loadTodosFailureرا ارسال می کند.مرحله 6: تعریف interface برای Storeاینترفیس در فایلstore/store.ts تعریف خواهد شد .export interface AppState {
  todo: TodoState
}

export interface AppStore {
  todo: ActionReducer&lt;TodoState, Action&gt;;
}

export const appStore: AppStore = {
  todo: todoReducer
}

export const appEffects = [TodoEffects];اینترفیسAppStateتمام ویژگی های feature برنامه را تعریف می کند. در اینجا ما یک feature واحد داریم بنامtodo که از نوعTodoState است . ما می توانیم چندین feature در برنامه خود مانند این داشته باشیم.اینترفیسAppStoreتمام انواع reducer های مورد استفاده در برنامه ما را تعریف می کند. در این مورد، ما یک reducer برای todo داریم، بنابراین ما todoReducerرا به ویژگیtodoنسبت میدهیم. appStoreبرای پیکربندی ماژول store ما استفاده خواهد شد.اینappEffectsآرایه ای از کلاس های افکت های تعریف شده را خواهد داشت. این برای ثبت effectها در برنامه استفاده می شود.مرحله 7: Store and Effects را در کامپوننت standalone اصلی ثبت کنید.برای استفاده از store NGRX در برنامه خود، باید آن را درStoreModuleدر AppModuleخود ثبت کنید . ما در اینجا از کامپوننت های مستقل استفاده خواهیم کرد.@Component({
  selector: &#039;my-app&#039;,
  standalone: true,
  imports: [
    CommonModule
  ],
  template: ``,
})
export class App {
}

bootstrapApplication(App, {
  // register the store providers here
  providers: [
    provideStore(appStore),
    provideEffects(appEffects),
    ToDoService
  ]
});در این مثال، store را با استفاده ازappStoreثبت کردیم.ما همچنین افکت ها را با appEffectsثبت کردیم.مرحله 8: از store در کامپوننت ToDo List استفاده کنید.برای استفاده از store در کامپوننت‌های خود، باید سرویسStoreرا تزریق کنید و اکشن ها را ارسال کنید. ما یک کامپوننت مستقلTodoListComponentداخل src/todo-list.component.tsبا فایل html در src/todo-list.component.html ایجاد خواهیم کرد.@Component({
standalone: true,
selector: &#039;app-todo-list&#039;,
imports: [NgFor, NgIf, AsyncPipe, JsonPipe],
templateUrl: &#039;./todo-list.component.html&#039;
})
export class TodoListComponent {
todos$: Observable&lt;Todo[]&gt;;
isLoading$: Observable&lt;boolean&gt;;

constructor(private store: Store&lt;AppState&gt;) {
  this.todos$ = this.store.select(todoSelector);
  this.isLoading$ = this.store.select(state =&gt; state.todo.loading);
  this.loadTodos();
}

loadTodos() {
this.store.dispatch(TodoActions.loadTodos());
}

addTodo(index: number) {

const todo: Todo = {id: index, description: &#039;New Todo&#039;, completed: false };
this.store.dispatch(TodoActions.addTodo({ todo }));
}

complete(todo: Todo) {
  this.store.dispatch(TodoActions.updateTodo({todo : {...todo, completed: true}}));
}
}در این مثال، کامپوننتی را تعریف می کنیم که از سرویسStoreبرای بارگذاری و نمایش کارهای انجام شده استفاده می کند. سرویس Storeرا تزریق می کنیم و ویژگیtodosرا از state با استفاده از متد select انتخاب می کنیم. ما همچنین سه متد برای ارسال اکشن های loadTodos, addTodoو updateTodo تعریف می کنیم. پایپ asyncبرای سابسکرایب کردن به $todosو نمایش لیست کارهای انجام شده استفاده می شود.مرحله 9: کامپوننت ToDo List را در کامپوننت اصلی ثبت کنید.@Component({
  selector: &#039;my-app&#039;,
  standalone: true,
  imports: [
    CommonModule,
    TodoListComponent
  ],
  template: `
    &lt;app-todo-list/&gt;
  `,
})
export class App {
}اینTodoListComponentرا در آرایه imports اضافه کنید .تگ &lt;app-todo-list/&gt;را برای نمایش TodoListComponent اضافه کنید .نتیجهدر این راهنما، ما روند گام به گام پیاده سازی NGRX در یک برنامه Angular را طی کرده ایم. ما نحوه تعریف state، actions، reducers ، effects و نحوه استفاده از store را در کامپوننت ها دیدیم. با انجام این مراحل می توانید به راحتی با استفاده از NGRX حالت های برنامه خود را به صورت مقیاس پذیر و قابل پیش بینی مدیریت کنید.این مقاله برگردان شده یک مقاله معتبر درباره این موضوع است.برای دسترسی به مقاله منبع اینجا کلیک کنید.برای مشاهده پست های بیشتر و ارتباط با من از طریق لینکدین اینجا کلیک کنید.امیدوارم براتون مفید واقع شده باشه.</description>
                <category>مصطفی میری</category>
                <author>مصطفی میری</author>
                <pubDate>Fri, 28 Jul 2023 01:18:21 +0330</pubDate>
            </item>
                    <item>
                <title>دیزاین پترن Facade در Angular</title>
                <link>https://virgool.io/@mostafamiri/%D8%AF%DB%8C%D8%B2%D8%A7%DB%8C%D9%86-%D9%BE%D8%AA%D8%B1%D9%86-facade-%D8%AF%D8%B1-angular-ch1kvyqvnf8c</link>
                <description>معرفیبه‌عنوان یک توسعه‌دهنده Angular، ممکن است نیاز به ساده‌سازی کدهای پیچیده و ارائه یک رابط ساده‌شده برای سایر بخش‌های برنامه نیاز پیدا کرده باشید. اینجاست که دیزاین پترن facade به کار می‌رود. در این مقاله، ما در مورد دیزاین پترن facade ، پیاده سازی آن در Angular و اینکه چگونه می تواند عملکرد برنامه شما را بهبود بخشد، بحث خواهیم کرد.دیزاین پترن facade چیست؟دیزاین پترن facade یک دیزاین پترن ساختاری است که یک رابط ساده برای یک سیستم پیچیده از کلاس‌ها، توابع و APIها فراهم می‌کند. این به شما امکان می دهد گروهی از زیرسیستم های پیچیده را کپسوله کنید و یک رابط ساده شده را در فراهم کنید.به عبارت ساده، یک facade مانند یک بسته بندی در اطراف یک سیستم پیچیده است که یک رابط کاربری ساده برای تعامل مشتریان فراهم می کند. Facade پیچیدگی سیستم را پنهان می کند و یک API ساده را در اختیار مشتریان قرار می دهد تا از آن استفاده کنند.پیاده سازی در Angularدر Angular می توانیم با استفاده از Services دیزاین پترن facade را پیاده سازی کنیم. سرویس‌ها ستون فقرات برنامه‌های Angular هستند و برای به اشتراک گذاشتن داده‌ها، منطق و توابع در چندین کامپوننت استفاده می‌شوند. سرویس ها همچنین می تواند برای محصور کردن زیرسیستم های پیچیده و ارائه یک رابط ساده شده برای کد مشتری استفاده شود.بیایید نگاهی به مثالی بیندازیم که چگونه می‌توانیم الگوی طراحی facade را با استفاده از سرویس‌ها در Angular پیاده‌سازی کنیم.مثال 1فرض کنید ما یک سیستم پیچیده از کلاس ها و توابع داریم که مسئول رسیدگی به احراز هویت در برنامه Angular ما هستند. سیستم احراز هویت از کلاس ها و توابع زیر تشکیل شده است:سرویس AuthService : مسئول رسیدگی به احراز هویت کاربر استسرویس UserService : مسئول مدیریت داده های کاربر استسرویس TokenService : مسئول مدیریت توکن های کاربر استسرویس JwtHelperService : مسئول رمزگشایی توکن های JWT استبرنامه ما چندین کامپوننت دارد که نیاز به دسترسی به این کلاس ها و توابع دارند. برای ساده کردن کد و ارائه یک رابط ساده به کد مشتری، می‌توانیم یک سرویس Facade به نام AuthFacadeService ایجاد کنیم.سرویس AuthFacadeService زیرسیستم احراز هویت پیچیده را کپسوله می کند و یک رابط ساده ارائه می دهد . AuthFacadeService متدهای زیر را خواهد داشت:متد ()login: ورود کاربر را کنترل می کند.متد ()logout: خروج کاربر را کنترل می کند.متد ()getUser: داده های کاربر را بازیابی می کند.متد ()isAuthenticated: بررسی می کند که آیا کاربر احراز هویت شده است یا خیر.در اینجا نحوه پیاده سازی AuthFacadeService آمده است:import { Injectable } from &#039;@angular/core&#039;;
import { AuthService } from &#039;./auth.service&#039;;
import { UserService } from &#039;./user.service&#039;;
import { TokenService } from &#039;./token.service&#039;;
import { JwtHelperService } from &#039;./jwt-helper.service&#039;;

@Injectable({
 providedIn: &#039;root&#039;
})
export class AuthFacadeService {

constructor(
  private authService: AuthService,
  private userService: UserService,
  private tokenService: TokenService,
  private jwtHelperService: JwtHelperService
 ) { }

 login(username: string, password: string) {
  // Call AuthService to handle user authentication
  const token = this.authService.login(username, password);

  // Save token to TokenService
  this.tokenService.saveToken(token);
 }

 logout() {
  // Call AuthService to handle user logout
  this.authService.logout();

 // Remove token from TokenService
 this.tokenService.removeToken();
  }

 getUser() {
  // Call UserService to retrieve user data
  return this.userService.getUser();
 }

 isAuthenticated(): boolean {
  // Check if the user is authenticated using the JWT token
  const token = this.tokenService.getToken();
  return token &amp;&amp; !this.jwtHelperService.isTokenExpired(token);
 }
}در کد بالا، ما یک سرویس Facade به نام AuthFacadeService ایجاد کرده ایم که زیرسیستم احراز هویت پیچیده را کپسوله می کند. AuthFacadeService با متدهایی مانند login، logout و getUser یک رابط ساده برای کد کلاینت فراهم می کند. این متد ها به صورت داخلی از AuthService برای انجام عملیات لازم استفاده می کنند.مثال 2بیایید یک مثال از یک برنامه تجارت الکترونیکی بزنیم که چندین سرویس برای رسیدگی به بخش‌های مختلف برنامه مانند احراز هویت، سبد خرید و پرداخت دارد. این خدمات به شدت به یکدیگر وابسته هستند و حفظ و بهبود عملکرد را دشوار می کند. برای حل این مشکل، می‌توانیم یک سرویس facade  ایجاد کنیم که یک رابط ساده‌شده را در اختیار این سرویس‌ها قرار دهد.ابتدا بیایید سرویس هایی را ایجاد کنیم که می خواهیم ساده کنیم:@Injectable()
export class AuthService {
 // Authentication logic
}

@Injectable()
export class CartService {
 // Shopping cart logic
}

@Injectable()
export class PaymentService {
 // Payment logic
}اکنون، بیایید سرویس facade را ایجاد کنیم که یک رابط ساده‌ شده را در اختیار این سرویس‌ها قرار می‌دهد:@Injectable()
export class FacadeService {
 
constructor(
 private authService: AuthService,
 private cartService: CartService,
 private paymentService: PaymentService
 ) {}

 // Simplified interface to authentication
 login(username: string, password: string) {
 return this.authService.login(username, password);
  }

 // Simplified interface to shopping cart
 addToCart(item: Item) {
 return this.cartService.addToCart(item);
  }

 removeFromCart(item: Item) {
 return this.cartService.removeFromCart(item);
  }

 // Simplified interface to payment
 pay() {
 return this.paymentService.pay();
  }
}همانطور که می بینید، FacadeService یک رابط ساده برای سیستم پیچیده خدمات ارائه می دهد. به جای نیاز به تعامل با هر سرویس به طور جداگانه، مشتری می تواند با FacadeService تعامل داشته باشد و تمام اقدامات لازم را انجام دهد.حال، بیایید نگاهی به نحوه استفاده از FacadeService در کامپوننت های خود بیندازیم:@Component({
 selector: &#039;app-login&#039;,
 template: `
    &lt;form (ngSubmit)=&amp;quotonSubmit()&amp;quot&gt;
      &lt;input type=&amp;quottext&amp;quot [(ngModel)]=&amp;quotusername&amp;quot&gt;
      &lt;input type=&amp;quotpassword&amp;quot [(ngModel)]=&amp;quotpassword&amp;quot&gt;
      &lt;button type=&amp;quotsubmit&amp;quot&gt;Login&lt;/button&gt;
    &lt;/form&gt;
  `
})
export class LoginComponent {
 username: string;
 password: string;

 constructor(private facadeService: FacadeService) {}

 onSubmit() {
 this.facadeService.login(this.username, this.password)
      .subscribe(() =&gt; {
 // Redirect to dashboard
      }, (error) =&gt; {
 // Display error message
      });
  }
}

@Component({
 selector: &#039;app-cart&#039;,
 template: `
    &lt;div *ngFor=&amp;quotlet item of cartItems&amp;quot&gt;
      {{ item.name }}
      &lt;button (cllick)=&amp;quotaddToCart(item)&amp;quot&gt;Add to cart&lt;/button&gt;
    &lt;div&gt; 
`
export class CartComponent {
 username: string;
 password: string;

 constructor(private facadeService: FacadeService) {}

 addToCart(item: Item) {
 this.facadeService.addToCart(item)
      .subscribe(() =&gt; {
 // Add to cart
      }, (error) =&gt; {
 // Display error message
      });
  }
}در کد بالا، به وضوح می‌بینیم که سرویس facade از تمام عملکردهای مربوط به ورود، افزودن به سبد خرید یا حذف از سبد خرید و غیره مراقبت می‌کند. با ایجاد یک رابط ساده که پیچیدگی سیستم زیرین را پنهان می‌کند، می‌توانیم خوانایی و آزمایش پذیری کد خود را بهبود بخشیم.امیدوارم این مقاله درک خوبی از الگوی facade و نحوه استفاده از آن در برنامه های Angular به شما ارائه کرده باشد.به طور کلی، دیزاین پترن facade ابزار قدرتمندی برای بهبود عملکرد برنامه های Angular شما با ساده کردن عملیات پیچیده است. با انتزاع کردن پیچیدگی سیستم های پایه ای، دیزاین پترن facade می تواند نوشتن کدهای تمیز و قابل خواندن را برای توسعه دهندگان آسان تر کند که نگهداری و به روز رسانی آن در طول زمان آسان تر است.این مقاله برگردان شده یک مقاله معتبر درباره این موضوع است.برای دسترسی به مقاله منبع اینجا کلیک کنید.برای مشاهده پست های بیشتر و ارتباط با من از طریق لینکدین اینجا کلیک کنید.امیدوارم براتون مفید واقع شده باشه.</description>
                <category>مصطفی میری</category>
                <author>مصطفی میری</author>
                <pubDate>Mon, 10 Jul 2023 15:09:44 +0330</pubDate>
            </item>
                    <item>
                <title>استفاده از WebWorker در Angular</title>
                <link>https://virgool.io/@mostafamiri/angular-web-worker-jysohoioffp8</link>
                <description>استفاده از Angular به همراه Web Workers می تواند به طور قابل توجهی عملکرد و پاسخگویی برنامه های وب  را افزایش دهد . انگولار یک چارچوب قدرتمند برای ساخت برنامه های پیچیده است، در حالی که Web Workers به ​​شما اجازه می دهد تا وظایف محاسباتی فشرده را برای جدا کردن thread ها بارگذاری کنید و از مسدود کردن thread UI اصلی جلوگیری کنید. در این مقاله، نحوه استفاده از Angular با Web Workers را همراه با یک مثال دقیق بررسی خواهیم کرد.در این مقاله، فرآیند راه‌اندازی Angular را با Web Workers پوشش دادیم، از جمله ایجاد یک فایل Web Worker، پیاده‌سازی یک سرویس Angular برای ارتباط، و استفاده از سرویس در یک کامپوننت. با استفاده از Web Workers، می‌توانید برنامه‌های Angular خود را بهینه کنید و با واگذاری وظایف محاسباتی سنگین به threadهای جداگانه، عملکرد را افزایش دهید.وب ورکر ( Web Worker ) چیست؟وب ورکر یکی از ویژگی‌های مرورگرهای مدرن است که اجرای کد جاوا اسکریپت را در پس‌زمینه، جدا از  thread اصلی مرورگر، امکان‌پذیر می‌کند. با استفاده از Web Workers، می توانید کارهای وقت گیر مانند محاسبات پیچیده، پردازش داده ها یا محاسبات سنگین را بدون مسدود کردن thread UI اصلی انجام دهید. این رویکرد تضمین می کند که برنامه پاسخگو باقی می ماند و تجربه کاربری روانی را ارائه می دهد.راه اندازی پروژه Angularبرای شروع، اجازه دهید یک پروژه Angular راه اندازی کنیم که از Web Workers استفاده می کند. ترمینال یا خط فرمان خود را باز کنید و مراحل زیر را دنبال کنید:ابزار Angular CLI را به صورت سراسری با اجرای دستور زیر نصب کنید:npm install -g @angular/cli۲. یک پروژه Angular جدید ایجاد کنید:ng new angular-web-worker-example۳. به دایرکتوری پروژه وارد شوید:cd angular-web-worker۴. سرور توسعه Angular را اجرا کنید:ng serveاین دستور سرور توسعه را راه اندازی می کند و شما می توانید با رفتن به آدرسlocalhost:4200در مرورگر خود برنامه را مشاهده کنید.ایجاد Web Workerبرای استفاده از Web Worker در یک پروژه Angular، باید یک فایل جداگانه ایجاد کنیم که حاوی کدهایی باشد که باید در Web Worker اجرا شوند. برای ایجاد Web Worker مراحل زیر را دنبال کنید:در پوشهsrcپروژه Angular خود، یک فایل جدید به نامfactorial.worker.tsایجاد کنید.در داخل فایلfactorial.worker.ts، کدی را بنویسید که در Web Worker اجرا می شود. برای مثال، بیایید worker ایجاد کنیم که فاکتوریل یک عدد را محاسبه کند:// factorial.worker.ts
self.addEventListener(&#039;message&#039;, (event) =&gt; {
   const number = event.data;
   const result = calculateFactorial(number);
   self.postMessage(result);
});

function calculateFactorial(number: number): number {
   let factorial = 1;
   for (let i = 2; i &lt;= number; i++) {
      factorial *= i;
   }
   return factorial;
}ایجاد یک سرویس برای Web Workerبرای برقراری ارتباط با Web Worker، یک سرویس ایجاد می کنیم. این سرویس تعامل با Web Worker را مدیریت می‌کند و رابطی را برای استفاده سایر کامپوننت ها فراهم می‌کند. این مراحل را دنبال کنید:در پوشهsrc/app، یک سرویس جدید با استفاده از Angular CLI ایجاد کنید:ng generate service factorialاین دستور یک فایل جدید به نامfactorial.service.tsایجاد می کند و فایلapp.module.tsرا با import و provider لازم به روز می کند.۲. فایل factorial.service.tsرا در پوشهsrc/appباز کنید و محتوای آن را با کد زیر جایگزین کنید:// factorial.service.ts
import { Injectable } from &#039;@angular/core&#039;;

@Injectable({
   providedIn: &#039;root&#039;
})
export class FactorialService {
   private worker: Worker;

 constructor() {
   this.worker = new Worker(&#039;./factorial.worker&#039;, { type: &#039;module&#039; });
   this.worker.addEventListener(&#039;message&#039;, this.onWorkerMessage.bind(this));
 }

 calculateFactorial(number: number): void {
   this.worker.postMessage(number);
 }

 private onWorkerMessage(event: MessageEvent): void {
   const result = event.data;
   console.log(`Factorial: ${result}`);
 }
}کلاس FactorialServiceبا استفاده از سازندهWorker یک Web Worker ایجاد می کند . messageرویداد را از worker گوش می دهد و نتیجه را ثبت می کند. این متدcalculateFactorialپیامی را به ورکر ارسال می کند و محاسبات فاکتوریل را آغاز می کند.استفاده از Web Worker Service در یک کامپوننتاکنون که سرویس Web Worker خود را راه اندازی کرده ایم، بیایید یک کامپوننت ایجاد کنیم که از سرویس برای محاسبه فاکتوریل یک عدد استفاده می کند. این مراحل را دنبال کنید:فایلapp.component.ts را در پوشه src/appباز کنید .محتوای آن را با کد زیر جایگزین کنید:// app.component.ts
import { Component } from &#039;@angular/core&#039;;
import { FactorialService } from &#039;./factorial.service&#039;;

@Component({
  selector: &#039;app-root&#039;,
  template: `
    &lt;div&gt;
      &lt;h1&gt;Calculate Factorial&lt;/h1&gt;
      &lt;input type=&amp;quotnumber&amp;quot [(ngModel)]=&amp;quotnumber&amp;quot placeholder=&amp;quotEnter a number&amp;quot&gt;
      &lt;button (click)=&amp;quotcalculate()&amp;quot&gt;Calculate&lt;/button&gt;
    &lt;/div&gt;
  `
})
export class AppComponent {
  number: number;

 constructor(private factorialService: FactorialService) {}

 calculate(): void {
     this.factorialService.calculateFactorial(this.number);
 }
}این کامپوننت شامل یک فیلد ورودی و یک دکمه است. هنگامی که دکمه کلیک می شود، متدcalculate فراخوانی می شود که متدcalculateFactorialرا درFactorialServiceفعال می کند.به روز رسانی App Moduleدر نهایت، باید فایل app.module.tsرا به‌روزرسانی کنیم تا به عنوان ارائه‌دهنده actorialServiceباشد . فایل app.module.tsرا در پوشه src/appباز کرده و به صورت زیر آپدیت کنید:// app.module.ts
import { NgModule } from &#039;@angular/core&#039;;
import { BrowserModule } from &#039;@angular/platform-browser&#039;;
import { FormsModule } from &#039;@angular/forms&#039;;

import { AppComponent } from &#039;./app.component&#039;;
import { FactorialService } from &#039;./factorial.service&#039;;

@NgModule({
   declarations: [AppComponent],
   imports: [BrowserModule, FormsModule],
   providers: [FactorialService],
   bootstrap: [AppComponent]
})
export class AppModule {}اجرای برنامهاکنون که پروژه Angular خود را با اضافه کردن Web Worker راه‌اندازی کرده‌ایم، زمان اجرای برنامه و مشاهده آن در عمل فرا رسیده است. این مراحل را دنبال کنید:در ترمینال خود، مطمئن شوید که در دایرکتوری ریشه پروژه Angular خود هستید.سرور توسعه Angular را با اجرای دستور زیر راه اندازی کنید:ng serveاین دستور برنامه را می سازد و آن را روی یک سرور توسعه محلی راه اندازی می کند.۳. مرورگر خود را باز کرده و به http://localhost:4200بروید. شما باید یک فیلد ورودی را ببینید که در آن می توانید یک عدد و یک دکمه &quot;محاسبه&quot; وارد کنید.۴. یک عدد را در فیلد ورودی وارد کنید و روی دکمه &quot;Calculate&quot; کلیک کنید. فاکتوریل عدد وارد شده با استفاده از Web Worker محاسبه می شود و نتیجه در کنسول مرورگر ثبت می شود.تبریک می گویم! شما موفقیت Web Workers را با Angular ادغام کردید. محاسبه فاکتوریل در یک thread جداگانه بارگذاری می‌شود و از مسدود کردن رشته thread UI اصلی جلوگیری می‌کند و تجربه کاربری روان را تضمین می‌کند.این مقاله برگردان شده یک مقاله معتبر درباره این موضوع است.برای دسترسی به مقاله منبع اینجا کلیک کنید.برای مشاهده پست های بیشتر و ارتباط با من از طریق لینکدین اینجا کلیک کنید.امیدوارم براتون مفید واقع شده باشه.</description>
                <category>مصطفی میری</category>
                <author>مصطفی میری</author>
                <pubDate>Thu, 06 Jul 2023 00:36:11 +0330</pubDate>
            </item>
                    <item>
                <title>ویژگی جدید Required Inputs در Angular v16</title>
                <link>https://virgool.io/@mostafamiri/required-inputs-angular-16-otwm8o0pmf2y</link>
                <description>اکنون Angular 16 در دسترس است و مجموعه ای از ویژگی ها و پیشرفت های عالی را برای توسعه دهندگان به همراه دارد. در این مقاله، نگاهی دقیق تر به syntax جدیدInput@خواهیم داشت .کلمه کلیدی Input یک دکوریتور انگولار است که به ما امکان می دهد یک property از کامپوننت را به یک ویژگی DOM در template متصل کنیم. هنگامی که Angular روند change detection را اجرا می کند، تغییرات DOM property را بررسی می کند و در صورت لزوم property کامپوننت را به روز می کند و مطمئن می شود که همیشه به روز است.بیایید مثالی از استفاده از یک کامپوننت را در نظر بگیریم:&lt;app-child [name]=&amp;quot&#039;John&#039;&amp;quot&gt;&lt;/app-child&gt;ویژگیnameازDOM به یک پراپرتی از کامپوننت متصل می شود و در داخل کامپوننت می توانیم از مقدار Johnاستفاده کنیم.در اینجا یک مثال از دیدگاه کامپوننت آورده شده است:@Component({
  selector: &#039;app-child&#039;,
  template: `Name: {{name}}`
})
class ChildComponent {
  @Input() name: string;
}با این حال، هیچ راه ساده ای برای اطمینان از اینکه مقدارname از کامپوننت پدر مشخص شده است وجود نداشت، که این اصلا ایده آل نبود.&lt;app-child&gt;&lt;/app-child&gt;کد بدون هیچ خطایی کار می کند، اما الگوی رندر شده فاقد نام است. یک راه حل این است که اطمینان حاصل شود که انتخابگر به آن ویژگی نیاز دارد.@Component({
  selector: &#039;app-child[name]&#039;,
})
class ChildComponent {
  @Input() name: string;
}با این حال، افزودن این مورد برای هر ویژگی ورودی می‌تواند فرآیند خسته‌کننده‌ای باشد.ورودی های اجباری (Required Inputs)خوشبختانه این مثال مربوط به گذشته است! Angular 16 ورودی های اجباری را معرفی می کند.اکنون، ورودی‌ها می‌توانند یک گزینه پیکربندی اضافی داشته باشند که آنها را اجباری می‌سازد. این بدان معناست که اگر مقداری برای آن ورودی تعیین نکنیم، یک خطای زمان کامپایل واقعی دریافت خواهیم کرد. در اینجا یک نمونه خطای ذکر شده آورده شده است:Required input &#039;name&#039; from component ChildComponent must be specifiedکد جدید به این صورت است:@Component({
  selector: &#039;app-child&#039;,
  template: `Name: {{name}}`
})
class ChildComponent {
  @Input({ required: true }) name: string;
}این requiredگزینه اختیاری است، بنابراین ما همچنان می توانیم ورودی های &quot;اختیاری&quot; را مانند قبل داشته باشیم.@Component({
  selector: &#039;app-child&#039;,
  template: `Name: {{name}}`
})
class ChildComponent {
  // required input
  @Input({ required: true }) name: string;

  // basic input, not really required
  @Input() lastName: string;
}نام مستعار (Aliasing)همچنین یک مورد جالب از aliasing inputs وجود دارد، که در آن می‌خواهیم نامی متفاوت برای ویژگی DOM نسبت به نام ویژگی کامپوننت که به آن محدود شده است داشته باشیم.برای انجام این کار، می‌توانیم یک نام مستعار اختیاری برای اتصال ویژگی DOM اضافه کنیم، همانطور که در زیر نشان داده شده است:@Input() name: string;
@Input(&#039;lastname&#039;) surname: string;برای اتصال ویژگی نام خانوادگی در الگو، باید از lastnameویژگی استفاده کنیم.&lt;app-child name=&amp;quotJohn&amp;quot lastname=&amp;quotDoe&amp;quot&gt;&lt;/app-child&gt;&lt;&gt;از Angular 16، امکان اضافه کردن نام مستعار با استفاده از syntax جدید وجود دارد. این را می توان با اضافه کردن یک گزینه پیکربندی به ورودی انجام داد، همانطور که در زیر نشان داده شده است:@Component({
  selector: &#039;app-child&#039;,
  template: `Name: {{heroName}}`
})
class ChildComponent {
  // required input with alias
  @Input({ required: true, alias: &#039;name&#039;}) heroName: string;

  // basic input with alias 
  @Input({alias: &#039;lastname&#039;}) heroSurname: string;
}از ویژگی های جدید Angular 16 لذت ببرید! ?این مقاله برگردان شده یک مقاله معتبر درباره این موضوع است.برای دسترسی به مقاله منبع اینجا کلیک کنید.برای مشاهده پست های بیشتر و ارتباط با من از طریق لینکدین اینجا کلیک کنید.امیدوارم براتون مفید واقع شده باشه.</description>
                <category>مصطفی میری</category>
                <author>مصطفی میری</author>
                <pubDate>Fri, 30 Jun 2023 16:31:29 +0330</pubDate>
            </item>
                    <item>
                <title>اتصال اطلاعات Router به ورودی های کامپوننت در Angular</title>
                <link>https://virgool.io/@mostafamiri/%D8%A7%D8%AA%D8%B5%D8%A7%D9%84-%D8%A7%D8%B7%D9%84%D8%A7%D8%B9%D8%A7%D8%AA-router-%D8%A8%D9%87-%D9%88%D8%B1%D9%88%D8%AF%DB%8C-%D9%87%D8%A7%DB%8C-%DA%A9%D8%A7%D9%85%D9%BE%D9%88%D9%86%D9%86%D8%AA-%D8%AF%D8%B1-angular-gm3vgxkp29jt</link>
                <description>در Angular نسخه ۱۶ یک ویژگی قدرتمند جدید معرفی شده است که اتصال خودکار اطلاعات روتر، مانند query parameter ها ، path parameter ها ، static data و resolver data را به ورودی های یک کامپوننت مسیریابی شده امکان پذیر می کند.این عملکرد را می توان با استفاده از تابعwithComponentInputBinding فعال کرد. در اینجا مثالی از نحوه استفاده از آن آورده شده است:import { bootstrapApplication } from &#039;@angular/platform-browser&#039;;
import { provideRouter, withComponentInputBinding } from &#039;@angular/router&#039;;

bootstrapApplication(AppComponent, {
 providers: [
 provideRouter(
      [
        {
          path: &#039;&#039;,
          pathMatch: &#039;full&#039;,
          loadComponent: () =&gt; import(&#039;./app/home/home.component&#039;),
        },
        {
          path: &#039;todo&#039;,
          loadComponent: () =&gt; import(&#039;./app/todo/todo.component&#039;),
        },
      ],
         // ???
         withComponentInputBinding()
    ),
  ],
});با استفاده از این ویژگی، روترoutletکامپوننت به کوئری پارامترها ،  path parameter ها و data observable های مسیر فعال گوش می دهد و به طور خودکار متدsetInputرا با مقدار اولیه و مقداری که تغییر می کند فراخوانی می کند.به عنوان مثال، فرض کنید ما یک کوئری پارامترidدر URL خود داریم و می خواهیم آن را واکشی کنیم. در این صورت URL ما todo?id=1/خواهد بود و کامپوننت ما به شکل زیر خواهد بود:@Component({
  ...
  standalone: true,
})
export default class TodoComponent {
 private todosService = inject(TodosService);

  todo$: Observable&lt;Todo&gt;;

 // The input name should be the same as the query param key
 @Input() set id(value: string) {
 this.todo$ = this.todosService.get(value);
  }
}به طور مشابه، اگر یک عبارت جستجو در URL خود داشته باشیم به عنوان مثال foo?searchTerm=bar/، می توانیم کنترل فرم خود را با مقدار آن به روز کنیم:@Component({
  ...
 standalone: true,
 imports: [ReactiveFormsModule],
})
export default class FooComponent {
   searchControl: FormControl&lt;string&gt;;

   @Input() searchTerm: string;

 ngOnInit() {
   this.searchControl = new FormControl(this.searchTerm || &#039;&#039;);
  }
}در موارد تداخل، اولویت بر اساس موارد زیر تنظیم می شود: داده ها &gt; path parameters ها &gt; query parameters ها.در اینجا یک مثال است که تمام موارد استفاده معتبر را نشان می دهد:bootstrapApplication(AppComponent, {
  providers: [
 provideRouter(
      [
        {
          path: &#039;todos/:todoId&#039;,
          data: {
            isSomething: true,
          },
          resolve: { resolveFoo: () =&gt; &#039;My resolved data&#039; },
          loadComponent: () =&gt; import(&#039;./app/todo/todo.component&#039;),
        },
      ],
      withComponentInputBinding()
    ),
  ],
});فرض کنید URL ما todos/1?searchTerm=angular/است و کامپوننت ما به شکل زیر است:@Component({
  ...
 standalone: true,
})
export default class TodoComponent {
  @Input() resolveFoo: string; // My resolved data
  @Input() isSomething: boolean; // true
  @Input() todoId: string; // 1
  @Input() searchTerm: string; // angular
}به طور کلی، این ویژگی جدید فرآیند ارسال اطلاعات روتر به کامپوننت مسیریابی شده را ساده می کند و نیاز به کدهای تکراری را کاهش می دهد.این مقاله برگردان شده یک مقاله معتبر درباره این موضوع است.برای دسترسی به مقاله منبع اینجا کلیک کنید.برای مشاهده پست های بیشتر و ارتباط با من از طریق لینکدین اینجا کلیک کنید.امیدوارم براتون مفید واقع شده باشه.</description>
                <category>مصطفی میری</category>
                <author>مصطفی میری</author>
                <pubDate>Tue, 27 Jun 2023 12:50:55 +0330</pubDate>
            </item>
                    <item>
                <title>10 ویژگی مفید Angular که احتمالاً هرگز استفاده نکرده اید!</title>
                <link>https://virgool.io/@mostafamiri/10-%D9%88%DB%8C%DA%98%DA%AF%DB%8C-%D9%85%D9%81%DB%8C%D8%AF-angular-%DA%A9%D9%87-%D8%A7%D8%AD%D8%AA%D9%85%D8%A7%D9%84%D8%A7%D9%8B-%D9%87%D8%B1%DA%AF%D8%B2-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D9%86%DA%A9%D8%B1%D8%AF%D9%87-%D8%A7%DB%8C%D8%AF-q09trcdewnoo</link>
                <description>1.سرویس Titleتگ title یک عنصر HTML است که عنوان یک صفحه وب را مشخص می کند. تگ های title در صفحات نتایج موتورهای جستجو (SERP) در قالب عنوان قابل کلیک برای یک نتیجه نمایش داده می‌شوند. آنها برای افزایش usability ، سئو و اشتراک گذاری در شبکه های اجتماعی بسیار مهم هستند.برنامه های Angular ، عنوان را در پنجره مرورگر از تگ &lt;title&gt;...&lt;/title&gt;داخل index.html تنظیم می کنند. پیمایش بین کامپوننت ها در Angular عنوان را تغییر نمی دهد.آیا می دانید، می توانید عنوان مرورگر را از روی کامپوننت ها تنظیم کنید؟انگولار یک سرویس Title در angular/platform-browser@ دارد. ما فقط سرویس Title را در کامپوننت خود تزریق می کنیم و از متد setTitle برای تنظیم عنوان استفاده می کنیم.import { Title } from &amp;quot@angular/platform-browser&amp;quot
@Component({
    ...
})
export class LoginComponent implements OnInit {
    constructor(private title: Title) {}    
    ngOnInit() {
        title.setTitle(&amp;quotLogin&amp;quot)
    }
}وقتی به LoginComponent هدایت می‌شویم، عنوان مرورگر روی «Login» تنظیم می‌شود.می‌توانیم این موضوع را در تمام کامپوننت های پروژه‌مان تکرار کنیم تا وقتی به آن‌ها هدایت می‌شوند، پنجره مرورگر تغییر کند تا عنوان کامپوننت را نمایش دهد.2.سرویس Metaبرنامه انگولار ما چیزهایی را رندر میکند که عمدتاً از index.html می آیند. متا تگ هایی که برنامه ما خواهد داشت همان است که در index.html تنظیم شده است. Angular دارای یک سرویس Meta در angular/platform-browser@ است که به ما امکان می دهد متا تگ ها را از کامپوننت های خود تنظیم کنیم.این از نظر سئو و به اشتراک گذاری صفحه مربوط به کامپوننت در رسانه های اجتماعی بسیار مفید است.طبق ویکی پدیا:عناصر Meta تگ هایی هستند که در اسناد HTML و XHTML برای ارائه metadata ساختار یافته در مورد یک صفحه وب استفاده می شوند . آنها بخشی ازhead یک صفحه وب هستند. چندین عنصر Meta با ویژگی های مختلف را می توان در یک صفحه استفاده کرد. عناصر Meta را می توان برای تعیین توضیحات صفحه، کلمات کلیدی و هر metadata دیگری که از طریق عناصر و ویژگی های دیگرheadارائه نمی شود.عناصر متا اطلاعاتی در مورد صفحه وب ارائه می دهند که می تواند توسط موتورهای جستجو برای دسته بندی صحیح صفحه مورد استفاده قرار گیرد.استفاده از آن بسیار آسان است، فقط Meta را ازangular/platform-browser@ تزریق کرده و آن را در کامپوننت استفاده کنید.import { Meta } from &amp;quot@angular/platform-browser&amp;quot
@Component({
    ...
})
export class BlogComponent implements OnInit {
    constructor(private meta: Meta) {}    
    ngOnInit() {
        meta.updateTag({name: &amp;quottitle&amp;quot, content: &amp;quot&amp;quot})
        meta.updateTag({name: &amp;quotdescription&amp;quot, content: &amp;quotLorem ipsum dolor&amp;quot})
        meta.updateTag({name: &amp;quotimage&amp;quot, content: &amp;quot./assets/blog-image.jpg&amp;quot})
        meta.updateTag({name: &amp;quotsite&amp;quot, content: &amp;quotMy Site&amp;quot})
    }
}با این کار، BlogComponent ما می تواند در فیس بوک، توییتر، و غیره ارائه شود و کامپوننت ما را توصیف کند و عناوین، تصاویر و توضیحات ارائه دهد.3. بازنویسی Template Interpolationهمه ما از Template Interpolation پیش‌فرض {{}}در templateهای خود برای نمایش property ها در کامپوننت استفاده می‌کنیم.اگر یک عضو property را بین آنها قرار دهیم، در DOM مرورگر نمایش داده می شود.آیا می‌دانید که می‌توانیم جداکننده‌های شروع و پایان را به دلخواه خودمان تغییر دهیم؟ ساده است، آن را در ویژگیinterpolationموجود در Component decorator مشخص کنید.@Component({
    interpolation: [&amp;quot((&amp;quot,&amp;quot))&amp;quot]
})
export class AppComponent {}درون یابی برای استفاده در صفحه AppComponent به شکل&quot;(())&quot;است و دیگر&quot;{{}}&quot; نخواهد بود .@Component({
    template: `
        &lt;div&gt;
            ((data))
        &lt;/div&gt;
    `,
    interpolation: [&amp;quot((&amp;quot,&amp;quot))&amp;quot]
})
export class AppComponent {
    data: any = &amp;quotdataVar&amp;quot
}متن &quot;dataVar&quot;به جای((data)) رندر می شود .4.سرویس Locationما می توانیم URL پنجره فعلی مرورگر را با استفاده از سرویس Location دریافت کنیم. بسته به اینکه کدام LocationStrategy استفاده می شود، Locationیا در مسیر URL یا بخش هش URL باقی می ماند.با Location، می‌توانیم به یک URL برویم، در تاریخچه پلتفرم به جلو حرکت کنیم، در تاریخچه پلتفرم به عقب برگردیم، URL مرورگرها را تغییر دهیم.ما سرویس Location را از CommonModule تزریق می کنیم تا از آن استفاده کنیم.import { Location } from &amp;quot@angular/common&amp;quot
@Component({
    ...
})
export class AppComponent {
    constructor(private location: Location) {}    

    navigateTo(url) {
        this.location.go(url)
    }    
   goBack() {
        location.back()
    }    
   goForward() {
        location.forward()
    }
}5.ویژگی DOCUMENTگاهی اوقات می خواهیم مدل سند را دریافت کنیم تا بتوانیم عملیات روی DOM را در برنامه Angular خود انجام دهیم.توکن DOCUMENT دقیقاً همین را ارائه می دهد. DOCUMENT یک توکن DI است که محتوای اصلی رندر را نشان می دهد. در مرورگر، این سند DOM است. این عملیات DOM را به روشی environment-agnostic ارائه می دهد.توجه: زمانی که محتوای برنامه ما و محتوای رندر شده یکسان نیستند (مثلاً هنگام اجرای برنامه در Web Worker) ممکن است Document در Context برنامه در دسترس نباشد.فرض کنید یک عنصر در فایل html خود داریم:&lt;canvas id=&amp;quotcanvas&amp;quot&gt;&lt;/canvas&gt;با تزریق DOCUMENT می توانیم HTMLElement مربوط به canvas را بدست آوریم:@Component({})
export class CanvasElement {
    constructor(@Inject(DOCUMENT) _doc: Document) {}    
    renderCanvas() {
        this._doc.getElementById(&amp;quotcanvas&amp;quot)
    }
}ما می توانیم با خیال راحت این کار را با استفاده از ElementRef و template reference انجام دهیم، اما شما این روش را هم یاد گرفته اید.هشدار: با احتیاط قدم بردارید! تعامل مستقیم با DOM خطرناک است و می تواند خطرات XSS را ایجاد کند.6.دکوریتور Attribute@ما عمدتاً از decoratorها برای کامپوننت، ماژول و دایرکتیوها در برنامه Angular خود استفاده کرده ایم.ما این Attribute decorator را داریم که به ما امکان می دهد یک رشته استاتیک را بدون تاثیر منفی در performance با حذف change detection روی آن، بفرستیم.مقادیر Attribute decorator یک بار بررسی می شوند و دیگر بررسی نمی شوند. آنها مشابه Input decorator@ استفاده می شوند:@Component({
    ...
})
export class BlogComponent {
    constructor(@Attribute(&amp;quottype&amp;quot) private type: string ) {}
}7.واسط HttpInterceptorاین یک ویژگی بسیار قدرتمند در Angular است. interceptor هاHttpRequestرا رهگیری و مدیریت می کنند.اکثر interceptorها درخواست خروجی را قبل از ارسال آن به interceptor بعدی در زنجیره، با فراخوانی next.handle(transformedReq)تغییر می دهند .در موارد نادر، interceptorها ممکن است بخواهند خودشان یک درخواست را به طور کامل رسیدگی کنند و به بقیه زنجیره واگذار نکنند. این رفتار مجاز است.یک HttpInterceptor را می توان در موارد زیر استفاده کرد:احراز هویتفرآیند Cachingپیاده سازی Fake backendتبدیل URLاصلاح header هااستفاده از آن ساده است، ابتدا یک سرویس ایجاد کنید و رابط HttpInterceptor را پیاده سازی کنید.@Injectable()
export class MockBackendInterceptor implements HttpInterceptor {
    constructor() {}    
    intercept(req: HttpRequest&lt;any&gt;, next: HttpHandler): 
    Observable&lt;HttpEvent&lt;any&gt;&gt; {
        ...
    }
}سپس آن را در ماژول اصلی خود وارد کنید:@NgModule({
    ...
    providers: [
        {
            provide: HTTP_INTERCEPTORS,
            useClass: MockBackendInterceptor,
            multi: true
        }
    ]
    ...
})
export class AppModule {}8.ویژگی AppInitializerگاهی اوقات می‌خواهیم زمانی که برنامه Angular ما شروع به کار می‌کند، یک قطعه کد اجرا شود، شاید برخی تنظیمات بارگیری شود، حافظه cache بارگیری شود، پیکربندی‌ها بارگیری شود یا برخی از بررسی‌ها انجام شود. توکن AppInitialzer در این مورد کمک می کند.تابعAPP_INITIALIZER: تابعی که با مقداردهی اولیه (initialized) برنامه اجرا می شود.کاربردش ساده است. می خواهیم این تابع runSettings در راه اندازی برنامه Angular ما اجرا شود:function runSettingsOnInit() {
    ...
}ما به ماژول اصلی خود، AppModule می رویم و آن را به بخش providers در دکوراتور NgModule آن اضافه می کنیم:@NgModule({
    providers: [
        { provide: APP_INITIALIZER, useFactory: runSettingsOnInit }
    ]
})9.ویژگی Bootstrap Listenerدرست مانند  Angular ، AppInitializer دارای یک ویژگی است که به ما امکان می دهد زمانی که یک کامپوننت در حال بوت استرپ شدن است به آن گوش دهیم. اینAPP_BOOTSTRAP_LISTENER است.تمام متدهای ارائه شده از طریق این توکن برای هر کامپوننتی که بوت استرپ شده است فراخوانی می شود.ما دلایل زیادی برای گوش دادن به bootstrapping کامپوننت ها داریم، به عنوان مثال، ماژول Router از آن برای تخریب و ایجاد کامپوننت ها بر اساس route navigation استفاده می کند.برای استفاده APP_BOOTSTRAP_LISTENER، آن را با تابع callback به بخش providers در AppModule خود اضافه می کنیم:@NgModule({
    {
        provide: APP_BOOTSTRAP_LISTENER, multi: true, 
        useExisting: runOnBootstrap
    }
    ...
})
export class AppModule {}10.ویژگی  NgPluralتکثر Pluralization در حوزه خود مشکلی است. ما باید همیشه گرامر را در برنامه هایمان بر اساس مقدار مفرد/جمع (singular/plural) به درستی تعریف کنیم. برخی از وب سایت ها از (s) استفاده می کنند. مثل:1 component(s) removed
3 component(s) removed این بر عهده خواننده است که هنگام خواندن آن (s) را حذف کند یا (s) را اضافه کند :)در Angular، دستورالعمل NgPlural برای مدیریت تعداد جمعی در قالب‌ها استفاده می‌شود. این دستورالعمل یکی از ویژگی‌های بین‌المللی‌سازی (i18n) Angular است که به توسعه‌دهندگان امکان می‌دهد برنامه‌ها را به چند زبان پشتیبانی کرده و ویژگی‌های خاص زبانی مانند اعداد جمع را مدیریت کنند.برای استفاده از این دایرکتیو باید یک عنصر کانتینری ارائه کنید که [ngPlural]ویژگی را به عبارت سوئیچ تنظیم کند. عناصر داخلی با یک [ngPluralCase]اراده بر اساس بیان آنها نمایش داده می شود:&lt;p [ngPlural]=&amp;quotcomponents&amp;quot&gt;
    &lt;ng-template ngPluralCase=&amp;quot=1&amp;quot&gt;1 component removed&lt;/ng-template&gt;    
    &lt;ng-template ngPluralCase=&amp;quot&gt;1&amp;quot&gt;{{components}} components removed 
     &lt;/ng-template&gt;    
&lt;/p&gt;ببینید، ما از دستورالعمل NgPlural برای حذف (s)هنگام نمایش تعداد &quot;کامپوننت های حذف شده&quot; استفاده کرده ایم. نمایش خواهد داد:// if 1 component
1 component removed
// if 5 components
5 components removedدستورالعمل NgPlural معمولاً همراه با دستورالعمل NgSwitch استفاده می‌شود تا محتوا را براساس قوانین تعداد جمع زبان مورد نظر نمایش دهد. این دستورالعمل ورودی عددی را دریافت می‌کند و امکان تعریف قالب‌های مختلف بر اساس شکل جمع این عدد را فراهم می‌کند.در ادامه نمونه‌ای از نحوه استفاده از NgPlural در یک قالب Angular آورده شده است:&lt;div [ngSwitch]=&amp;quotmessageCount&amp;quot&gt;  &lt;ng-container *ngPlural=&amp;quotmessageCount&amp;quot&gt;    &lt;ng-container *ngPluralCase=&amp;quot&#039;=0&#039;&amp;quot&gt;No messages.&lt;/ng-container&gt;    &lt;ng-container *ngPluralCase=&amp;quot&#039;=1&#039;&amp;quot&gt;1 message.&lt;/ng-container&gt;    &lt;ng-container *ngPluralCase=&amp;quot&#039;other&#039;&amp;quot&gt;{{ messageCount }} messages.&lt;/ng-container&gt;  &lt;/ng-container&gt;&lt;/div&gt;در این نمونه، متغیری به نام messageCount وجود دارد که تعداد پیام‌ها را نمایش می‌دهد. دستورالعمل ngSwitch برای ارزیابی مقدار messageCount استفاده شده است و دستورالعمل ngPlural درون دستورالعمل ngSwitch استفاده شده است تا قالب‌های مختلف بر اساس شکل جمع مقدار تعریف شود.- دستورالعمل ngPluralCase با مقدار &#x27;=0&#x27; برای مدیریت حالتی استفاده می‌شود که messageCount برابر با صفر است.- دستورالعمل ngPluralCase با مقدار &#x27;=1&#x27; برای مدیریت حالتی استفاده می‌شود که messageCount برابر با یک است.- دستورالعمل ngPluralCase با مقدار &#x27;other&#x27; برای مدیریت سایر حالت‌ها استفاده می‌شود.دستورالعمل NgPlural قوانین مختلف تعداد جمع بر اساس زبان مورد نظر شما را پشتیبانی می‌کند. به طور پیش‌فرض، ویژگی بومی‌سازی Angular پشتیبانی از زبان‌های مختلفی را فراهم می‌کند. با این حال، شما همچنین می‌توانید قوانین تعداد جمع را سفارشی کنید یا با استفاده از کتابخانه‌های بومی‌سازی Angular پشتیبانی از زبان‌های اضافی را اضافه کنید.لازم به ذکر است که برای استفاده از دستورالعمل NgPlural، باید بسته بین‌المللی‌سازی (i18n) Angular را در پروژه Angular خود نصب و به درستی پیکربندی کنید.این مقاله برگردان شده یک مقاله معتبر درباره این موضوع است.برای دسترسی به مقاله منبع اینجا کلیک کنید.برای مشاهده پست های بیشتر و ارتباط با من از طریق لینکدین اینجا کلیک کنید.امیدوارم براتون مفید واقع شده باشه.</description>
                <category>مصطفی میری</category>
                <author>مصطفی میری</author>
                <pubDate>Fri, 16 Jun 2023 20:05:12 +0330</pubDate>
            </item>
                    <item>
                <title>بررسی عمیق OnPush Change Detection در Angular</title>
                <link>https://virgool.io/@mostafamiri/angular-change-detection-strategy-ip4qr6owiwsr</link>
                <description>فریم ورک Angular دو استراتژی را پیاده سازی می کند که رفتار change detection را در سطح کامپوننت ها کنترل می کند. این استراتژی ها به صورت زیر تعریف می شوند :Default - OnPushExport  enum  ChangeDetectionStrategy { 
 OnPush = 0 , 
 Default = 1
}فریم ورک Angular از این استراتژی‌ها برای تعیین اینکه آیا کامپوننت فرزند باید هنگام اجرای change detection برای کامپوننت والد بررسی شود یا خیر، استفاده می‌کند . استراتژی تعریف شده برای یک کامپوننت بر همه directiveهای فرزند تأثیر می گذارد زیرا آنها به عنوان بخشی از host component بررسی می شوند. یک استراتژی تعریف شده را نمی توان در زمان اجرا لغو کرد.استراتژی default، که بعنوانCheckAlwaysبه آن اشاره می‌شود ، change detection خودکار برای یک کامپوننت است، مگر اینکه view مشخصا جدا شده باشد. آنچه به عنوانOnPushاستراتژی شناخته می شود، به این معنی است که اگر یک کامپوننت به عنوان dirty علامت گذاری شود، change detection برای آن اجرا می شود . Angular مکانیسم هایی را برای علامت گذاری خودکار یک کامپوننت به عنوان dirty پیاده سازی می کند. در صورت نیاز، یک کامپوننت را می توان به صورت دستی با استفاده از متد markForCheck که در ChangeDetectorRef قرار دارد ، علامت گذاری کرد .وقتی که این استراتژی را با استفاده از دکوراتور ()Component@تعریف می کنیم، کامپایلر Angular آن را در تعریف یک کامپوننت از طریق تابع defineComponent ثبت می کند . به عنوان مثال، برای کامپوننتی مانند زیر:@Component({
 selector: &#039;a-op&#039;,
 template: `I am OnPush component`,
 changeDetection: ChangeDetectionStrategy.OnPush
})
export class AOpComponent {}کد تولید شده توسط کامپایلر به صورت زیر است:وقتی Angular یک کامپوننت را نمونه‌سازی می‌کند، از این تعریف برای تنظیم یک flag بر روی نمونه ساخته شده ازLView استفاده می‌کند که view کامپوننت را نشان می‌دهد:به این معنی است که همه نمونه‌هایLViewایجاد شده برای این کامپوننت دارای یکی از دو پرچم CheckAlways یا Dirty هستند. برای استراتژیOnPush، پرچمDirtyبه طور خودکار پس از اولین مرحله change detection تنظیم نمی شود.هنگامی که Angular تعیین می کند که آیا یک کامپوننت باید بررسی شود ، پرچم های تنظیم شده رویLViewدر داخل تابع refreshView بررسی می شوند :function refreshComponent(hostLView, componentHostIdx) {
 // Only attached components that are CheckAlways or OnPush and dirty 
 // should be refreshed
 if (viewAttachedToChangeDetector(componentView)) {
 const tView = componentView[TVIEW];
 if (componentView[FLAGS] &amp; (LViewFlags.CheckAlways | LViewFlags.Dirty)) {
 refreshView(tView, componentView, tView.template, componentView[CONTEXT]);
    } else if (componentView[TRANSPLANTED_VIEWS_TO_REFRESH] &gt; 0) {
 // Only attached components that are CheckAlways 
 // or OnPush and dirty should be refreshed
 refreshContainsDirtyView(componentView);
    }
  }
}بیایید اکنون این استراتژی ها را با جزئیات بیشتر بررسی کنیم.استراتژی defaultفرایند change detection در استراتژی default زمانی اجرا می‌شود که:یک Input@ آپدیت شود.یک Browser event مثل click, change, keyup و ... رخ دهد.یک setTimeout یا setInterval a در حال اجرا باشد.درخواست های async : مثل XHR و promises که اجرا شود.استراتژی default change detection به این معنی است که اگر کامپوننت والد بررسی شده باشد، کامپوننت فرزند همیشه بررسی می‌شود . تنها استثنای این قانون این است که change detector کامپوننت فرزند را مانند زیر جدا کنید:@Component({
 selector: &#039;a-op&#039;,
 template: `I am OnPush component`
})
export class AOpComponent {
 constructor(private cdRef: ChangeDetectorRef) {
    cdRef.detach();
  }
}توجه داشته باشید که اگر کامپوننت والد بررسی نشده باشد، انگولار change detection را برای کامپوننت فرزند اجرا نمی‌کند، حتی اگر از استراتژی default change detection استفاده کند. این از این واقعیت ناشی می شود که Angular این مکانیسم را برای کامپوننت فرزند به عنوان بخشی از بررسی والد آن اجرا می کند .فریم ورک Angular هیچ workflow را بر روی دولوپرها اعمال نمی کند تا تشخیص دهد که چه زمانی وضعیت یک کامپوننت تغییر می کند، به همین دلیل رفتار پیش فرض این است که همیشه کامپوننت ها را بررسی کند. یک مثال از workflow ، انتقال یک شی تغییرناپذیر از طریقInput@است. این چیزی است که برای استراتژیOnPushاستفاده می شود و در ادامه آن را بررسی خواهیم کرد.در اینجا ما یک سلسله مراتب ساده از دو کامپوننت داریم:@Component({
 selector: &#039;a-op&#039;,
 template: `
    &lt;button (click)=&amp;quotchangeName()&amp;quot&gt;Change name&lt;/button&gt;
    &lt;b-op [user]=&amp;quotuser&amp;quot&gt;&lt;/b-op&gt;
  `,
})
export class AOpComponent {
  user = { name: &#039;A&#039; };
 
 changeName() {
 this.user.name = &#039;B&#039;;
  }
}
 
@Component({
 selector: &#039;b-op&#039;,
 template: `&lt;span&gt;User name: {{user.name}}&lt;/span&gt;`,
})
export class BOpComponent {
 @Input() user;
}وقتی روی دکمه کلیک می کنیم، Angular یک event handler را اجرا می کند که در آن ما user.nameرا بروز رسانی می کنیم . به عنوان بخشی از اجرای حلقه change detection بعدی، کامپوننتB فرزند بررسی می شود و صفحه به روز می شود:در حالی که ارجاع به شیuserتغییر نکرده است، در داخل آن تغییراتی داشته است، اما همچنان می‌توانیم نام جدید را روی صفحه نمایش ببینیم. به همین دلیل است که رفتار پیش فرض این است که همه کامپوننت ها را بررسی کند. بدون محدودیت object immutability در Angular، نمی‌توان متوجه تغییر ورودی‌ها و  به‌روزرسانی وضعیت کامپوننت شد.استراتژی OnPushفرایند change detection در استراتژی default زمانی اجرا می‌شود که:مرجع ورودی Input@ تغییر کند.یک Browser event مثل click, change, keyup و ... رخ دهد.در حالی که Angular محدودیت object immutability را به ما تحمیل نمی کند، اما مکانیزمی به ما می دهد تا یک کامپوننت را دارای ورودی های تغییرناپذیر اعلام کنیم تا تعداد دفعات بررسی کامپوننت را کاهش دهیم. این مکانیزم استراتژی OnPushchange detectionاست و یک تکنیک بهینه سازی بسیار رایج است. در درون کدها این استراتژیCheckOnceنامیده می شود ، زیرا به این معنی است که از اجرای change detection برای یک کامپوننت صرفنظر می شود تا زمانی که به عنوان dirty علامت گذاری شود، سپس یک بار بررسی می شود و سپس دوباره رد می شود. یک کامپوننت را می توان به صورت خودکار یا دستی با استفاده از متدmarkForCheckعلامت گذاری dirty کرد.بیایید از مثال بالا استفاده کنیم و استراتژی OnPush change detectionرا برای کامپوننتB اعلام کنیم:@Component({...})
export class AOpComponent {
  user = { name: &#039;A&#039; };
 
 changeName() {
 this.user.name = &#039;B&#039;;
  }
}
 
@Component({
 selector: &#039;b-op&#039;,
 template: `
    &lt;span&gt;User name: {{user.name}}&lt;/span&gt;
  `,
 changeDetection: ChangeDetectionStrategy.OnPush
})
export class BOpComponent {
 @Input() user;
}وقتی برنامه را اجرا می کنیم Angular دیگر تغییری در user.name ایجاد نمی کند :می‌توانید ببینید که کامپوننتBهنوز یک بار در طول بوت استرپ بررسی می‌شود - مقدار اولیهAرا ارائه می‌کند . اما در طول اجراهای بعدی change detection بررسی نمی‌شود، بنابراین با کلیک روی دکمه، تغییر نام از Aبه بهBرا مشاهده نمی‌کنید. این به این دلیل اتفاق می‌افتد که ارجاع به شیuserکه از طریقInput@ به کامپوننتBمنتقل می‌شود، تغییر نکرده است.قبل از اینکه نگاهی به راه‌های مختلف علامت‌گذاری یک کامپوننت به عنوان dirty بیندازیم، در اینجا فهرستی از سناریوهای مختلفی وجود دارد که Angular برای تست رفتار OnPushاستفاده می‌کند:باید کامپوننت های OnPush را در هنگام به‌روزرسانی وقتی dirty نیستند، نادیده گرفت. نباید کامپوننت های OnPush را در هنگام به‌روزرسانی وقتی که رویدادهای والد رخ می‌دهند ، بررسی کنید.باید کامپوننت های OnPush را در هنگام مقداردهی اولیه بررسی کنید. باید doCheck را فراخوانی کنید، حتی زمانی که کامپوننت های OnPush ما dirty  نیستند.باید کامپوننت های OnPush را موقع به‌روزرسانی بررسی کنید وقتی inputها تغییر می‌کنند. باید کامپوننت های OnPush را موقع به روز رسانی بررسی کنید هنگامی که رویدادهای کامپوننت رخ می دهد.باید کامپوننت های OnPush را موقع بروز رسانی بررسی کنید هنگامی که رویدادهای فرزند رخ می دهد.باید کامپوننت های OnPush والد را بررسی کنید هنگامی که یک دایرکتیو فرزند در یک template یک ایونت را منتشر می کند.آخرین دسته از سناریوهای تستی نشان میدهد که فرآیند خودکار علامت‌گذاری یک کامپوننت dirty در سناریوهای زیر رخ می‌دهد:یک Input@ تغییر می کند.یک رویداد دریافت می شود که بر روی خود کامپوننت راه اندازی می شود.حال بیایید اینها را بررسی کنیم.ورودی با Input bindings@در بیشتر موقعیت‌ها، تنها زمانی که ورودی‌های آن کامپوننت تغییر می‌کند، باید کامپوننت فرزند را بررسی کنیم. این امر مخصوصاً در مورد کامپوننت های pure presentational که ورودی آنها صرفاً از طریق اتصالات می‌آید صادق است.بیایید مثال قبلی را در نظر بگیریم:@Component({...})
export class AOpComponent {
  user = { name: &#039;A&#039; };
 
 changeName() {
 this.user.name = &#039;B&#039;;
  }
}
 
@Component({
 selector: &#039;b-op&#039;,
 template: `
    &lt;span&gt;User name: {{user.name}}&lt;/span&gt;
  `,
 changeDetection: ChangeDetectionStrategy.OnPush
})
export class BOpComponent {
 @Input() user;
}همانطور که در بالا دیدیم، وقتی روی دکمه کلیک می کنیم و نام را تغییر می دهیم، نام جدید روی صفحه به روز نمی شود. دلیل آن این است که Angular مقایسه سطحی را برای پارامترهای ورودی انجام می دهد و ارجاع به شیuserتغییر نکرده است. تغییر مستقیم یک خاصیت شی منجر به ایجاد یک مرجع جدید نمی شود و به طور خودکار کامپوننت را علامت گذاری dirty نمی کند.ما باید مرجع شیuserرا برای Angular تغییر دهیم تا تفاوتInput@را تشخیص دهد.اگر به جای تغییر در نمونه موجود، یک نمونه جدید ایجاد کنیم ، همه چیز همانطور که انتظار می رود کار خواهد کرد:@Component({...})
export class AOpComponent {
  user = { name: &#039;A&#039; };
 
 changeName() {
 this.user = {
      ...this.user,
      name: &#039;B&#039;,
    }
  }
}بله، همه چیز خوب است:به دلیل اینکه با تغییر خاصیت شی این کامپوننت بررسی نمی شود،‌ با اجرای Object.freeze می توانید به راحتی تغییر ناپذیری را بر روی اشیاء اعمال کنید :export function deepFreeze(object) {
 const propNames = Object.getOwnPropertyNames(object);
 
 for (const name of propNames) {
 const value = object[name];
 if (value &amp;&amp; typeof value === &#039;object&#039;) {
 deepFreeze(value);
    }
  }
 
 return Object.freeze(object);
}به طوری که وقتی کسی سعی می کند شی را تغییر دهد، این خطا را می دهد:احتمالاً بهترین روش استفاده از یک کتابخانه تخصصی مانند immer است :import { produce } from &#039;immer&#039;;
 
@Component({...})
export class AOpComponent {
  user = { name: &#039;A&#039; };
 
 changeName() {
 this.user = produce(this.user, (draft) =&gt; {
      draft.name = &#039;B&#039;;
    });
  }
}این نیز به خوبی کار خواهد کرد.رویدادهای رابط کاربریهمه رویدادهای native، هنگامی که بر روی کامپوننت فعلی اجرا می شوند، همه اجداد کامپوننت تا کامپوننت ریشه را dirty می کنند. پیشفرض بر این است که یک رویداد می تواند باعث تغییر در درخت کامپوننت ها شود. Angular نمی داند که آیا والدین تغییر خواهند کرده اند. به همین دلیل است که Angular همیشه هر کامپوننت اجدادی را پس از اجرا شدن یک رویداد بررسی می کند.تصور کنید که یک سلسله مراتب درخت کامپوننت ها  مانند کامپوننت هایOnPushدارید :AppComponent 
  HeaderComponent 
  ContentComponent 
      TodoListComponent 
         TodoComponentاگر یک event listener را در داخل TodoComponenttemplateضمیمه کنیم :@Component({
 selector: &#039;todo&#039;,
 template: `
    &lt;button (click)=&amp;quotedit()&amp;quot&gt;Edit todo&lt;/button&gt;
  `,
 changeDetection: ChangeDetectionStrategy.OnPush
})
export class TodoComponent {
 edit() {}
}فریم ورک Angular قبل از اجرای event handler، همه کامپوننت های اجداد این کامپوننت را dirty می کند:از این رو سلسله مراتب کامپوننت ها به صورت زیر می شود :Root Component -&gt; LViewFlags.Dirty
     |
    ...
     |
   ContentComponent -&gt; LViewFlags.Dirty
     |
     |
   TodoListComponent  -&gt; LViewFlags.Dirty
     |
     |
 TodoComponent (event triggered here) -&gt; markViewDirty() -&gt; LViewFlags.Dirtyدر طول چرخه change detection بعدی، Angular کل درخت کامپوننت های اجدادیTodoComponentرا بررسی می کند:AppComponent (checked)
    HeaderComponent
    ContentComponent  (checked)
        TodosComponent  (checked)
            TodoComponent (checked)توجه داشته باشید که HeaderComponentبه دلیل اینکه از اجداد TodoComponent نیست بررسی نشده است .علامت گذاری دستی کامپوننت ها به عنوان dirty بیایید به مثالی برگردیم که در آن هنگام به‌روزرسانی نام، ارجاع به شیuserرا تغییر دادیم. این کار به Angular امکان می‌دهد تا تغییرات را دریافت کرده و به‌طور خودکار کامپوننتBرا به عنوان dirty علامت‌گذاری کند. فرض کنید می خواهیم نام را به روز کنیم اما نمی خواهیم مرجع را تغییر دهیم. در این صورت، می توانیم به صورت دستی کامپوننت را به عنوان dirty علامت گذاری کنیم.برای این کار می توانیم changeDetectorRefراتزریق کرده و از متدmarkForCheck آن استفاده کنیم تا برای Angular نشان دهیم که این کامپوننت باید بررسی شود:@Component({...})
export class BOpComponent {
 @Input() user;
 
 constructor(private cd: ChangeDetectorRef) {}
 
 someMethodWhichDetectsAndUpdate() {
 this.cd.markForCheck();
  }
}کجا می توانیم از someMethodWhichDetectsAndUpdateدر کد بالا استفاده کنیم ؟ هوک NgDoCheckکاندیدای بسیار خوبی است.این هوک قبل از اینکه Angular مکانیزم change detection را برای این کامپوننت اجرا کند و در حین بررسی کامپوننت والد اجرا می شود. اینجا جایی است که کد مقایسه مقادیر و همینطور علامت گذاری دستی کامپوننت به عنوان dirty هنگام change detection را قرار می دهیم.اجرای این کار درNgDoCheckحتی اگر یک کامپوننت OnPushباشد اغلب باعث سردرگمی می شود. اما این عمدی است و اگر بدانید که به عنوان بخشی از بررسی کامپوننت والد اجرا می شود، تناقضی وجود ندارد. به خاطر داشته باشید که ngDoCheckفقط برای بالاترین کامپوننت فرزند فعال می شود. اگر کامپوننت دارای تعدادی فرزند باشد و Angular این کامپوننت را بررسی نکند، ngDoCheckبرای آنها فعال نمی شود.ازngDoCheckبرای log کردن مراحل بررسی کامپوننت استفاده نکنید . درعوض، از تابع Accessor در داخل template مانند این استفاده کنید {{ ()logCheck }}.بنابراین بیایید منطق مقایسه خود را در داخل NgDoCheck معرفی کنیم و وقتی تغییر را تشخیص دادیم، کامپوننت  را dirty  علامت گذاری کنیم:@Component({...})
export class AOpComponent {...}
 
@Component({
 selector: &#039;b-op&#039;,
 template: `
    &lt;span&gt;User name: {{user.name}}&lt;/span&gt;
  `,
 changeDetection: ChangeDetectionStrategy.OnPush
})
export class BOpComponent {
 @Input() user;
  previousUserName = &#039;&#039;;
 
 constructor(private cd: ChangeDetectorRef) {}
 
 ngDoCheck() {
 if (this.user.name !== this.previousUserName) {
 this.cd.markForCheck();
 this.previousUserName = this.user.name;
    }
  }
}به یاد داشته باشید کهmarkForCheck تضمینی برای اجرای change detection نیست. برای جزئیات بیشتر به بخش manual control مراجعه کنید.نقش Observablesها به عنوان Inputs@حالا بیایید مثال خود را کمی پیچیده تر کنیم. بیایید فرض کنیم کامپوننت  فرزندB ما بر اساس RxJ قابل مشاهده است که به‌روزرسانی‌ها را به صورت ناهمزمان منتشر می‌کند. این مشابه چیزی است که ممکن است در معماری مبتنی بر NgRx داشته باشید:@Component({
 selector: &#039;a-op&#039;,
 template: `
    &lt;button (click)=&amp;quotchangeName()&amp;quot&gt;Change name&lt;/button&gt;
    &lt;b-op [user$]=&amp;quotuser$.asObservable()&amp;quot&gt;&lt;/b-op&gt;
  `,
})
export class AOpComponent {
  user$ = new BehaviorSubject({ name: &#039;A&#039; });
 
 changeName() {
 const user = this.user$.getValue();
 this.user$.next(
 produce(user, (draft) =&gt; {
        draft.name = &#039;B&#039;;
      })
    );
  }
}بنابراین ما این جریان از شیuser را در کامپوننت فرزندB دریافت می کنیم . ما باید subscribe کنیم، بررسی کنیم که آیا مقدار به روز شده است یا خیر و در صورت نیاز کامپوننت را به عنوان dirty علامت گذاری کنیم:@Component({
  selector: &#039;b-op&#039;,
  template: `
    &lt;span&gt;User name: {{user.name}}&lt;/span&gt;
  `,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class BOpComponent {
 @Input() user$;
  user = null;
 
 constructor(private cd: ChangeDetectorRef) {}
 
 ngOnChanges() {
  this.user$.subscribe((user) =&gt; {
  if (user !== this.user) {
  this.cd.markForCheck();
  this.user = user;
      }
    });
  }
}منطق داخل ngOnChangesتقریباً همان کاری است که async pipe انجام می دهد :export class AsyncPipe {
 transform() {
 if (obj) {
 this._subscribe(obj);
    }
  }
 
 private _updateLatestValue(async, value) {
 if (async === this._obj) {
 this._latestValue = value;
 this._ref!.markForCheck();
    }
  }
}به همین دلیل است که رویکرد متداول این است که منطق subscription و مقایسه را به async pipe واگذار کنیم . تنها محدودیت این است که اشیا باید تغییر ناپذیر باشند.در اینجا اجرای کامپوننتB که از async pipe استفاده می کند، آمده است:@Component({
 selector: &#039;b-op&#039;,
 template: `
    &lt;span&gt;User name: {{(user$ | async).name}}&lt;/span&gt;
  `,
 changeDetection: ChangeDetectionStrategy.OnPush
})
export class BOpComponent {
 @Input() user$;
}مجموعه ای از test casesها وجود دارد که async pipe و تعامل آن با انواع مختلف را آزمایش می کنند:describe(&#039;Observable&#039;, () =&gt; {
	describe(&#039;transform&#039;, () =&gt; {
	  it(&#039;should return null when subscribing to an observable&#039;);
	  it(&#039;should return the latest available value&#039;);
	  it(&#039;should return same value when nothing has changed since the last call&#039;);
	  it(&#039;should dispose of the existing subscription when subscribing to a new observable&#039;);
	  it(&#039;should request a change detection check upon receiving a new value&#039;);
	  it(&#039;should return value for unchanged NaN&#039;);
	});
});
describe(&#039;Promise&#039;, () =&gt; {...});
describe(&#039;null&#039;, () =&gt; {...});
describe(&#039;undefined&#039;, () =&gt; {...});
describe(&#039;other types&#039;, () =&gt; {...});این تست برای موردی است که در اینجا بررسی کردیم:it(&#039;should request a change detection check upon receiving a new value&#039;, done =&gt; {
    pipe.transform(subscribable);
    emitter.emit(message);
 
 setTimeout(() =&gt; {
 expect(ref.markForCheck).toHaveBeenCalled();
 done();
    }, 10);
});در اینجا pipe به observable درون transformمتصل شده است و هنگامی که observable یک messageجدید منتشر می کند ، کامپوننت را به عنوان dirty علامت گذاری می کند.این مقاله برگردان شده یک مقاله معتبر درباره این موضوع است.برای دسترسی به مقاله منبع اینجا کلیک کنید.برای مشاهده پست های بیشتر و ارتباط با من از طریق لینکدین اینجا کلیک کنید.امیدوارم براتون مفید واقع شده باشه.</description>
                <category>مصطفی میری</category>
                <author>مصطفی میری</author>
                <pubDate>Fri, 09 Jun 2023 02:16:31 +0330</pubDate>
            </item>
                    <item>
                <title>مکانیزم Change Detection در Angular</title>
                <link>https://virgool.io/@mostafamiri/angular-change-detection-mlsle1bgmup2</link>
                <description>این مقاله با توضیح درباره خطای ExpressionChangedAfterItHasBeenChecked شروع می شود و از آن برای بررسی دقیق مکانیسم change detection و جزئیات پیاده سازی داخلی آن استفاده می کند.برنامه های کاربردی وب مدرن تعاملی هستند. وضعیت یک برنامه می تواند در هر زمان در نتیجه کلیک دکمه یا درخواستی از سرور تغییر کند. و همانطور که وضعیت تغییر می کند، کد باید آن را شناسایی کند و تغییر را در رابط کاربری منعکس کند. این کار اصلی مکانیزم change detection است .در طول سال گذشته، من مقالات زیادی در مورد مکانیزم change detection در Angular نوشتم. آنها توضیحات مفصلی ارائه می دهند و بسیاری از جزئیات داخلی را پوشش می دهند. اما، آنها همچنین به زمان زیادی برای خواندن کامل نیاز دارند. برای کسانی از شما که وقت ندارید اما با این وجود کنجکاو هستید این مقاله توضیحی «سبک‌تر» از مکانیسم change detection ارائه می‌دهد. این یک دید کلی و سطح بالا از اجزای تشکیل دهنده و مکانیزم آن و ساختارهای داخلی که برای نمایش یک component  استفاده شده است، قوانین binding ها و عملیات های  انجام شده  را توضیح میدهد. من همچنین به zone ها  را اشاره می‌کنم و به شما نشان می‌دهم که دقیقاً چگونه این عملکرد، تشخیص خودکار تغییرات را در Angular فعال می‌کند.وقتی همه چیز خراب می شود ، دانش داخلی change detection به شما کمک می کند تا خطاها را به طور موثرتر اشکال زدایی کنید ، مثل خطایExpressionChangedAfterItHasBeenCheckedErrorو همچنین از برخی سردرگمی های رایج جلوگیری کنید . در این مقاله من چند اقدام را نشان می‌دهم که باعث ایجاد خطا می‌شوند و از آنها برای توضیح آنچه درون change detection اتفاق میفتد استفاده می‌کنم.اولین برخوردبیایید با این کامپوننت ساده Angular شروع کنیم. ساعت را در لحظه ای که change detection در برنامه اتفاق می افتد، ارائه می دهد. نشانگر ساعت دارای دقت میلی ثانیه است. با کلیک بر روی دکمه change detection فعال می شود:در اینجا پیاده سازی آن آورده شده است :@Component({
    selector: &#039;my-app&#039;,
    template: `
        &lt;h3&gt;
            Change detection is triggered at:
            &lt;span [textContent]=&amp;quottime | date:&#039;hh:mm:ss:SSS&#039;&amp;quot&gt;&lt;/span&gt;
        &lt;/h3&gt;
        &lt;button (click)=&amp;quot0&amp;quot&gt;Trigger Change Detection&lt;/button&gt;
    `
})
export class AppComponent {
    get time() {
        return Date.now();
    }
}همانطور که می بینید مثالی ساده است. دریافت کننده ای به نام time وجود دارد که زمان فعلی را بر می گرداند و من آن را به عنصرspanدر HTML متصل می کنم.در Angular اجازه استفاده از empty expressions داده نمی دهد، بنابراین من مقدار صفر را به عنوان مقدار بازگشتی به رویداد کلیک داده ام.اینجا می توانید این قطعه کد را اجرا کنید . هنگامی که Angular فرآیند change detection را اجرا می کند، مقدار ویژگیtimeرا می گیرد ، آن را از date pipe عبور می دهد و از نتیجه برای به روز رسانی DOM استفاده می کند. همه چیز همانطور که انتظار می رود کار می کند. با این حال، وقتی کنسول را بررسی می کنم، این ExpressionChangedAfterItHasBeenCheckedErrorخطا را می بینم:این در واقع بسیار تعجب آور است. معمولاً این خطا در پیاده سازی های بسیار پیچیده تر ظاهر می شود. بنابراین چگونه ممکن است که ما آن را با چنین عملکرد ساده ای دریافت کنیم؟ نگران نباشید، ما اکنون آن را بررسی می کنیم.بیایید با پیام خطا شروع کنیم:عبارت بعد از بررسی تغییر کرده است. مقدار قبلی: “textContent: 1542375826274”. مقدار فعلی: “textContent: 1542375826275”.به ما می گوید که مقادیر تولید شده برای textContent متفاوت است. بله، میلی‌ثانیه‌ها واقعاً متفاوت هستند. بنابراین Angular عبارت  time | date: hh:mm:ss:SSSرا دو بار ارزیابی کرد و نتایج را مقایسه کرد. تفاوت را تشخیص داد و این همان چیزی است که باعث خطا شد.اما چرا Angular این مقایسه را انجام می دهد؟ یا دقیقا چه زمانی این کار را انجام می دهد؟اینها سوالاتی بود که کنجکاوی من را برانگیخت و در نهایت مرا به درون change detection کشاند. زیرا، برای یافتن پاسخ این سؤالات باید شروع به اشکال زدایی کنم. و من داشتم debugging می کردم و خب، فکر می کنم حدود ... چند ماه طول کشید. بیایید با سوال دوم شروع کنیم که خطا در چه زمانی اتفاق میفتد. اما ابتدا باید برخی از یافته‌هایم را با شما به اشتراک بگذارم که به ما در درک رفتاری که در بالا مشاهده کردیم کمک می‌کند.تعریف Component views and bindingsدو بلوک اصلی برای change detection در Angular وجود دارد:یک : component viewدو : اتصالات یا bindingهر کامپوننت در Angular دارای یک template با عناصر HTML دارد. وقتی Angular گره‌های DOM را برای نمایش محتوای template روی صفحه ایجاد می‌کند، به مکانی برای ذخیره ارجاعات به آن گره‌های DOM نیاز دارد. برای این منظور،  یک ساختار داده درونی به نام View وجود دارد. همچنین برای ذخیره ارجاع به نمونه های ساخته شده از component ها و مقادیر قبلی عبارات bind شده استفاده می شود. یک رابطه یک به یک بین یک component و یک view وجود دارد. در اینجا نموداری است که view را نشان می دهد:همانطور که کامپایلر template را تجزیه و تحلیل می کند، property های عناصر DOM را شناسایی می کند که ممکن است نیاز به بروز رسانی در طول change detection داشته باشند. برای هر یک از این ویژگی ها، کامپایلر یک binding ایجاد می کند . Binding نام ویژگی برای به روز رسانی و عبارتی را که Angular برای بدست آوردن مقدار جدید استفاده می کند، تعریف می کند.در این نمونه، ویژگیtimeبرای خاصیت textContentاستفاده می شود . بنابراین Angular یک اتصال ایجاد می کند و آن را با عنصرspanمرتبط می کند:در پیاده سازی واقعی، یکviewDefinitionپیوندهایی را برای عناصر template و property ها برای روز رسانی تعریف می کند. عبارت مورد استفاده برای bind کردن در تابعupdateRendererقرار می گیرد.بررسی یک component viewهمانطور که می دانید در Angular فرآیند change detection برای هر کامپوننت انجام می شود. اکنون که می دانیم کامپوننت ها در داخل به صورت viewهایی نمایش داده می شوند، می توان گفت که change detection برای هر view انجام می شود.هنگامی که Angular یک view را بررسی می کند، بر روی تمام اتصالات ایجاد شده برای view توسط کامپایلر اجرا می شود. عبارات را ارزیابی می کند و نتیجه آنها را با مقادیر ذخیره شده در  آرایهoldValuesروی view مقایسه می کند. نام dirty checking از همین جا می آید. اگر تفاوت را تشخیص دهد، ویژگی DOM مربوط به binding را به روز می کند. و همچنین باید مقدار جدید را در آرایهoldValuesدر view قرار دهد. اکنون یک رابط کاربری به روز شده دارید. هنگامی که Angular بررسی component فعلی را انجام داد، دقیقاً همان مراحل را برای componentهای فرزند تکرار می‌کند.در برنامه ما، فقط یک اتصال به ویژگی textContent عنصر span در کامپوننت App وجود دارد. بنابراین در حین change detection در برنامه، Angular مقدار time را می خواند،پایپ date را اعمال می کند و آن را با مقدار قبلی ذخیره شده در view مقایسه می کند. اگر تفاوتی را تشخیص دهد، Angular ویژگی textContent در span و آرایه oldValues ​​را به روز می کند.اما خطا از کجا می آید؟پس از هر چرخه change detection، در حالت develop، انگولار به طور همزمان یک بررسی دیگر را اجرا می‌کند تا اطمینان حاصل کند که عبارات همان مقادیر را در طول اجرای change detection قبلی ایجاد می‌کنند. این بررسی بخشی از چرخه change detection اصلی نیست. پس از اتمام بررسی برای کل درخت کامپوننت ها اجرا می شود و دقیقاً همان مراحل را انجام می دهد. با این حال، این بار، همانطور که Angular تفاوت را تشخیص می دهد، DOM را به روز نمی کند. در عوض،خطای  ExpressionChangedAfterItHasBeenCheckedError را برمیگرداند.دلیل این اتفاق چیست ؟بنابراین اکنون می دانیم که خطا چه زمانی رخ داده است. اما چرا Angular به این بررسی نیاز دارد؟ خوب، تصور کنید که برخی از ویژگی های کامپوننت ها در طول اجرای change detection به روز شده اند. در نتیجه، عبارات مقادیر جدیدی تولید می‌کنند که با آنچه در رابط کاربری ارائه می‌شود ناسازگار است. بنابراین، Angular چه کاری انجام می دهد؟ مطمئناً می تواند چرخه change detection دیگری را برای همگام سازی وضعیت برنامه با رابط کاربری اجرا کند. اما اگر در طول این فرآیند برخی از ویژگی ها دوباره به روز شوند چه؟ الگو را ببینید؟ Angular در واقع می تواند در یک حلقه نامحدود از اجراهای change detection ختم شود. و در واقع، این اتفاق اغلب در AngularJS رخ می دهد .برای جلوگیری از این وضعیت، Angular به اصطلاح جریان داده یک طرفه  را اعمال کرد . و این بررسی که بعد از change detection اجرا می شود و ExpressionChangedAfterItHasBeenCheckedError خطای حاصل از آن مکانیسم اجرایی است. هنگامی که Angular اتصالات را برای کامپوننت فعلی پردازش کرد، دیگر نمی‌توانید property های کامپوننت را که در bindings استفاده می‌شود، به‌روزرسانی کنید.رفع خطابرای جلوگیری از خطا، باید اطمینان حاصل کنیم که مقادیر بازگردانده شده توسط عبارات در طول اجرای change detection و بررسی بعدی یکسان هستند. در این نمونه ی ما، می‌توانیم این کار را با انتقال بخشtimeبه خارج از getter  انجام دهیم :export class AppComponent {
    _time;
    get time() {  return this._time; }

    constructor() {
        this._time = Date.now();
    }
}با این حال، با این پیاده سازی مقدار دریافت کننده timeهمیشه یکسان خواهد بود. ما هنوز باید مقدار را به روز کنیم. قبلاً یاد گرفتیم که آن بررسی که خطا را ایجاد می‌کند دقیقاً بعد از چرخه change detection به‌طور synchronously اجرا می‌شود. بنابراین اگر آن را به‌صورت asynchronously به‌روزرسانی کنیم، از خطا جلوگیری می‌کنیم. بنابراین برای به‌روزرسانی مقدار در هر میلی‌ثانیه، می‌توانیم از تابع setIntervalبا تأخیر 1میلی‌ثانیه استفاده کنیم:export class AppComponent {
    _time;
    get time() {  return this._time; }

    constructor() {
        this._time = Date.now();

        setInterval(() =&gt; {
            this._time = Date.now();
        }, 1);
    }
}این پیاده سازی مشکل اصلی ما را حل می کند. اما متأسفانه یک مورد جدید را معرفی می کند. همه رویدادهای زمان‌بندی، مانند setInterval،روند change detection را در Angular راه‌اندازی می‌کنند. این بدان معناست که با این پیاده‌سازی، در یک حلقه نامحدود از چرخه‌های change detection قرار می‌گیریم. برای جلوگیری از آن، ما به راهی برای اجرا setIntervalو عدم ایجاد change detection نیاز داریم. خوشبختانه برای ما، راهی برای انجام این کار وجود دارد. برای یادگیری نحوه انجام این کار، باید در وهله اول دلیل انجام change detection توسطsetInterval در Angular را درک کنیم.تشخیص خودکار تغییر با zoneهابرخلاف React،فرآیند change detection در Angular می‌تواند به طور کاملاً خودکار در نتیجه هر رویداد غیرهمزمان (async event) در مرورگر فعال شود. این امر با استفاده از کتابخانه ای به نام zone.jsکه مفهوم zoneها را معرفی می کند ممکن می شود. برخلاف تصور رایج، zoneها بخشی از مکانیسم change detection در Angular نیستند. در واقع، Angular می تواند بدون آنها کار کند . این کتابخانه به سادگی راهی برای رهگیری رویدادهای غیرهمزمان، مانند setInterval، و اطلاع دادن به Angular در مورد آنها فراهم می کند. بر اساس آن اعلان، Angular روند change detection را اجرا می کند.جالب اینجاست که شما می توانید zoneهای مختلفی را در یک صفحه وب داشته باشید. یکی از آنهاNgZone است . هنگامی که Angular بوت استرپ می شود ایجاد می شود. این همان zone است که برنامه Angular در آن اجرا می‌شود. و Angular فقط اعلان‌هایی درباره رویدادهایی که در داخل این منطقه رخ می‌دهند دریافت می‌کند.اما، zone.jsهمچنین یک API برای اجرای برخی از کدها در zoneای غیر از angular zone ارائه می دهد. Angular از رویدادهای ناهمگام در مناطق دیگر مطلع نمی شود. و عدم اطلاع رسانی به معنای عدم انجام change detection است. نام متدی که برای انجام این کار فراخوانی می شود runOutsideAngularو توسط سرویس NgZoneپیاده سازی می شود .در اینجا تزریقNgZoneو اجرای setInterval خارج از angularzone را داریم :export class AppComponent {
    _time;
    get time() {
        return this._time;
    }

    constructor(zone: NgZone) {
        this._time = Date.now();

        zone.runOutsideAngular(() =&gt; {
            setInterval(() =&gt; {
                this._time = Date.now()
            }, 1);
        });
    }
}اکنون ما دائماً زمان را به روز می کنیم، اما این کار را به صورت ناهمزمان و خارج از منطقه Angular انجام می دهیم. این تضمین می کند که در هنگام change detection و بررسی بعد از آن، time getterهمان مقدار را برمی گرداند. و هنگامی که Angular مقدارtime را در چرخه change detection بعدی می خواند، مقدار به روز می شود و تغییرات بر روی صفحه نمایش نمایش داده می شود.استفاده از NgZone برای اجرای برخی از کدها در خارج از Angular برای جلوگیری از شروع change detection یک تکنیک بهینه سازی رایج است.اشکال زداییشاید از خود بپرسید که آیا راهی برای دیدن این view و اتصالات داخل Angular وجود دارد یا خیر. در واقع وجود دارد. یک تابع به نامcheckAndUpdateView داخل ماژول@angular/coreوجود دارد . روی هر view (component) در درخت کامپوننت ها اجرا می شود و بررسی هر view را انجام می دهد. این تابعی است که من همیشه وقتی با change detection مشکل دارم، اشکال زدایی را شروع می کنم.سعی کنید این را برای خودتان دیباگ کنید. به این برنامه stackblitz بروید و کنسول را باز کنید. تابع را پیدا کنید و یک breakpoint در آنجا قرار دهید. روی دکمه کلیک کنید تا change detection فعال شود. متغیر viewرا بررسی کنید. در اینجا یک تصویر از من در حال انجام این کار است:اولین view، viewمیزبان است. این نوعی کامپوننت ریشه است که توسط Angular برای میزبانی app component ما ایجاد شده است. ما باید اجرا را از سر بگیریم تا به view فرزند آن برسیم که view ایجاد شده برایAppComponent ما خواهد بود . آن را بررسی کنید . این ویژگیpropertycomponentدارای ارجاع به نمونه ساخته شده از Appcomponentاست.  ویژگی (property)هایnodes ارجاع به گره های DOM ایجاد شده برای عناصر داخل template کامپوننتApp دارند. آرایهoldValuesنتایج عبارات bindشده را ذخیره می کند.ترتیب عملیات هاما به تازگی آموخته ایم که به دلیل محدودیت جریان داده یک طرفه، نمی توانید برخی از ویژگی های یک کامپوننت را در حین change detection پس از بررسی این مؤلفه تغییر دهید. این به‌روزرسانی از طریق یک سرویس مشترک یا پخش رویداد همزمان ( asynchronous event broadcasting ) هنگام اجرای change detection برای کامپوننت های فرزند انجام می‌شود. اما همچنین می‌توان مستقیماً یک کامپوننت والد را به کامپوننت فرزند تزریق کرد و وضعیت والد را در یک lifecycle hook به‌روزرسانی کرد. در اینجا کدی وجود دارد که این را نشان می دهد:@Component({
    selector: &#039;my-app&#039;,
    template: `
        &lt;div [textContent]=&amp;quottext&amp;quot&gt;&lt;/div&gt;
        &lt;child-comp&gt;&lt;/child-comp&gt;
    `
})
export class AppComponent {
    text = &#039;Original text in parent component&#039;;
}

@Component({
    selector: &#039;child-comp&#039;,
    template: `&lt;span&gt;I am child component&lt;/span&gt;`
})
export class ChildComponent {
    constructor(private parent: AppComponent) {}

    ngAfterViewChecked() {
        this.parent.text = &#039;Updated text in parent component&#039;;
    }
}اینجا می توانید آن را امتحان کنید . اساسا، ما یک سلسله مراتب ساده از دو کامپوننت را تعریف می کنیم. کامپوننت والد خاصیت textمورد استفاده در binding را اعلام می کند. کامپوننت فرزند کامپوننت والد را به سازنده تزریق می کند و property آن را در ngAfterViewCheckedبه روز می کند. آیا می توانید حدس بزنید که قرار است در کنسول چه چیزی ببینیم؟ ?درست است، خطای آشنای ExpressionChangedAfterItWasChecked . و این به این دلیل است که وقتی Angular چرخه حیاتngAfterViewChecked را برای کامپوننت فرزند فراخوانی می‌کند، قبلاً اتصال کامپوننت Appوالد را بررسی کرده است. اما ما در حال به روز رسانی ویژگی text در والد که در binding استفاده  شده است هستیم.اما قسمت جالب اینجاست. اگر الان hook را عوض کنم چه؟ مثلا ، به ngOnInit. آیا فکر می کنید ما هنوز هم خطا را مشاهده خواهیم کرد؟export class ChildComponent {
    constructor(private parent: AppComponent) {}

    ngOnInit() {
        this.parent.text = &#039;Updated text in parent component&#039;;
    }
}خب، این بار خطا آنجا نیست. این دمو را اجرا و بررسی کنید . در واقع ما می توانیم کد را در هر hook دیگری (به جزAfterViewInit وAfterViewChecked) قرار دهیم و خطا را در کنسول مشاهده نخواهیم کرد. پس اینجا چه خبر است؟ چراngAfterViewChecked یک hookخاص است؟برای درک این رفتار، باید بدانیم که Angular چه عملیاتی را در حین change detection انجام می دهد و ترتیب آنها را انجام می دهد. و، ما قبلاً می دانیم که کجا می توانیم آنها را پیدا کنیم: تابع checkAndUpdateViewکه قبلاً به شما نشان دادم. در اینجا بخشی از کدی است که می توانید در بدنه تابع پیدا کنید:function checkAndUpdateView(view, ...) {
    ...       
    // update input bindings on child views (components) &amp; directives,
    // call NgOnInit, NgDoCheck and ngOnChanges hooks if needed
    Services.updateDirectives(view, CheckType.CheckAndUpdate);
    
    // DOM updates, perform rendering for the current view (component)
    Services.updateRenderer(view, CheckType.CheckAndUpdate);
    
    // run change detection on child views (components)
    execComponentViewsAction(view, ViewAction.CheckAndUpdate);
    
    // call AfterViewChecked and AfterViewInit hooks
    callLifecycleHooksChildrenFirst(…, NodeFlags.AfterViewChecked…);
    ...
}همانطور که می بینید، Angular همچنین hook های چرخه حیات را به عنوان بخشی از change detection فعال می کند. نکته جالب این است که برخی از هوک ها قبل از قسمت رندر زمانی که Angular پردازش های binding را انجام می دهد و برخی بعد از آن فراخوانی می شوند. در اینجا نموداری وجود دارد که نشان می‌دهد هنگام اجرای change detection در Angular برای کامپوننت والد چه اتفاقی می‌افتد:بیایید قدم به قدم آن را مرور کنیم. ابتدا، اتصالات ورودی برای کامپوننت فرزند را به روز می کند . سپس دوباره , هوک های DoCheck, OnInitو OnChanges را بر روی کامپوننت فرزند فراخوانی می کند . منطقی است زیرا فقط اتصالات ورودی را به روز کرده است و Angular باید به کامپوننت های فرزند اطلاع دهد که اتصالات ورودی initial شده اند. سپس Angular رندر کامپوننت فعلی را انجام می دهد. و پس از آن، change detection را برای کامپوننت فرزند اجرا می کند. این بدان معنی است که اساساً این عملیات را در view فرزند تکرار می کند. و در نهایت، کامپوننت فرزندAfterViewInit و AfterViewChecked رافراخوانی می کند تا به آن اطلاع دهد که بررسی شده است.چیزی که در اینجا می توانیم متوجه شویم این است که Angular پس از پردازش اتصالات کامپوننت والد، هوک AfterViewChecked را برای کامپوننت فرزند فراخوانی می کند . از طرف دیگر، چرخه OnInitقبل از پردازش binding ها فراخوانی می شود . بنابراین حتی اگر تغییری در مقدارtext در OnInitوجود داشته باشد ، باز هم در بررسی زیر یکسان خواهد بود. و این رفتار به ظاهر عجیب و غریب نداشتن خطا با هوک ngOnInitرا توضیح می دهد . خلاصهخوب، بیایید اکنون آنچه را که یاد گرفتیم خلاصه کنیم. تمام کامپوننت ها در Angular به صورت داخلی در یک ساختار داده به نام view نمایش داده می‌شوند. کامپایلر Angular یک template را تجزیه می کند و bindingها را ایجاد می کند. هر binding یک property از یک عنصر DOM را برای به روز رسانی و عبارتی مورد استفاده برای به دست آوردن مقدار تعریف می کند. مقادیر قبلی که برای مقایسه در حین change detection استفاده می‌شوند، روی یک view در آرایهoldValues ذخیره می‌شوند. در حین change detection در برنامه، Angular روی bindingها اجرا می شود، عبارات را ارزیابی می کند، آنها را با مقادیر قبلی مقایسه می کند و در صورت لزوم DOM را به روز می کند. پس از هر چرخه change detection در برنامه، Angular بررسی می‌کند تا مطمئن شود که وضعیت کامپوننت با رابط کاربری همگام است یا خیر. این بررسی به صورت همزمان انجام می شود و ممکن است خطای ExpressionChangedAfterItWasChecked راایجاد کند.این مقاله برگردان شده یک مقاله معتبر درباره این موضوع است.برای دسترسی به مقاله منبع اینجا کلیک کنید.برای مشاهده پست های بیشتر و ارتباط با من از طریق لینکدین اینجا کلیک کنید.امیدوارم براتون مفید واقع شده باشه.</description>
                <category>مصطفی میری</category>
                <author>مصطفی میری</author>
                <pubDate>Sun, 04 Jun 2023 00:32:23 +0330</pubDate>
            </item>
                    <item>
                <title>شروع کار با Angular Universal و SSR در Angular</title>
                <link>https://virgool.io/@mostafamiri/angular-universal-wkg3kxdmmun6</link>
                <description>عنوان Angular Universal فرآیند رندر سمت سرور (SSR) برنامه شما به HTML روی سرور (یعنی Node.js) است.برنامه‌های معمولی Angular  برنامه‌های تک صفحه‌ای (معروف به SPA) هستند که در آن رندر در مرورگر انجام می‌شود . این فرآیند را می توان به عنوان رندر سمت کلاینت(CSR) نیز نامید.در این مقاله به درک عمیقی خواهیم پرداخت: Angular Universal چیست، انواع مختلف SSR چیست، و چگونه می‌توانیم برنامه‌های خود را برای تبدیل شدن به Universal تنظیم کنیم.اگر به Angular Universal علاقه مند بوده اید، یا به دنبال اطلاعاتی در مورد Angular Universal هستید، ممکن است برای شروع کار با مشکل مواجه شده باشید، زیرا در چند سال گذشته تغییرات زیادی رخ داده است و اطلاعات نادرست تاریخ گذشته یا غلط زیادی وجود دارد!در این مجموعه چند قسمتی به موارد زیر می پردازیم:عنوان Angular Universal چیست؟کار با Angular Universalمعماری و انواع SSR.توجه : قبلاً Angular Universal با نامangular2-universalدر npm شناخته می‌شد . مطمئن شوید که هر مقاله یا کدی را بر اساس آن نادیده بگیرید، زیرا مدت‌هاست که منسوخ شده است!حالا Angular Universal چیست؟بحث Angular Universal نام مستعار رندر سمت سرور (به اختصار SSR) با Angular است . از نظر فنی پکیج مربوط به آن درangular/platform-server@ یافت می شود .اکنون که کمی از این موضوع فهمیدیم، بیایید به این موضوع بپردازیم که چگونه می‌توانید رندر سرور برنامه‌های Angular خود را همین حالا شروع کنید!شروع با Angular Universalدر این آموزش قصد داریم شروع کار با Angular Universal را با کمک Angular CLI بررسی کنیم.ابتدا، اجازه دهید یک پروژه Angular-CLI جدید ایجاد کنیم:مطمئن شوید که آخرین Angular-CLI را دارید . npm i --g @angular/cliتوجه: در صورت تمایل می توانید از این موضوع صرف نظر کنید و مستقیماً سعی کنید Universal را به برنامه موجود خود اضافه کنید.ng new some-amazing-projectحالا بیایید Angular Universal ? را اضافه کنیم.از Angular-CLI نسخه 6.1، کد ng addطرح شماتیک اضافه شده است که به شما امکان می دهد به سرعت چهارچوب مورد نیاز، سرور Node express و سایر ارتباطات مورد نیاز برای شروع کار را ایجاد کنید.نصب شماتیک Angular Universalبه پوشه اصلی پروژه Angular-CLI خود که می‌خواهید Universal را به آن اضافه کنید وارد شوید، و شماتیک زیر را اجرا کنید.به clientProject [name]-- توجه کنید .به فایل angular.json خود نگاهی بیندازید و مطمئن شوید که &quot;نام&quot; پروژه ای را که می خواهید رندر سمت سرور را به آن اضافه کنید، قرار داده اید.2. در مرحله بعد، ازng addبرای نصب شماتیک ها در برنامه ما استفاده کنید.ng add @nguniversal/express-engine --clientProject [name]
// angular.json
{ ...
  &amp;quotprojects&amp;quot: {
    &amp;quotsome-amazing-project&amp;quot: {}
}در مثال بالا، می‌خواهیم از کد زیر استفاده کنیم :ng add @nguniversal/express-engine --clientProject some-amazing-projectچگونه Angular Universal را برای برنامه خود اجرا کنیم؟معمولاً ما ng serveرااجرا می‌کنیم تا برنامه معمولی CSR خود را اجرا کنیم.وقتی می خواهیم نسخه رندر شده سمت سرور خود را بسازیم و اجرا کنیم، باید از چند اسکریپت استفاده کنیم که به package.json ما اضافه شده است .قبل از اینکه به آن اسکریپت‌های مهم بپردازیم، می‌خواهم 2 موضوع مهم مختلف در دنیای رندر سمت سرور (صرف نظر از JS Framework / غیره) را پوشش دهم…رندر سمت سرور (SSR) پویا و Static Pre-renderingرندر سمت سرور (SSR) پویا مفهومی است که وقتی یک سرور Node  ، هر زمان مسیری مورد بازدید قرار می‌گیرد، به صورت پویا برنامه را تولید می‌کند - آن رشته را به مرورگر برمی‌گرداند.پیش رندر استاتیک زمانی است که می‌خواهیم فهرستی از مسیرها را از قبل رندر کنیم و فایل‌های استاتیک ایجاد کنیم (به عنوان مثال: index.html، about-us.html، و غیره) و سپس از یک سرور انتخابی خود برای ارائه آن فایل‌ها در آینده استفاده کنیم.پس چگونه بفهمیم کدام یک را چه زمانی انتخاب کنیم؟پیش‌رندر (Pre-rendering) معمولاً عملکرد بهتری به شما می‌دهد، زیرا ما منتظر نیستیم تا سرور تمام APIهای لازم را در برنامه شما را بررسی کند، و هیچ چیزی نباید «serialized» شود، در حال حاضر تمام HTMLهای serialized شده برنامه شما برای هر کدام از فایل های Routes آماده شده است.در اینجا لیست خوبی از سوالاتی است که قبل از بررسی مسیری که باید طی کنیم با کلاینت در نظر می گیریم.زمان استفاده از Static Pre-Rendering:برنامه شما به خودی خود نسبتا Static است.(یا حداقل مسیرهایی که می‌خواهید از pre-render کنید)به عنوان مثال: homepage | about us | contact usبخش‌های بسیار پویا از سایت شما (و آنهایی که پشت یک مانع Login/Auth قرار دارند) را می‌توان به نسخه نرم‌افزار رندر شده سمت کلاینت (CSR) برنامه شما در نظر گرفت و Angular می‌تواند به طور معمول خود را بوت استرپ کند.برنامه شما اغلب به روز نمی شود.هر زمان که تغییراتی در مسیرهای استاتیک مورد نیاز باشد، می‌توانید به سادگی اسکریپت buildرا دوباره اجرا کنید و پوشه‌ dist را که حاوی همه فایل‌های از پیش رندر شده‌تان است، دوباره منتشر کنید.زمان استفاده از Dynamic SSR:برنامه شما (و مسیرهایی که به SSR نیاز دارید) بسیار پویا هستندشما فهرستی از“trending products” | “live data” و غیره دارید، که باید برای هر رندر سمت سرور به درستی پر شود.ساختار برنامه های شما بر اساس فایل های JSON یا یک CMS ارائه می شود که در آن هر چیزی ممکن است در هر لحظه تغییر کند.معمولاً اکثر برنامه‌ها به پیش‌رندر استاتیک نیاز دارند زیرا هر مسیری که در پشت دیوار احراز هویت است سود زیادی از استفاده از SSR نمی‌برد، زیرا یکی از اهداف اصلی، دستاوردهای SEO و performance است.به یاد داشته باشید، همیشه می توانید جنبه های خاصی از برنامه خود را در طول SSR رندر نکنید و آن بخش های پویا در طول CSR رندر کنید! در بخش بعدی این مجموعه به آن خواهیم پرداخت.نحوه راه اندازی Angular Universalبا بازگشت به package.json خود ، می بینیم تعداد زیادی اسکریپت جدید اضافه شده است، اما اگر به زیر نگاهی بیندازید - هر دو روش SSR را فوراً در اختیار شما قرار می دهند!توجه داشته باشید، همچنین می‌توانید این اسکریپت‌ها را به یک اسکریپت کوتاه کنید که هر دو را اجرا می‌کند تا برای شما راحت‌تر شود.// Dynamic SSR                                                                                                                                     npm run build:ssr &amp;&amp; npm run serve:ssrاین برنامه شما را کامپایل می کند و یک سرور Node Express را می چرخاند تا برنامه یونیورسال شما را در آن سرویس دهدhttp://localhost:4000// Static Pre-Rendering                                                                                                                        
npm run build:prerender &amp;&amp; npm run serve:prerenderاین اسکریپت برنامه شما را کامپایل می‌کند و فایل‌های برنامه‌ها شما را از قبل رندر(pre-render) می‌کند و یک http-server دمو را اجرا میکند تا بتوانید آن را درhttp://localhost:8080 مشاهده کنید.توجه: برای استقرار وبسایت استاتیک خود در یک پلتفرم static hosting، شما باید پوشه dist/browser را استقرار دهید، به جای مسیر معمولی dist.پس چطور می‌توانیم مطمئن شویم که عملکرد درستی داشته است؟ بسته به نسخه‌ای که راه‌اندازی کرده‌اید (پویا/استاتیک)، اگر به آدرس localhost دسترسی داشته باشید، باید ببینید که بین تگ معمولا خالیه &lt;app-root&gt;&lt;/app-root&gt; حالا دارای محتوا است! ✨ ✨شما می‌توانید&lt;title&gt;My Application is amazingggg&lt;/title&gt; خود را تنظیم کنید. شما می‌توانید تگ &lt;meta&gt;&lt;/meta&gt; خود را تنظیم کنید. محتوای برنامه شما serialized می‌شود.برای مشاهده محتوای خروجی در داخل&lt;app-root&gt;&lt;/app-root&gt; ، جاوا اسکریپت یا View Source را غیرفعال کنید.از این نسخهبه نسخه ssrراه اندازی دستی Angular Universal:اگر ترجیح می‌دهید همه چیز را خودتان اضافه کنید، نگاهی به ویکی CLI بیندازید که (گام به گام) نحوه افزودن تمام قطعات به یک پروژه CLI را شرح می‌دهد:https://github.com/angular/angular- cli/wiki/stories-universal renderingهمچنین می توانید نگاهی به مخزن Angular Universal-Starter بیاندازید که نتیجه نهایی مراحل نشان داده شده در ویکی است. تمام فایل های جدید مورد نیاز، راه اندازی سرور اصلی Node و غیره.این مقاله برگردان شده یک مقاله معتبر درباره این موضوع است.برای دسترسی به مقاله منبع اینجا کلیک کنید.برای مشاهده پست های بیشتر و ارتباط با من از طریق لینکدین اینجا کلیک کنید.امیدوارم براتون مفید واقع شده باشه.</description>
                <category>مصطفی میری</category>
                <author>مصطفی میری</author>
                <pubDate>Mon, 29 May 2023 22:39:08 +0330</pubDate>
            </item>
                    <item>
                <title>بهترین شیوه‌های مدیریت state ها در انگولار</title>
                <link>https://virgool.io/@mostafamiri/angular-best-practice-wdi5x5n4tmet</link>
                <description>بهترین شیوه‌ها عمدتاً به ترجیحات شخصی مربوط می‌شوند و افرادی با نظرات متفاوت می‌توانند با آن ها مخالفت کنند.همانطور که گفته شد، بهترین روش ها در این مقاله بر اساس یک دهه کار با برنامه های کاربردی تک صفحه ای و مدیریت وضعیت ها است.من در 7 سال گذشته در بیش از 100 پروژه Angular حضور داشته ام و هزاران روش مختلف را دیده ام و طرز فکر صدها و صدها متخصص مختلف را یاد گرفته ام.من flux، redux، state models، @ngrx/store، Akita، Ngxs، BehaviorSubjects، ObservableState، Signals را انجام داده ام... چیزهای زیادی دیده ام و این مقاله درباره نحوه استدلال من در مورد حالت است.مدیریت state چیست ؟این ممکن است یک سوال پیش پا افتاده به نظر برسد اما نظرات مختلفی در مورد این موضوع وجود دارد. اولین سوالی که می خواهیم از خود بپرسیم این است: &quot;state چیست؟&quot;. &quot;وضعیت یک مقدار یا شی است که در حافظه نگهداری می شود&quot;.یک property روی یک component که دارای یک مقدار است، حالت در نظر گرفته می شود.یک property روی یک service که دارای یک مقدار است، حالت در نظر گرفته می شود.مقدار یک ویژگی ()Input@ حالت در نظر گرفته می شود.مقداری که در نوعی store در چارچوب مدیریت state مانند ngrx@ نگهداری می شود، حالت در نظر گرفته می شود.مقداری که در سطح ngModule یا سطح route ارائه می شود، حالت در نظر گرفته می شود.@Component({...}
export class HelloCompontent {
    // This message property is state
    public message = &#039;hello&#039;;
}حالا مدیریت state چیست؟ زمانی در مورد مدیریت state صحبت خواهیم کرد که یک یا چند مورد از این گفته ها درست باشد:وقتی می خواهیم حالت را به روز کنیم.وقتی می خواهیم حالت را اضافه کنیم.وقتی می خواهیم حالت را باطل کنیم.وقتی می خواهیم حالت را بین کامپوننت ها اشتراک بگذاریم.وقتی می خواهیم حالت را بین فیچر ها به اشتراک بگذاریم.وقتی می‌خواهیم حالت را در db، localstorage و غیره حفظ کنیم.اولین تصور اشتباه :بسیاری از توسعه دهندگان فکر می کنند که مدیریت state به این معنی است که: به اشتراک گذاری وضعیت در سراسر برنامه. این درست نیست، و با این استدلال در مورد state مردم تمایل دارند تا وضعیت های زیادی را مدیریت کنند یا آن را اشتباه مدیریت کنند.مدیریت state به سادگی مثال زیر است :@Component({...}
export class HelloCompontent {
    // This message property is state
    public message = &#039;hello&#039;;

    // This is state management
    public update(newMessage: string): void {
        this.message = newMessage;
    }
}دومین تصور اشتباه :اینکه آیا شما عاشق ایجاد اکشن‌ها برای هر چیزی هستید که باعث ایجاد افکت‌ها می‌شود، که اینها نیز  باعث ایجاد افکت‌های دیگری می‌شوند که دوباره باعث ایجاد افکت‌های دیگر می‌شوند، تصمیمی است که هر تیم باید برای خود بگیرد.اما بیایید وانمود نکنیم که این مدیریت state است.بهترین شیوه شماره 1: KISS (Keep it simple, stupid)هنگامی که این اصل برای اکثر راه حل ها در توسعه نرم افزار (یا حتی در زندگی) معتبر است، بسیار مهم است که در مدیریت state نیز این ساده نگه داشتن را رعایت کنید.انگولار از مدیریت state مبتنی بر RxJS به سمت مدیریت حالت سیگنال محور حرکت می کند.این پیام بسیار واضحی را ارسال می کند: تیم اصلی انگولار می خواهد مدیریت حالت را ساده تر کند.بهترین شیوه شماره 2: حالتی را که نباید مدیریت کنید، مدیریت نکنید.این به این خلاصه می شود: همه چیز را در یک store قرار ندهید. اگر انجام دهید:برای به دست آوردن آن حالت، باید از حلقه ها بپرید.با کدهای تکراری و هزینهنگهداری زیادی مواجه خواهید شد.شما باید آن حالت را در یک زمان معین باطل کنید.شما تمام ماژول های feature برنامه خود را به هم می چسبانید.شما با یک طراحی store پیچیده به پایان خواهید رسید.این مثال را در نظر بگیرید،آیا این کد ساده تر از این نمی تواند باشد؟!‍‍‍@Component({
    ...
    template: `
    &lt;user-form [user]=&amp;quotuser$|async&amp;quot&gt;&lt;/user-form&gt;
    `
})
export class UserComponent {
    ...
    public user$ = this.userId$.pipe(
        switchMap(id =&gt;this.userService.getById(id))
    );

}این مثال ساده:هر بار که $userId تغییر می کند، داده ها را برای ما واکشی می کند.به طور خودکار برای ما subscribes می کند.داده ها را به صورت خودکار برای ما در هنگام subscription واکشی می کند.به طور خودکار برای ما unsubscribes می کند.برای مثال، با قرار دادن آن در store در ngrx@ ، پیچیدگی های زیر را داریم:یک reducers , selector , effects , actionType , action باید نوشته شوند.ما مستقیماً به پاسخ ()getById دسترسی نداریم، باید آن را در store اضافه کنیم.ما مستقیماً به خطاها دسترسی نداریم، اگر می‌خواهیم به آنها دسترسی پیدا کنیم، باید آن‌ها را در store اضافه کنیم.وقتی کامپوننت از بین می‌رود، باید user را باطل کنیم، بنابراین در متد ()ngOnDestroy باید این کار را انجام دهیم.قانون کلی این است: اگر حالت شما نیازی به اشتراک گذاری ندارد، آن را در store قرار ندهید.بهترین شیوه شماره 3: state خود را تا حد امکان پایین نگه دارید.منظور ما از آن این است که اگر به حالت خود در پایین ترین مؤلفه فرزند نیاز دارید، و فقط در آنجا... آن را همانجا نگه دارید!اگر می‌خواهید یک حالت را بین دو مؤلفه خواهر و برادر به اشتراک بگذارید، وضعیت را در مؤلفه والد این دو نگه دارید.اگر نیاز دارید که یک حالت را بین smart components به اشتراک بگذارید، state ارائه شده را در feature حفظ کنید.اگر واقعاً نیاز به یک قسمت از state دارید که بین ماژول‌های مختلف لود شده تنبل به اشتراک گذاشته شود، در آن صورت، ما در مورد global state صحبت می‌کنیم و این بالاترین سطح است. همیشه سعی کنید آن را تا حد امکان پایین نگه دارید. در آینده از خود تشکر خواهید کرد!انگولار دارای این ویژگی تزریق وابستگی عالی است که در آن ما می توانیم یک تزریق را در انواع مختلف سطوح ارائه کنیم. به عنوان مثال به این مثال توجه کنید:@Component({
    ...
    // provided right on this ChatboxComponent
    providers: [ChatboxState]
})
export class ChatboxComponent {
    private readonly state = inject(ChatboxState);
}اکنون ChatboxState برای ChatboxComponent مهیا شده است و چرخه عمر آن مؤلفه را نیز به اشتراک می‌گذارد.این بدان معناست که وقتی ChatboxComponent از بین می‌رود، نمونه ChatboxState نیز از بین می‌رود. حتی می توانید قلاب چرخه حیات ()ngOnDestroy را در ChatboxState نیز پیاده سازی کنید:export class ChatboxState implements OnDestroy {
    // gets called when the item that provides this class gets destroyed
    public ngOnDestroy(): void {
    }
}هرچه این state را پایین تر نگه دارید، مدیریت آن برای ما آسان تر خواهد شد. در این سناریو، به عنوان مثال، بی اعتباری حالت به صورت رایگان اتفاق می افتد.بهترین شیوه شماره 4: حالت را synchronous نگه دارید، همیشه snapshots ارائه دهید.طرز تفکر سالم در مورد state این است که state همیشه باید یک مقدار داشته باشد. این بدان معناست که ما همیشه نمی‌خواهیم subscribe کنیم، اما می‌خواهیم یک snapshots  از آن مقدار دریافت کنیم.این مثال بد را در نظر بگیرید:public saveUser(): void {
    this.user$
        .pipe(
            take(1),
            withLatestFrom(this.courses$),
            takeUntil(this.destroy$)
        )
        .subscribe(({user, courses}) =&gt; {
            this.userService.update(user, courses).subscribe({...})
        })
}از آنجا که $user و $courses هر دو observable هستند، نمی‌توانیم فقط مقادیری از آنها دریافت کنیم. برای بازیابی آن مقادیر، باید subscribe کنیم. اگر حالت ما synchronous باشد، این قطعه کد بسیار تمیزتر می شود:public saveUser(): void {
    const {user, courses} = this.state.snapshot;
    this.userService.update(user, courses).subscribe({...})
}آن قطعه کد از ObservableState استفاده می‌کند، اما سیگنال‌ها نیز در مورد آن هستند. با سیگنال ها به شکل زیر است:public saveUser(): void {
    const {user, courses} = this.state();
    this.userService.update(user, courses).subscribe({...})
}بهترین شیوه شماره 5: همیشه مقادیر اولیه را ارائه دهید.هنگام کار با state، این یک راه سالم است که همیشه به مقادیر اولیه فکر کنید: اگر می توانید از مقدار null اجتناب کنید، در برخی موارد به آن نیاز خواهید داشت، اما نمونه سازی یک آرایه با [ ] کد شما را قوی تر می کند.شما خطای &quot;Cannot read properties of null&quot; کمتری خواهید داشت و استدلال در مورد state را آسان تر می کند.نوشتن type برای state نیز آسان تر می شود زیرا مجبور نیستید your-type&gt;|null&gt; را بعد از هر نوع حالت اضافه کنید. این برای مثال می تواند بسیار آزاردهنده باشد:type UserState = {
    firstName: string|null,
    lastName: string|null
    // etc
}بهترین شیوه شماره 6: ساختارهای داده تغییرناپذیرساختارهای داده تغییرناپذیر لزوماً به دلیل بهینه سازی عملکرد استفاده نمی شوند. دلیل استفاده از آن این است که این ساختارها قابل پیش بینی هستند. ما می‌توانیم Change Detection را بهینه کنیم، می‌توانیم از عملگرهای RxJS استفاده کنیم و می‌توانیم جریان‌های داده یک طرفه تمیز ایجاد کنیم.بطور خلاصه : چه از چارچوب‌های مدیریت حالت استفاده کنیم یا از پیاده‌سازی‌های سفارشی، سعی کنید آن را ساده نگه دارید، state را تا حد امکان پایین نگه دارید، مطمئن شوید که مقادیر اولیه و snapshots دارید و وضعیتی را که نباید مدیریت کنید، مدیریت نمی‌کنید.لینک مقاله اصلی: https://blog.simplified.courses/angular-state-management-best-practices</description>
                <category>مصطفی میری</category>
                <author>مصطفی میری</author>
                <pubDate>Sun, 21 May 2023 14:18:48 +0330</pubDate>
            </item>
            </channel>
</rss>