برنامه نویسی

پارامترهای URL روتر زاویه ای با استفاده از فروشگاه روتر NgRx

هنگامی که ما برنامه‌هایی را با حالت می‌سازیم، نقطه ورود کلیدی برای مقداردهی اولیه وضعیت ما برای اجزای ما است، اما گاهی اوقات، ما الزاماتی برای حفظ وضعیت برنامه در URL داریم تا به کاربران اجازه دهیم تا حالت‌های برنامه خاص را نشانه‌گذاری کنند یا به اشتراک بگذارند، با هدف بهبود کاربر. تجربه کنید و ناوبری را آسان کنید.

در بیشتر موارد، Angular Router و ActivatedRoute را در کامپوننت‌های خود ترکیب می‌کنیم تا این موارد را حل کنیم و این مسئولیت را به کامپوننت‌ها محول می‌کنیم یا در موارد دیگر ترکیبی بین اجزا و افکت ایجاد می‌کنیم تا سعی کنیم آن را حل کنیم.

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

سناریو

من می خواهم یک صفحه ویرایش ایجاد کنم که در آن کاربران بتوانند جزئیات مکان انتخاب شده را تغییر دهند، URL را به اشتراک بگذارند و بعداً به همان حالت برگردند. مثلا، http://localhost/places/2، جایی که 2 شناسه مکانی است که در حال ویرایش است. کاربران همچنین باید بتوانند پس از انجام یک عمل به صفحه اصلی بازگردند.

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

https://www.danywalls.com/understanding-when-and-why-to-implement-ngrx-in-angular

https://www.danywalls.com/how-to-debug-ngrx-using-redux-devtools

https://www.danywalls.com/how-to-implement-actioncreationgroup-in-ngrx

https://www.danywalls.com/how-to-use-ngrx-selectors-in-angular

https://danywalls.com/when-to-use-concatmap-mergemap-switchmap-and-exhaustmap-operators-in-building-a-crud-with-ngrx

مخزن را شبیه سازی کنید start-with-ngrx، این پروژه را با ngrx و برنامه آماده کرده و به شعبه سوئیچ کنید crud-ngrx

https://github.com/danywalls/start-with-ngrx.git
git checkout crud-ngrx
وارد حالت تمام صفحه شوید

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

زمان کدنویسی فرا رسیده است!

صفحه ویرایش

ابتدا ترمینال را باز کنید و با استفاده از Angular CLI یک کامپوننت جدید ایجاد کنید:

ng g c pages/place-edit
وارد حالت تمام صفحه شوید

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

بعد، باز کنید app.routes.ts و ثبت نام کنید PlaceEditComponent با پارامتر /places/:id:

{
  path: 'places/:id',
  component: PlaceEditComponent,
},
وارد حالت تمام صفحه شوید

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

مکان ویرایش را دریافت کنید

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

  • روش را در سرویس مکان ها اضافه کنید.

  • گوش دادن به اقدامات

  • موفقیت را برای به روز رسانی وضعیت مکان انتخاب شده تنظیم کنید.

  • مکان انتخاب شده را در edit-place.component بخوانید.

ابتدا اضافه کنید getById روش در places.service.ts، با استفاده از شناسه مکان را بدست می آورد.

getById(id: string): ObservablePlace> {
  return this.http.getPlace>(`${environment.menorcaPlacesAPI}/${id}`);
}
وارد حالت تمام صفحه شوید

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

در مرحله بعد، اقدامات جدیدی را برای مدیریت getById اضافه کنید، باز کنید places.actions.ts اعمال را به ویرایش، موفقیت و شکست اضافه کنید:

// PlacePageActions
'Edit Place': props{ id: string }>(),

// PlacesApiActions
'Get Place Success': props{ place: Place }>(),
'Get Place Failure': props{ message: string }>(),
وارد حالت تمام صفحه شوید

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

برای انجام این اقدامات، کاهنده را به‌روزرسانی کنید:

on(PlacesApiActions.getPlaceSuccess, (state, { place }) => ({
  ...state,
  loading: false,
  placeSelected: place,
})),
on(PlacesApiActions.getPlaceFailure, (state, { message }) => ({
  ...state,
  loading: false,
  message,
})),
وارد حالت تمام صفحه شوید

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

