ساخت وبلاگ عکس بدون سرور پویا با Angular & Google Sheets – 1: حل مشکلات ناوبری روتر زاویه ای
مقدمه()؛
سلام توسعه دهندگان!
آیا به دنبال ایجاد یک وبلاگ عکس پویا بدون دردسر یک باطن سنتی هستید؟ در این سری از مقالات، من تجربه خود را در ساخت وبلاگ عکس بدون سرور برای نمونه کارهای عکاسی همسرم با استفاده از Angular و Google Sheets به اشتراک خواهم گذاشت.
میزبانی در یک صفحه GitHub گزینه خوبی برای یک سایت ثابت است، اما برای یک سایت پویا، به یک Backend نیاز دارید. اینجاست که Google Sheets بهعنوان یک پایگاه داده و یک فرم Google برای ایجاد ورودیهای وبلاگ جدید با عکسها مفید است.
در این مجموعه، من شما را در مسیر ساخت این وبلاگ عکس قرار میدهم و برخی از چالشهایی که در این مسیر با آنها مواجه شدم و همچنین نحوه حل آنها را به اشتراک میگذارم.
در این بخش اول، در مورد چگونگی حل مشکلات ناوبری روتر Angular بحث خواهم کرد.
مشکل()؛
همانطور که ما به ساختن “ESHARTISIC” وبلاگ عکس، بیایید نگاهی دقیق تر به نوار پیمایش بیندازیم.
(واقعیت جالب: همسرم این نام را پیدا کردESHARTISIC” برای وبلاگ عکس او، که ترکیبی از نام او، ESHA، و کلمه ARTISTIC است. فکر می کنم نام بسیار جالبی است، نه؟)
همانطور که در تصویر می بینید، نوار ناوبری از چهار دسته تشکیل شده است: Art
، Design
، Photography
، و Craft
، همه آنها با استفاده از یک مؤلفه در Angular ارائه می شوند. علاوه بر این، صفحه درباره در یک جزء متفاوت ارائه می شود.
برای رسیدن به این هدف، باید مسیرها را در روتر Angular تعریف کنیم. مسیر برای چهار دسته خواهد بود list/:category
، جایی که :category
می تواند باشد art
، design
، photography
، یا craft
. این مسیر از یک مؤلفه برای ارائه دستههای مختلف استفاده میکند. مسیر برای صفحه درباره خواهد بود about
.
هنگامی که کاربر روی هر یک از پیوندهای چهار دسته در نوار پیمایش کلیک می کند، صفحه مربوطه باید با لیستی از پست ها ارائه شود. برای دریافت لیست پست ها، فقط باید با شماره تماس بگیرید getPostList()
زمانی که کامپوننت شروع به بارگذاری می کند یک بار روش را انجام دهید.
بنابراین این دو چیزی است که می خواهم به آن برسم.
TheProbableSolution();
در انگولار، ngOnInit()
یک روش قلاب چرخه حیات است که تنها یک بار زمانی که یک جزء مقدار دهی اولیه می شود فراخوانی می شود.
-> در مورد من می توانم تماس بگیرم getPostList()
داخل ngOnInit()
مثل این:
import { ActivatedRoute } from '@angular/router';
export class ListComponent implements OnInit {
category: string;
constructor(private route: ActivatedRoute) { }
ngOnInit() {
this.category = this.route.snapshot.paramMap.get('category');
this.getPostList(this.category);
}
private getPostList(category: string) {
// fetch posts for the current category
}
}
این کد مقدار پارامتر دسته فعلی را از URL بازیابی می کند و از آن برای واکشی پست های آن دسته با استفاده از getPostList()
زمانی که کامپوننت بارگذاری می شود، روش را فقط یک بار انجام دهید.
اگر در حال حاضر در صفحه هنر هستم و روی یکی از چهار پیوند موجود در نوار پیمایش کلیک می کنم، ngOnInit()
متد دوباره فراخوانی نخواهد شد. این به این دلیل است که من قبلاً روی این مؤلفه و ngOnInit()
قبلا فراخوانی شده است. در عوض، کاری که باید انجام دهم این است که تغییری در پارامتر :category روتر شناسایی کنم.
-> برای تشخیص تغییرات در پارامتر :category روتر، می توانم از آن استفاده کنم ngDoCheck()
قلاب چرخه حیات در Angular. این روش هر بار که کامپوننت برای تغییرات بررسی می شود، فراخوانی می شود که آن را به مکانی مناسب برای تشخیص تغییرات پارامتر روتر تبدیل می کند.
در اینجا یک نمونه از پیاده سازی است:
import { Component, OnInit, DoCheck } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-list',
templateUrl: './list.component.html',
styleUrls: ['./list.component.css']
})
export class ListComponent implements OnInit, DoCheck {
category: string;
constructor(private route: ActivatedRoute) { }
ngOnInit() {
this.category = this.route.snapshot.paramMap.get('category');
this.getPosts(this.category);
}
ngDoCheck() {
const newCategory = this.route.snapshot.paramMap.get('category');
if (newCategory !== this.category) {
this.category = newCategory;
this.getPostList(this.category);
}
}
private getPostList(category: string) {
// fetch posts for the current category
}
}
حال اگر دسته جدید با دسته فعلی متفاوت باشد، ویژگی دسته به روز می شود و getPostList()
روش با دسته جدید فراخوانی می شود. این تضمین می کند که کامپوننت همیشه محتوای صحیح را بر اساس دسته فعلی در روتر نمایش می دهد.
حال بیایید روش دیگری را برای رسیدن به نتیجه مشابه بررسی کنیم، که استفاده از آن است ActivatedRoute
.
-> برای شروع مجدد و ngOnInit()
روشی که دوباره فراخوانی می شود، می توانم در آن مشترک شوم ActivatedRoute
پارامتر و تشخیص تغییرات در پارامتر مسیر. من میتونم استفاده کنم paramMap
قابل مشاهده برای تشخیص تغییرات و شروع مجدد جزء در صورت نیاز.
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-list',
templateUrl: './list.component.html',
styleUrls: ['./list.component.css']
})
export class ListComponent implements OnInit {
category: string;
constructor(private route: ActivatedRoute) { }
ngOnInit() {
this.route.paramMap.subscribe(params => {
this.category = params.get('category');
this.getPostList(this.category);
});
}
private getPostList(category: string) {
// fetch posts for the current category
}
}
در ngOnInit()
با استفاده از روش، ما در حال اشتراک تغییرات در پارامتر مسیر هستیم this.route.paramMap.subscribe()
. هر زمان که پارامتر دسته تغییر کند، متد subscribe فراخوانی میشود و سپس میتوانیم ویژگی دسته را بهروزرسانی کرده و آن را فراخوانی کنیم getPostList()
روشی برای واکشی پست های دسته جدید.
از نظر تشخیص تغییرات در پارامترهای مسیر، با استفاده از ActivatedRoute
به طور کلی رویکرد ترجیحی در Angular است زیرا راه کارآمدتر و قابل اعتمادتری برای دسترسی به پارامترهای مسیر فعلی ارائه می دهد.
پس از پیاده سازی راه حل با استفاده از ActivatedRoute
، هنگام پیمایش از صفحه هنری به صفحات طراحی، عکاسی یا صنایع دستی، getPostList()
متد دوباره فراخوانی خواهد شد. با این حال، اگر کاربر در حالی که قبلاً در صفحه هنری است، روی پیوند هنری کلیک کند، getPostList()
متد دوباره فراخوانی نخواهد شد زیرا پارامتر تغییر نکرده است. آره زندگی پر از گل رز نیست!
بیایید ببینیم چگونه این مشکل را حل کردم.
راه حل()؛
برای دستیابی به هدف خود در بارگذاری مجدد مؤلفه لیست و واکشی پست ها هر زمان که بر روی هر یک از چهار پیوند نوار ناوبری کلیک می شود، ابتدا باید یک مسیر ساختگی ایجاد کنیم که به یک مؤلفه ساختگی نگاشت شده است.
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AboutPageComponent } from './about-page/about-page.component';
import { DummyComponent } from './components/dummy/dummy.component';
import { ListPageComponent } from './list-page/list-page.component';
import { MainPageComponent } from './main-page/main-page.component';
const routes: Routes = [
{ path: 'post/:category', component: ListPageComponent },
{ path: 'post/:category/:id', component: MainPageComponent },
{ path: 'about', component: AboutPageComponent },
{ path: '', redirectTo: '/post/art', pathMatch: 'full' },
{ path: 'dummy', component: DummyComponent}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
ما یک مسیر ساختگی نگاشت شده به یک جزء ساختگی اضافه کرده ایم، مرحله بعدی فراخوانی یک است onClick()
روش هنگام کلیک بر روی پیوند نوار ناوبری. این روش ابتدا به مسیر ساختگی و سپس بلافاصله به مسیر نوار ناوبری کلیک شده هدایت می شود. در حالی که ما می توانیم اضافه کنیم onClick()
روش موجود در فایل .ts کامپوننت نوار ناوبری، ممکن است در سایر مؤلفه ها نیز مورد نیاز باشد. برای رفع این مشکل، سرویسی با a ایجاد کرده ایم navigateToRoute()
پیاده سازی متد، به ما این امکان را می دهد که این متد را در هر زمان و هر کجا که لازم باشد فراخوانی کنیم.
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
@Injectable({
providedIn: 'root'
})
export class RouteHandlerService {
constructor(private router: Router) { }
navigateToRoute(clickedRoute: string) {
const currentRouteUrl = clickedRoute;
this.router.navigateByUrl('/dummy', { skipLocationChange: true }).then(() => {
this.router.navigate([currentRouteUrl]);
});
}
}
را navigateToRoute()
روش در تعریف شده است RouteHandlerService
. یک پارامتر فراخوانی می گیرد clickedRoute
، مسیری است که کاربر در نوار ناوبری روی آن کلیک کرده است.
درون navigateToRoute()
روش، URL مسیر فعلی روی مسیر کلیک شده تنظیم می شود. سپس navigateByUrl()
روش سرویس روتر با یک مسیر ساختگی و گزینه فراخوانی می شود skipLocationChange
تنظیم کنید true
. این بدون تغییر URL در نوار آدرس مرورگر به مسیر ساختگی هدایت می شود.
هنگامی که ناوبری به مسیر ساختگی کامل شد، router.navigate
روش برای حرکت به مسیر کلیک شده فراخوانی می شود. این نشانی اینترنتی را در نوار آدرس مرورگر تغییر میدهد و مؤلفه مربوطه را بارگیری میکند.
به طور کلی، این سرویس راهی برای پیمایش به یک مسیر جدید در یک برنامه Angular بدون اینکه نوار آدرس مرورگر مسیر میانی را نمایش دهد، ارائه میکند. این می تواند زمانی مفید باشد که می خواهید وقتی کاربر روی یک پیوند کلیک می کند یک مؤلفه را دوباره بارگیری کنید، اما نمی خواهید URL را در نوار آدرس مرورگر تغییر دهید.
سپس یک را ایجاد کردم onClickMenuItem()
در کامپوننت نوار ناوبری استفاده کنید و آن را با پیوندهای نوار ناوبری پیوند دهید.
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { RouteHandlerService } from 'src/app/services/route-handler.service';
import { AppConstants } from 'src/utils/appConstants';
import { Page } from 'src/utils/models/page';
@Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.scss']
})
export class NavbarComponent {
pages: Page[] = AppConstants.pages;
constructor(private router: Router, private routeHandlerService: RouteHandlerService) { }
onClickMenuItem(clickedRoute: string) {
this.routeHandlerService.navigateToRoute(clickedRoute);
}
}
<a routerLink="{{page.getRouterLink()}}" (click)="onClickMenuItem(page.getRouterLink())" class="menu-text nav-link">{{page.getTitle()}}</a>
هر زمان که روی پیوند نوار ناوبری کلیک شود، onClickMenuItem()
متد فراخوانی می شود که به نوبه خود the را فراخوانی می کند navigateRoute()
روش. در نتیجه، هر بار که روی پیوند نوار ناوبری کلیک می کنیم، مؤلفه لیست دوباره بارگذاری می شود.
سپس در جزء لیست من فقط تماس گرفتم getPostList()
که در ngOnInit()
روش.
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { PostType } from 'src/utils/enums/postType';
import { PostService } from '../services/post.service';
@Component({
selector: 'app-list-page',
templateUrl: './list-page.component.html',
styleUrls: ['./list-page.component.scss']
})
export class ListPageComponent implements OnInit {
constructor(
private postService: PostService,
private router: Router
) { }
ngOnInit() {
this.getPostList(this.getPostType(this.router.url));
}
async getPostList(postType: PostType) {
await this.postService.getPosts(postType);
}
private getPostType(routeUrl: string): PostType {
switch (routeUrl) {
case "list/design":
return PostType.DESIGN
case "list/photography":
return PostType.PHOTOGRAPHY
case "list/craft":
return PostType.CRAFT
default:
return PostType.ART
}
}
}
نتیجه()؛
قطعا! در پایان، این رویکردی است که من برای حل مشکل بارگیری مجدد مؤلفه لیست و واکشی آخرین پست ها در هر بار کلیک روی پیوند نوار ناوبری انجام دادم. با این حال، ممکن است راه حل ها یا رویکردهای دیگری وجود داشته باشد که بتواند به همان نتیجه یا حتی نتایج بهتری دست یابد. اگر کسی راه حل یا رویکرد بهتری دارد، من او را تشویق می کنم که آن را با جامعه به اشتراک بگذارد و به بهبود شیوه های توسعه ادامه دهد.