پارامترهای 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 برای کنترل وضعیت و ناوبری ادغام کردم و اجزای خود را تمیز نگه داشتم. این رویکرد به مدیریت بهتر حالت کمک می کند و با انتخابگرهای مسیریاب ترکیب می شود تا داده های روتر را به راحتی بخواند.