باز کن place.effects.ts، یک افکت جدید برای گوش دادن به آن اضافه کنید editPlace اقدام، تماس placesService.getById، و سپس برای ارسال پاسخ دریافت کنید getPlaceSuccess عمل.

export const getPlaceEffect$ = createEffect(
  (actions$ = inject(Actions), placesService = inject(PlacesService)) => {
    return actions$.pipe(
      ofType(PlacesPageActions.editPlace),
      mergeMap(({ id }) =>
        placesService.getById(id).pipe(
          map((apiPlace) =>
            PlacesApiActions.getPlaceSuccess({ place: apiPlace })
          ),
          catchError((error) =>
            of(PlacesApiActions.getPlaceFailure({ message: error }))
          )
        )
      )
    );
  },
  { functional: true }
);
وارد حالت تمام صفحه شوید

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

این راه حل امیدوار کننده به نظر می رسد. باید اکشن editPlace را ارسال کنم و روتر را به داخل تزریق کنم place-card.component.ts برای حرکت به /places:id مسیر

goEditPlace(id: string) {
  this.store.dispatch(PlacesPageActions.editPlace({ id: this.place().id }));
  this.router.navigate(['/places', id]);
}
وارد حالت تمام صفحه شوید

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

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

💡یک راه حل، به لطف Jörgen de Groot، انتقال روتر به سمت افکت است. باز کن places.effect.ts سرویس و روتر را فایل و تزریق کنید. به اکشن editPlace گوش دهید، داده ها را دریافت کنید، سپس حرکت کنید و اقدام را ارسال کنید.

کد نهایی به شکل زیر است:

export const getPlaceEffect$ = createEffect(
  (
    actions$ = inject(Actions),
    placesService = inject(PlacesService),
    router = inject(Router)
  ) => {
    return actions$.pipe(
      ofType(PlacesPageActions.editPlace),
      mergeMap(({ id }) =>
        placesService.getById(id).pipe(
          tap(() => console.log('get by id')),
          map((apiPlace) => {
            router.navigate(['/places', apiPlace.id]);
            return PlacesApiActions.getPlaceSuccess({ place: apiPlace });
          }),
          catchError((error) =>
            of(PlacesApiActions.getPlaceFailure({ message: error }))
          )
        )
      )
    );
  },
  { functional: true }
);
وارد حالت تمام صفحه شوید

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

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

قلاب‌های چرخه حیات افکت‌ها به ما اجازه می‌دهند تا زمانی که افکت‌ها ثبت می‌شوند، اکشن‌ها را راه‌اندازی کنیم، بنابراین می‌توانم اکشن loadPlaces را راه‌اندازی کنم و حالت را آماده کنم.

export const initPlacesState$ = createEffect(
  (actions$ = inject(Actions)) => {
    return actions$.pipe(
      ofType(ROOT_EFFECTS_INIT),
      map((action) => PlacesPageActions.loadPlaces())
    );
  },
  { functional: true }
);
وارد حالت تمام صفحه شوید

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

درباره چرخه عمر Effect و ROOT_EFFECTS_INIT بیشتر بخوانید

بسیار خوب، من وضعیت را آماده کرده ام، اما هنوز هنگام دریافت شناسه از وضعیت URL مشکل دارم.

یک راه حل سریع این است که بخوانید activatedRoute که در ngOnInit. اگر id حضور دارد، اقدام را ارسال کنید editPlace. این تغییر مسیر می دهد و تنظیم می کند selectedPlace حالت.

پس تزریق کن activatedRoute دوباره در PlaceEditComponent و منطق را در آن پیاده سازی کنید ngOnInit.

کد به شکل زیر است:

export class PlaceEditComponent implements OnInit {
  store = inject(Store);
  place$ = this.store.select(PlacesSelectors.selectPlaceSelected);
  activatedRoute = inject(ActivatedRoute);

  ngOnInit(): void {
    const id = this.activatedRoute.snapshot.params['id'];
    if (id) {
      this.store.dispatch(PlacesPageActions.editPlace({ id }));
    }
  }
}
وارد حالت تمام صفحه شوید

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

کار می کند! در نهایت، یک دکمه لغو برای تغییر مسیر به مسیر مکان ها اضافه می کنیم و رویداد کلیک را برای فراخوانی یک متد جدید، لغو، متصل می کنیم.

button (click)="cancel()" class="button is-light" type="reset">Cancel/button>
وارد حالت تمام صفحه شوید

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

به یاد داشته باشید که تزریق کنید router برای فراخوانی متد پیمایش به URL مکان ها. کد نهایی به شکل زیر است:

export class PlaceEditComponent implements OnInit {
  store = inject(Store);
  place$ = this.store.select(PlacesSelectors.selectPlaceSelected);
  activatedRoute = inject(ActivatedRoute);
  router = inject(Router);

  ngOnInit(): void {
    const id = this.activatedRoute.snapshot.params['id'];
    if (id) {
      this.store.dispatch(PlacesPageActions.editPlace({ id }));
    }
  }

 cancel() {
  router.navigate(['/places']);
 }
}
وارد حالت تمام صفحه شوید

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

بسیار خوب، با همه ویژگی‌ها کار می‌کند، اما مؤلفه ما وظایف بسیاری را انجام می‌دهد، مانند ارسال اقدامات و تغییر مسیر ناوبری. وقتی به ویژگی های بیشتری نیاز داشته باشیم چه اتفاقی می افتد؟ ما می توانیم همه چیز را با استفاده از روتر NgRx ساده کنیم که باعث کاهش میزان کد و مسئولیت در اجزای ما می شود.

چرا فروشگاه روتر NgRx؟

فروشگاه روتر NgRx اتصال وضعیت خود را با رویدادهای روتر و خواندن داده‌ها از روتر با استفاده از انتخابگرهای داخلی آسان می‌کند. گوش دادن به اقدامات روتر تعامل با داده ها و جلوه ها را ساده می کند و اجزای ما را از وابستگی های اضافی مانند روتر یا مسیر فعال دور نگه می دارد.

اقدامات روتر

روتر NgRx پنج عملکرد روتر را ارائه می دهد، این اقدامات به ترتیب ماشه هستند

  • ROUTER_REQUEST: هنگام شروع یک ناوبری.

  • ROUTER_NAVIGATION: قبل از محافظ و هفت تیر، در حین ناوبری کار می کند.

  • روتر؟ ناوبری: هنگامی که ناوبری کامل شد.

  • ROUTER_CANCEL: زمانی که ناوبری لغو می شود.

  • ROUTER_ERROR: زمانی که خطایی وجود دارد.

درباره ROUTER_ACTIONS بیشتر بخوانید

انتخابگرهای مسیریاب

این به خواندن اطلاعات از روتر، مانند پارامترهای پرس و جو، داده ها، عنوان و موارد دیگر، با استفاده از لیستی از انتخابگرهای داخلی ارائه شده توسط تابع کمک می کند. getRouterSelectors.

export const { selectQueryParam, selectRouteParam} = getRouterSelectors()
وارد حالت تمام صفحه شوید

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

درباره انتخابگرهای مسیریاب بیشتر بخوانید

از آنجا که، ما یک نمای کلی از روتر NgRx داریم، پس بیایید پیاده سازی آن را در پروژه شروع کنیم.

روتر NgRx را پیکربندی کنید

ابتدا باید روتر NgRx را نصب کنیم. انتخابگرهایی را برای خواندن از روتر و ترکیب با انتخابگرهای دیگر برای کاهش صفحه دیگ در اجزای ما فراهم می کند.

در ترمینال، ngrx/router-store را با استفاده از شماتیک ها نصب کنید:

ng add @ngrx/router-store
وارد حالت تمام صفحه شوید

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

بعد، باز کنید app.config و ثبت نام کنید routerReducer و provideRouterStore.

  providers: [
    ...,
    provideStore({
      router: routerReducer,
      home: homeReducer,
      places: placesReducer,
    }),
    ...
    provideRouterStore(),
  ],
وارد حالت تمام صفحه شوید

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

ما روتر NgRx را در پروژه خود داریم، بنابراین اکنون زمان کار با آن است!

درباره نصب روتر NgRx بیشتر بخوانید

با استفاده از NgRx RouterSelectors ساده کنید

به جای درخواست HTTP، از وضعیت خود استفاده می کنم زیرا افکت ngrx init همیشه هنگام ثبت اثر، وضعیت من را به روز می کند. این بدان معناست که من آخرین داده ها را دارم. من می توانم ترکیب کنم selectPlaces انتخابگر با selectRouterParams برای بدست آوردن selectPlaceById.

باز کن places.selector.ts فایل، ایجاد و صادر کردن یک انتخابگر جدید با ترکیب selectPlaces و selectRouteParams.

کد نهایی به شکل زیر است:

export const { selectRouteParams } = getRouterSelectors();

export const selectPlaceById = createSelector(
  selectPlaces,
  selectRouteParams,
  (places, { id }) => places.find((place) => place.id === id),
);

export default {
  placesSelector: selectPlaces,
  selectPlaceSelected: selectPlaceSelected,
  loadingSelector: selectLoading,
  errorSelector: selectError,
  selectPlaceById,
};
وارد حالت تمام صفحه شوید

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

عالی، اکنون زمان به روز رسانی و کاهش همه وابستگی ها در است PlaceEditComponentو از انتخابگر جدید استفاده کنید PlacesSelectors.selectPlaceById. کد نهایی به شکل زیر است:

export class PlaceEditComponent {
  store = inject(Store);
  place$ = this.store.select(PlacesSelectors.selectPlaceById);
}
وارد حالت تمام صفحه شوید

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

بسیار خوب، اما در مورد کنش لغو و تغییر مسیر چطور؟ ما می توانیم یک اقدام جدید ارسال کنیم، cancel، برای رسیدگی به این در اثر.

ابتدا باز کنید places.action.ts و عمل را اضافه کنید 'Cancel Place': emptyProps(). کد نهایی به شکل زیر است:

 export const PlacesPageActions = createActionGroup({
  source: 'Places',
  events: {
    'Load Places': emptyProps(),
    'Add Place': props{ place: Place }>(),
    'Update Place': props{ place: Place }>(),
    'Delete Place': props{ id: string }>(),
    'Cancel Place': emptyProps(),
    'Select Place': props{ place: Place }>(),
    'UnSelect Place': emptyProps(),
  },
});
وارد حالت تمام صفحه شوید

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

به روز رسانی روش لغو در PlacesComponent و ارسال کنید cancelPlace عمل.

 cancel() { 
    this.#store.dispatch(PlacesPageActions.cancelPlace());
  }
وارد حالت تمام صفحه شوید

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

مرحله آخر باز کردن است place.effect.ts، اضافه کردن returnHomeEffects اثر، تزریق روتر، و گوش دادن به cancelPlace عمل. استفاده کنید router.navigate برای تغییر مسیر زمانی که اقدام ارسال می شود.

export const returnHomeEffect$ = createEffect(
  (actions$ = inject(Actions), router = inject(Router)) => {
    return actions$.pipe(
      ofType(PlacesPageActions.cancelPlace),
      tap(() => router.navigate(['/places'])),
    );
  },
  {
    dispatch: false,
    functional: true,
  },
);
وارد حالت تمام صفحه شوید

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

در نهایت، آخرین مرحله به روز رسانی کارت مکان برای ارسال اکشن SelectPlace و استفاده از a است routerLink.

انجام شده! ما آن را انجام دادیم! ما روتر را حذف کردیم و وابستگی های مسیر را فعال کردیم، پارامتر URL را همگام نگه داشتیم و آن را با انتخابگرهای روتر ترکیب کردیم.

خلاصه

من یاد گرفتم که چگونه با استفاده از پارامترهای URL با NgRx Router Store در Angular، حالت را مدیریت کنم. من همچنین NgRx را با Angular Router برای کنترل وضعیت و ناوبری ادغام کردم و اجزای خود را تمیز نگه داشتم. این رویکرد به مدیریت بهتر حالت کمک می کند و با انتخابگرهای مسیریاب ترکیب می شود تا داده های روتر را به راحتی بخواند.

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

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

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

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