به نظر شما چه زمانی مناسب استفاده از vs2015 به همراه angularjs 2 در پروژهها میباشد؟
در بوت استرپ برای نمایش اعلانی به کاربر، از کلاس alert میتوان استفاده کرد. برای نمایش این اعلان کافی است محتوای خود را درون یک div با کلاس alert قرار دهیم:
کلاس فوق نیز جهت نگهداری اسامی کلاسهای alert، مورد استفاده قرار میگیرد. قدم بعدی، استفاده از کلاسهای فوق و انتقال alerts توسط TempData به داخل viewها میباشد. برای جلوگیری از زیاد شدن حجم کدهای تکراری داخل کنترلرها و همچنین به عنوان یک Best practice، یک کنترلر Base را برای اینکار تعریف میکنیم و متدهای موردنیاز را داخل آن مینویسیم:
از متدهایی که به صورت عمومی تعریف شدهاند جهت ارسال پیام به view استفاده میکنیم. متد AddAlert نیز جهت ایجاد لیستی از پیامها(Alert) مورداستفاده قرار میگیرد؛ زیرا ممکن است بخواهید هم زمان از چندین متد عمومی فوق استفاده کنید، یعنی چندین پیام را به کاربر نمایش دهید.
view :
اگر کد فوق را تست کنید خواهید دید که در خروجی تنها اطلاعات داخل TempData نمایش داده میشود.
<div class="alert"> نمایش اعلانات </div>
تعدادی کلاس دیگر نیز جهت استفاده از رنگهای مختلف نیز توسط بوت استرپ ارائه شده است:
همچنین اگر مایل بودید میتوانید با افزودن یک دکمه با کلاس close و ویژگی data-dismiss مساوی alert، امکان بستن پیام را در اختیار کاربر قرار دهید:
<div class="alert alert-warning alert-dismissable"> <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button> نمایش اعلان </div>
در ادامه قصد داریم این پیام را بعد از ثبت اطلاعات، به کاربر نمایش دهیم. یعنی در داخل کد، امکان صدا زدن این نوع پیامها را داشته باشیم.
ابتدا کلاسهای زیر را تعریف میکنیم:
کلاس Alert
public class Alert { public const string TempDataKey = "TempDataAlerts"; public string AlertStyle { get; set; } public string Message { get; set; } public bool Dismissable { get; set; } }
در کلاس فوق خصوصیت یک alert را تعریف کردهایم (از خاصیت TempDataKey جهت پاس دادن alertها به view استفاده میکنیم).
کلاس AlertStyles
public class AlertStyles { public const string Success = "success"; public const string Information = "info"; public const string Warning = "warning"; public const string Danger = "danger"; }
public class BaseController : Controller { public void Success(string message, bool dismissable = false) { AddAlert(AlertStyles.Success, message, dismissable); } public void Information(string message, bool dismissable = false) { AddAlert(AlertStyles.Information, message, dismissable); } public void Warning(string message, bool dismissable = false) { AddAlert(AlertStyles.Warning, message, dismissable); } public void Danger(string message, bool dismissable = false) { AddAlert(AlertStyles.Danger, message, dismissable); } private void AddAlert(string alertStyle, string message, bool dismissable) { var alerts = TempData.ContainsKey(Alert.TempDataKey) ? (List<Alert>)TempData[Alert.TempDataKey] : new List<Alert>(); alerts.Add(new Alert { AlertStyle = alertStyle, Message = message, Dismissable = dismissable }); TempData[Alert.TempDataKey] = alerts; } }
نکته: در کد فوق از TempData جهت پاس دادن شیء alerts استفاده کردهایم. TempData به صورت short-lived عمل میکند به دو دلیل: 1- بلافاصله بعد از خوانده شدن، حذف خواهد شد. 2- پس از پایان درخواست از بین خواهد رفت. از TempData جهت پاس دادن دادهها از درخواست فعلی به درخواست بعدی (redirect از یک صفحه به صفحه دیگر) استفاده میشود. یعنی در زمان redirect سعی میکند دادههای بین redirectها را در خود نگه دارد. اگر از ViewBag و ViewData استفاده میکردیم دادههای داخل آنها بلافاصله بعد از redirect شدن null میشدند.
به طور مثال اکشن متد زیر را در نظر بگیرید:
public ActionResult Index() { var userInfo = new { Name = "Sirwan", LastName = "Afifi", }; ViewData["User"] = userInfo; ViewBag.User = userInfo; TempData["User"] = userInfo; return RedirectToAction("About"); }
@{ ViewBag.Title = "About"; } <h1>Tempdata</h1><p>@TempData["User"]</p> <h1>ViewData</h1><p>@ViewData["User"]</p> <h1>ViewBag</h1><p>@ViewBag.User</p>
معمولاً برای ارسال دادههای خطاها از TempData استفاده میشود.
اکنون در هر کنترلری که میخواهید پیامی را به صورت alert، پس از ثبت اطلاعات به کاربر نمایش دهید، باید از کنترلر BaseController ارثبری کنید:
public class NewsController : BaseController { readonly INewsService _newsService; readonly IUnitOfWork _uow; public NewsController(INewsService newsService, IUnitOfWork uow) { _newsService = newsService; _uow = uow; } [HttpPost] [ValidateAntiForgeryToken] [ValidateInput(false)] public ActionResult Create(News news) { if (ModelState.IsValid) { _newsService.AddNews(news); _uow.SaveChanges(); Success(string.Format("خبر با عنوان <b>{0}</b> با موفقیت ذخیره گردید!", news.Title), true); return RedirectToAction("Index"); } Danger("خطا در هنگام ثبت اطلاعات "); return View(news); } [HttpPost] public ActionResult Delete(int id) { _newsService.DeleteNewsById(id); _uow.SaveChanges(); Danger("اطلاعات مورد نظر با موفقیت حذف گردید!", true); return RedirectToAction("Index"); } }
نمایش پیامها
برای نمایش پیامها یک partial view با نام _Alerts در مسیر Views\Shared ایجاد میکنیم:
@{ var alerts = TempData.ContainsKey(Alert.TempDataKey) ? (List<Alert>)TempData[Alert.TempDataKey] : new List<Alert>(); if (alerts.Any()) { <hr /> } foreach (var alert in alerts) { var dismissableClass = alert.Dismissable ? "alert-dismissable" : null; <div class="alert alert-@alert.AlertStyle @dismissableClass"> @if (alert.Dismissable) { <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button> } @Html.Raw(alert.Message) </div> } }
در کد فوق، ابتدا شیء alerts را از TempData دریافت کردهایم و توسط یک حلقه foreach، داخل آن به ازای هر آیتم، آن را پیمایش میکنیم و در نهایت کد html متناظر با هر alert را در خروجی نمایش میدهیم.
اکنون جهت استفاده از partial view فوق در جایی که میخواهید پیام نمایش داده شود partial view فوق را فراخوانی کنید (به عنوان مثال داخل فایل Layout):
<div> @{ Html.RenderPartial("_Alerts"); } @RenderBody() </div>
اشتراکها
2.Visual Studio 2019 RC منتشر شد
Top Issues Fixed in Visual Studio 2019 RC.2
- Find in files "Locating next match" UI is annoying.
- Find files keeps defaulting to current document.
- Quick references freezes VS 2019 RC.
- PackageId:MsSqlCmdLnUtils;PackageAction:Install;ReturnCode:1603;.
- Error List does not show errors because it is scoped to "Current Document".
- Cannot use conditional breakpoint on PropertyInfo.Name value.
- Visual Studio crashes when parsing macros at the end of a file.
- Search in Visual Studio 2019 is very slow.
- Visual Studio hangs when starting debugging.
- vdproj not supported in Visual Studio 2019 RC.
- Visual Studio installer welcome image contains offensive element for Chinese.
- VSIX Extension pre-req has been removed in Visual Studio 2019 RC breaking extension compatibility.
- Create Project from Start Screen Ignores Selected Project Folder.
- cpp properties is dialog does not show up when using Project menu item or select "Manage configuration" drop down menu.
- Visual Studio 2019 Build Tools - developer prompt title says "Developer Command Prompt for Visual Studio 2017".
- German tranlation regarding the Feedback Tool: Help > Send Feedback > Report a Problem.
- 自动完成功能,如果双击候选项,会丢失输入焦点,需要单机编辑器才能继续输入-AutoComplete function, if you double-click the candidate, will lose the input focus, need a stand-alone editor to continue to enter.
- Context menus are sometimes placed on the wrong monitor in a multiple monitor configuration.
- Visual Studio 2019 conflict with QQ Pinyin.
- Cannot drag maximized Visual Studio window.
- Fixed slow reload of multiple C# and Visual Basic projects.
- When IntelliSense is present, when a user types Shift + Enter, the active selection will be completed and a new line inserted.
- Fixed a PMA issue where editor tooltips and light bulb doesn't render properly.
- Notifications about crashes caused by extensions now show up again.
- Notifications about performance of Visual Studio have been secured against tampering.
- Fixed an issue with toolbar rendering when dragged across displays.
- Fixed an issue with Tools Options rendering when running with per-monitor awareness enabled.
- Various DpiHelper classes has been deprecated (extensibility).
- Fixed splash screen scaling to better match the primary monitor scale factor.
- Fixed an issue in settings import where warnings/errors were not always reported correctly.
- Fixed an issue where Tools Options reported software rendering regardless of rendering tier.
- Fixed an issue where the name of the open folder was not displayed in the title bar region.
- Fixed an issue with find in files positioning when per-monitor awareness is enabled.
- Fixed an issue with dock adorner rendering when per-monitor awareness is enabled.
Blazor، چارچوبی است ارائه شده توسط مایکروسافت که به ما اجازه میدهد برنامههای تعاملی وب را با CSharp و بدون JavaScript بنویسیم. Blazor از اساس، Component based بوده و در برنامههایی که Backend نیز با CSharp نوشته شده باشد ( مثلا با ASP.NET Core) امکان به اشتراک گذاری کد بین کلاینت و سرور را نیز فراهم میکند. معماری Blazor معماریای مدرن است که در دل خود از امکانات CSharp نیز به خوبی بهره برده است تا بتوان پروژه را بهصورتی که قابلیت نگهداری بالایی داشته باشد، توسعه داد. Blazor در ذات معماری خود امکان نوشتن برنامههای Native موبایل را نیز میدهد و برای اثبات این مهم چندین دمو برای Proof of concept ارائه شده است؛ اما تا به امروز امکانی که به صورت Production ready باشد، ارائه نشده است.
Blazor به دو شکل کار میکند. Blazor Server و Blazor Client
در Blazor Client با استفاده از پشتیبانی نزدیک به 90% ای مرورگرها از Web Assembly که اجازه اجرای کدهای غیر JavaScript ای را در مرورگر میدهد، ابتدا DLLها به همراه HTML-CSS و عکسها و ... دانلود شده و برنامه به صورت Single Page App کار میکند. در این مدل شما میتوانید از تکنیک Pre rendering یا SSR نیز استفاده کنید تا تجربه کاربری بهتری را نیز ارائه دهید و یا در بحث SEO بهتر عمل کنید.
Blazor در سمت کلاینت از NET Standard 2.1 پشتیبانی میکند که عملا به شما اجازه میدهد بازهی گستردهای از Nuget Packageها را استفاده کنید.
البته Blazor Client به صورت Preview ارائه شده است و "فعلا" دو مشکل را دارد:
۱- امکان دیباگ خوبی ندارد.
۲- در عمل باید فایلهای اجرایی برنامه به صورت wasm یا web assembly دانلود شوند که در این حالت سرعتی بسیار بالا و بیش از جاوااسکریپت خواهند داشت؛ اما تا این لحظه این فایلها به صورت DLL دانلود میشوند و در سمت مرورگر "تفسیر" میشوند که باعث میشود سرعت گاه تا 70 برابر کمتر شود.
البته این دو مشکل در زمان ارائه نسخه NET 5. حل خواهند شد و در پروژه نسخه نهایی خود این دو مشکل را نخواهد داشت.
Blazor مدل اجرای دومی نیز دارد که به آن Blazor Server نیز میگوییم. در این مدل کدها تماما سمت سرور اجرا میشوند و تعاملات UI به صورت Web Socket به کلاینت منتقل و یا از آن دریافت میشوند که در این مدل، امکان Debug بدون کوچکترین مشکلی کار میکند و مشکلی از بابت کندی دانلود و اجرای DLLها ندارد. فقط چون کدها تماما سمت سرور اجرا میشوند، در زمانیکه ارتباط شبکهای، مناسب نباشد، میتوانیم در سناریوهای مختلف، شاهد لگ و کندی باشیم.
آیندهی Blazor در مدل اول آن تعریف شدهاست و در گذر زمان برای مدل Blazor Server، نمیتوان آیندهی زیادی را متصور بود. اما نکتهی مهم و جالب اینجاست که کد پروژه در هر دو مدل یکی است و فقط Configuration این دو از هم متفاوت است. پس اگر علاقمند به شروع به استفاده از Blazor هستید، یک راه این است که با مدل Blazor Server شروع و بعدا با تغییر Configuration، به Blazor Client سوئیچ کنید. البته در این میان باید به نکاتی دقت کنید:
۱- Blazor Server از NET Core 3.1. پشتیبانی میکند و Blazor Client از NET Standard 2.1. در مواقعی خاص ممکن است یک کلاس یا یک متد در NET Core 3.1. باشد و در NET Standard 2.1. نباشد.
۲- در Blazor Server شما حتی میتوانید با Entity Framework Core مثلا به Sql Server وصل شوید؛ ولی طبیعتا Browser امکان اتصال مستقیم به Sql Server را در مدل Client به شما نخواهد داد.
این موارد باعث میشود که اگر پروژه را با Blazor Server شروع کنید، شاید در آن کارهایی را انجام دهید که در BlazorServer کار میکنند، ولی در BlazorClient کار نمیکنند و این باعث شود بعدا که نسخه نهایی و کامل BlazorClient آماده شد، شاید نتوانید به آن، به صورت ساده و آسانی سوئیچ کنید.
ایده آل این است که پروژهای داشته باشید که به سادگی عوض کردن یک کلمه، بین این دو مدل سوئیچ کنید و بر صحت عملکرد پروژه در هر دو حالت نظارت داشته باشید. برای رسیدن به این مهم، پروژهای را ساختهام به نام BlazorDualMode که عمدهی بررسیها را به صورت اتوماتیک انجام میدهد و عملا تضمینی بر مهاجرت آسان شما از BlazorServer به BlazorClient در آینده خواهد بود
اگر در نت جستجو کنید، پروژههایی با این اسم را خواهید دید؛ اما این پروژه دو مزیت مهم را دارد که الباقی فاقد آن هستند:
۱- مدل Blazor Client آن دارای تکنیک Pre rendering یا SSR است.
۲- پروژه Api آن، از پروژهی Blazor آن جداست. اگر پروژهی Api را پروژهای بدانیم که به همراه Model، Data و ... امکان دسترسی به DbContext و دیتابیس را دارد، جدا بودن پروژهی Blazor باعث میشود که حتی در مدل Server آن نیز مجبور باشیم دیتا را از Api به صورت Rest Api call بگیریم و به واسطه پروژه Shared مابین Api و Blazor نهایت Dtoها و سایر کدهای مشترک را ببینیم.
البته در پروژه DualMode فقط پروژه Api درست شدهاست و ما کاری با جزئیات آن، اینکه مثلا CQRS میخواهیم یا نه، آیا میخواهیم لایهای کار کنیم و ... نداریم و فقط روی موارد مرتبط با Blazor متمرکز شدهایم.
در پروژه یک فایل داریم به نام Directory.build.props. زمانیکه چنین فایلی در فولدری قرار بگیرد، تمامی موارد نوشته شده در آن به صورت ضمنی در تمامی فایلهای csproj زیر مجموعه آن اعمال میشود.
در این فایل داریم:
<Project> <PropertyGroup> <BlazorMode>Client</BlazorMode> <DefineConstants Condition=" '$(BlazorMode)' == 'Client' ">$(DefineConstants);BlazorClient</DefineConstants> <DefineConstants Condition=" '$(BlazorMode)' == 'Server' ">$(DefineConstants);BlazorServer</DefineConstants> <LangVersion>8.0</LangVersion> </PropertyGroup> </Project>
در ادامه Define Constant کردهایم که اجازه میدهد کدهای CSharp دخیل در Configuration پروژه Blazor را شرطی کنیم. برای مثال بنویسیم:
#if BlazorClient ... #elif BlazorServer ... #endif
نسخه CSharp تمامی پروژهها نیز 8.0 قرار داده شده است.
پروژهی BlazorDualMode.Api آن پروژهای است که Api Controllerها در آن قرار میگیرند و همچنین در حالت Blazor Client، پروژه را برای مرورگرها ارائه یا Serve میکند. به واقع در این مدل، درخواستی که به هیچ Api Controller ای نرسد، به Blazor تحویل میشود. Blazor نیز ابتدا به دنبال Component ای میگردد که برای Route مربوطه نوشته شده باشد و آن را اجرا میکند و سپس Response آماده، به کلاینت ارسال میشود. در ادامه، مرورگر فایلهای DLL را دانلود و برنامه به صورت Single Page App به کار خود ادامه میدهد.
پروژه BlazorDualMode.Shared پروژهای است که کد مشترک بین Api و Blazor در آن قرار میگیرد. برای مثال میتوانید Dtoها را در این قسمت قرار دهید.
پروژه BlazorDualMode.Web پروژهای است که در آن Componentهای Blazor قرار دارند. در حالت Server این پروژه نیز قابلیت اجرا مییابد و باید با امکانات Visual Studio یا IDE دلخواه خود پروژه Web و Api را به صورت همزمان اجرا کنید تا به درستی کار کند.
فایلهای Program.cs، Startup.cs و همچنین خود csprojها و در نهایت فایل Host.cshtml، نهایت تفاوت این دو حالت بوده و کدهای بیزینسی پروژه و حتی Componentها و Api Controllerها در هر دو حالت یکی هستند. Configuration با شرط if server یا if client هندل شدهاند و درک جزئیات تنظیمات مربوطه نیاز به تسلط بر روی خود Blazor را دارد که از موضوع این پست خارج است؛ ولی در صورت داشتن هر گونه سوالی، میتوانید از قسمت پرسش و پاسخ همین سایت استفاده کنید.
در این بخش قصد داریم تا به چگونگی آپلود فایل در AngularJs بپردازیم. پلاگینهای فراوانی جهت آپلود فایل وجود دارند، از جمله ng-file-Upload ، angular-file-upload و jquery-file-upload. البته نمونهی ذکر شدهی آخر، یکی از پلاگینهای معروف JQuery است که با AngularJs هم سازگار شده است.
پس از اینکه دو کامپوننت فوق را نصب نمودید، دو فایل اسکریپت دریافت شده را به فایل main html اضافه نمایید. پس آن شروع به نوشتن یک مثال ساده میکنیم.
تکه کد فوق مربوط به تگ آپلود فایل است که به توضیح آن خواهیم پرداخت. ngf-select Attribute دایرکتیوی برای تبدیل فایل آپلود، به ng-file-upload است. شما به طور کلی به دو صورت قادر هستید که این کار را انجام دهید. مورد اول ngf-select بود و مورد بعدی ngf-drop است. ngf-drop به گونهای دیگر نوشته میشود و کد زیر یک مورد استفادهی از ngf-drop میباشد.
همانطور که ملاحظه میکنید در این نوع فراخوانی، یک تگ div به یک file select مبدل شده که در مورد آن صحبت خواهیم نمود. همانطور که مشاهده میکنید، شما قادرید تا با استفاده از Attribute ایی با نام accept، فایلهای ارسالی را از لحاظ محتوایی فیلتر کنید. در کد فوق image/* این مفهوم را میرساند که شما قادرید فایلهای تصویری و با هر فرمتی را به سرور ارسال نمایید. اما اگر آن را به صورت image/jpg بنویسیم این عملیات تنها به تصاویر با فرمت jpeg منتهی میشود. مورد بعدی محدودیت حجمی تصویر است. شما با ngf-max-size و مقداری که درون آن قرار میدهید، قادرید تا محدودیت حجمی را بر روی فایلهای ارسالی به سرور اعمال نمایید. اگر با نوشتار form validationها در Angular آشنا باشید، به سادگی قادرید تا خطاهایی را که کاربر ممکن است در حین آپلود انجام دهد، به او گوشزد کنید. به کد زیر توجه کنید:
همانطور که مشاهده میکنید syntax نمایش خطاها دقیقا مانند نمایش خطاها در validation فرمهای انگیولاری است.
تمام عملیات در قالب یک فراخوانندهی ng-click از سوی view آغاز شدهاست. با این رخداد، متد uploadPic که همراه آن فایل attach شده نیز به عنوان پارامتر ارسال میگردد، فرخوانی میشود و کلیهی کارها طی عملیات post، توسط وابستگی Upload برای سرور ارسال میگردد. همانند http$ درون بلاک then سه متد Success ، Error و progress پیادهسازی شده است. البته اگر به فرایند مقداردهی Url و data توجه کنید میبینید که فایلها نیز درون data قرار گرفتهاند. شما برای ارسال فایلها میتوانید از وابستگی upload$ نیز استفاده کنید که مثالی هم در این زمینه در mono-software.com پیادهسازی شده است که از ng-file-upload و در سمت سرور، از ASP.NET Web API استفاده میکند. در این مثال چندین فایل به صورت غیر همزمان برای سرور ارسال میگردند.
ما قصد داریم تا در این مجموعه از ng-file-upload که توسط Danial Farid توسعه داده شده است، استفاده نماییم. این پلاگین شامل یک دایرکتیو کم حجم برای مدیریت آپلود فایل سمت کلاینت است و با تمامی تکنولوژیهای سمت سرور سازگار میباشد. صفحهی دمو پلاگین، شامل برخی از امکانات ذکر شده میباشد که قرار است به توسعه و بحث پیرامون آن بپردازیم. این مجموعه دارای اسناد معتبری است که میتوانید از آن برای توسعهی هر گونه آپلود فایل استفاده نمایید. نسخهی فعلی، نسخهی منتشر شدهی 3.0 است. نسخههای قبلی این پلاگین نیز در سایت GitHub موجود است.
ویژگی ها
در بخش زیر ویژگیهای این افزونه که در اسناد آن نیز موجود است، شرح داده شده است:
- امکان ایجاد progress bar و همچنین قابلیت لغو آپلود.
- Drag & Drop
- امکان paste کردن از Clipboard و قابلیت drag & drop از browser
- امکان resize کردن تصویر قبل از آپلود شدن
- امکان تغییر دادن orientation تصویر پیش از ارسال
- قابلیت resume و pause/play در آپلود
- امکان فیلتر کردن type، size و طول و عرض تصاویر
- قابلیت تبدیل به Flash shim در صورت عدم پشتیبانی html5 توسط مرورگر
نصب و پیاده سازی
برای استفادهی از پلاگین file upload لازم است که دو مجموعهی زیر را توسط Nuget یا Bower دریافت نمایید. پلاگین اول مربوط به مرورگرهایی است که از html5 پشتیبانی نمیکنند.
bower install ng-file-upload-shim --save bower install ng-file-upload --save
این مثال در JSFIDDLE قرار داده شده است، به این صورت که در این مثال یک فایل آپلود ساده در قالب یک فرم به سرور ارسال میگردد. امکانات خیلی ساده نظیر نمایش فایل، پیش از آپلود نیز در این قسمت قرار داده شده است.
به عنوان اولین گام به کد زیر توجه کنید:
<input type="file" ngf-select ng-model="picFile" name="file" accept="image/*" ngf-max-size="2MB" required>
<div ngf-drop ng-model="picFile" ngf-pattern="image/*"> ... </div>
<i ng-show="myForm.file.$error.required">*required</i><br> <i ng-show="myForm.file.$error.maxSize"> File too large: max is 2M </i>
و اما اینکه چه کارهایی نیاز است تا در سمت Controller انجام دهیم. اول اینکه لازم است شما dependency ngFileUpload زیر را به app.module اضافه نمایید. حال اینکه پس از inject کردن این وابستگی، تنها کافیست که وابستگی Upload را به کنترلر تزریق نمایید. این وابستگی وظیفهی ارسال فایل به سرور را بر عهده دارد. به مثال زیر توجه نمایید:
app.controller('MyCtrl', ['$scope', 'Upload', '$timeout', function ($scope, Upload, $timeout) { // the method uploadPic called from Angular View $scope.uploadPic = function(file) { file.upload = Upload.upload({ url: 'https://angular-file-upload-cors-srv.appspot.com/upload', data: {file: file, otherData: $scope.otherData}, }); file.upload.then(function (response) { // waiting for response $timeout(function () { file.result = response.data; }); }, function (response) { //error if (response.status > 0) $scope.errorMsg = response.status + ': ' + response.data; }, function (evt) { //progress file.progress = Math.min(100, parseInt(100.0 * evt.loaded / evt.total)); console.log(file.progress); }); } }]);
توحه داشته باشید که شما برای uploadهای ساده، قادر هستید تا دایرکتیو خاص خود را نوشته و از آن استفاده نمایید. در msdn نمونهای از این کار، یعنی نوشتن دایرکتیو فایل آپلود، به طور کامل پیادهسازی شده است. اما مزیت ng-file-upload این است که این پلاگین، با بسیاری از پلاگینهای 3rd party دیگر مانند ng-image-crop به خوبی همگام شدهاست. مثالی از آن را میتوانید در اینجا مشاهده نمایید.
در این مقاله، هدف کلی، چگونگی فایل آپلود در AngularJs و پس از آن معرفی و بررسی ویژگیها و همچنین تشریح عملکرد ng-file-upload بود. این افزونه قابلیتهای فراوان دیگری نیز دارد که بنابر نیاز میتوانید در کدهای خود از آنها استفاده نمایید.
مسیرراهها
AngularJS
- به اشتراک گذاری دادهها بین کنترلرها در AngularJs
- آشنایی با Directiveها در AngularJs
- ارتباط بین Controller و Directive در AngularJs
- inject$ در AngularJs
- روش Controller as در AngularJs
- ارث بری کنترلرها در AngularJs
- پیاده سازی کنترلرهای Angular با استفاده از Typescript
- واکشی اطلاعات سرویس Web Api با استفاده از TypeScript و AngularJs
- تفاوت AngularJS با KnockoutJS
- آشنایی با Promises در جاوا اسکریپت
- بررسی angular.bootstrap
- خواندن اطلاعات از سرور و نمایش آن توسط Angular در ASP.NET MVC
- ساختار پروژههای Angular
- یک نکته درباره Angular routeProvider
- مسیریابی تو در تو در Angularjs با استفاده از UI-Router
- Lazy Loading در AngularJS
- بررسی خروجی IsAjaxRequest در درخواستهای http$ توسط AngularJS
- بررسی سرویسهای on$ و emit$ و broadcast$ در AngularJs
- آشنایی بیشتر با AngularJS Directive
- چگونگی استفاده از افزونه Isotope در AngularJS
- نمایش تاریخ شمسی توسط JavaScript در AngularJS
- توسعه سرویسهای Angular به روش OOP
- Angular Interceptors
- پیاده سازی Template تو در تو در AngularJS و ASP.NET MVC
- ارث بری prototype ای در جاوا اسکریپت به زبان خیلی ساده
- زیرنویس فارسی ویدئوهای مقدمات AngularJS - قسمت اول
- زیرنویس فارسی ویدئوهای مقدمات AngularJS - قسمت دوم
- زیرنویس فارسی ویدئوهای مقدمات AngularJS - قسمت سوم
- زیرنویس فارسی ویدئوهای مقدمات AngularJS - قسمت چهارم
- زیرنویس فارسی ویدئوهای مقدمات AngularJS - قسمت پنجم
- زیرنویس فارسی ویدئوهای مقدمات AngularJS - قسمت ششم (قسمت آخر)
مطالب
Vue CLI
تیم Vue یک ابزار را جهت scaffold سریع یک پروژه Vue، به صورت رسمی ارائه کردهاست. توسط این ابزار به صورت سریع میتوانیم ساختار یک پروژه استاندارد Vue را ایجاد کنیم.
چرا نیاز به Vue CLI داریم؟
- زیرا نیاز به build processهایی داریم که به ما امکان استفاده از ES6, SCSS و دیگر ویژگیهای عالی را خواهند داد.
- جهت ساخت و یکیسازی فایلهای تمپلیت
- بارگذاری نکردن تمامی فایلها به صورت یکجا در زمان Startup
- میتوانیم تسکهایی از قبیل Server-side rendering, code-splitting را انجام دهیم.
نصب Vue CLI
ابتدا مطمئن شوید که آخرین نگارش Node.js را نصب کردهاید. سپس جهت نصب Vue CLI، خط فرمان را گشوده و دستور زیر ذیل را صادر کنید:
npm install -g vue-cli
با اجرای فرمان فوق، ابزار CLI به صورت global و عمومی نصب خواهد شد. در ادامه میتوانیم با دستور vue list، لیستی از قالبهای رسمی را که توسط CLI قابل ایجاد هستند، مشاهده نمائید:
در اینجا ما از قالب webpack-simple استفاده خواهیم کرد. برای اینکار دستور زیر را جهت ایجاد یک پروژه بر اساس این قالب صادر کنید:
vue init webpack-simple dntVue
به این ترتیب در سریعترین زمان ممکن توانستیم یک برنامهی Vue را ایجاد کنیم:
در اینجا ساختار یک پروژه جدید Vue را مشاهده میکنید:
index.html: کار شروع و ارائه برنامه را انجام میدهد.
package.json: وابستگیهای npm برنامه را به همراه دارد.
src/App.vue: کامپوننت اصلی برنامه است.
پوشه src/assets: حاوی فایلهای استاتیک پروژه است.
src/main.js: نقطهی آغاز برنامه است.
webpack.config.json: تنظیمات وبپک جهت اجرای پروژه و بارگذاری ماژولهای موردنیاز.
اجرای برنامه
ابتدا نیاز است وابستگیهای برنامه دریافت شوند. اینکار را توسط دستور npm install و یا دستور yarn (در صورتیکه yarn را از قبل بر روی سیستم خود نصب کردهاید) انجام خواهیم داد:
npm install
بررسی فایلهای Vue
درون یک برنامهی Vue واقعی، فایلهایی با پسوند vue. وجود دارند. این فایل شامل تمپلیت، کدها و همچنین استایلهای یک کامپوننت میباشند.
<template> <div> <!-- Write your HTML with Vue in here --> </div> </template> <script> export default { // Write your Vue component logic here } </script> <style scoped> /* Write your styles for the component in here */ </style>
بنابراین درون فایلی با ساختار فوق، تمامی موارد مورد نیاز برای یک کامپوننت ویو را خواهیم داشت و به اصطلاح نیازی به context switching نخواهیم داشت؛ زیرا تمامی قسمتها را به صورت یکجا در یک محل در اختیار داریم و به راحتی میتوانیم تمرکز خود را بر روی کدها قرار دهیم. درون کامپوننت نیز میتوانیم کامپوننتهای موردنیاز را ایمپورت و از آن استفاده کنیم:
import { New } from "./components/New.vue"; export default { components: { New } }
Vue CLI 3
تا اینجا از نسخهی پایدار Vue CLI استفاده کردیم. نسخهی 3 آن هنوز در مرحلهی beta قرار دارد. در این نسخه امکانات و دستورات بیشتری اضافه شدهاست؛ از ایجاد یک پروژه ساده تا ایجاد یک پروژه مبتنی بر TypeScript. برای نصب و یا آپگرید میتوانید از دستور زیر استفاده کنید:
npm install -g @vue/cli
3.0.0-beta.11
ایجاد یک پروژه جدید
برای ایجاد یک پروژه جدید میتوانید دستور زیر را صادر کنید:
vue create my-project
Vue CLI v3.0.0-beta.11 ? Please pick a preset: (Use arrow keys) ❯ default (babel, eslint) Manually select features
بعد از طی کردن مراحل، میتوانید قالب پروژهی ایجاد شده را به صورت یک preset داشته باشید تا در پروژههای آینده مجبور نباشید مراحل قبل را طی کنید. این preset درون یک فایل JSON به صورت زیر ذخیره خواهد شد و حاوی اطلاعات زیر است:
{ "useConfigFiles": true, "router": true, "vuex": true, "cssPreprocessor": "sass", "plugins": { "@vue/cli-plugin-babel": {}, "@vue/cli-plugin-eslint": { "config": "airbnb", "lintOn": ["save", "commit"] } } }
در حالت manually نیز میتوانید گزینههای بیشتری را برای تعیین نوع قالب پروژه، انتخاب نمائید. به عنوان مثال میتوان از TypeScript یا اینکه از lintter یا formatter خاصی برای کدها استفاده کرد:
در ادامه دیگر آپشنها را نیز میتوانید تعیین کرده و در نهایت به صورت یک قالب از پیش تعریف شده نیز پروژه را داشته باشید:
Zero-config Prototyping
یکی از قابلیتهای جالب Vue، امکان تهیه سریع prototype یا طرح اولیه میباشد. شاید اکثر اوقات نیاز داشته باشید یک ویژگی یا قابلیت خاص را با Vue تست کنید. در این موارد ممکن است از سایتی مانند CodePen استفاده کنید. اما توسط افزونهی cli-service-global میتوانید به صورت لوکال و بدون نیاز به راهاندازی یک پروژهی جدید، کدهای موردنیاز را آزمایش کنید. فرض کنید میخواهیم تمپلیت زیر را قبل از افزودن آن به پروژه، مورد تست قرار دهیم:
<!-- MyCard.vue --> <template> <div class="card"> <h1>Card Title</h1> <p>Card content goes here. Make sure it's not Lorem.</p> </div> </template>
npm install -g @vue/cli-service-global
vue serve MyCard.vue
خروجی:
به عنوان تمرین، همان برنامهی طراحی گریدی را که تا قسمت 14 تکمیل کردیم، با معرفی مسیریابی بهبود خواهیم بخشید. برای این منظور یک NavBar بوت استرپی را به بالای صفحه اضافه میکنیم که دارای سه لینک movies ،customers و rentals است. به همین جهت نیاز به دو کامپوننت مقدماتی customers و rentals نیز وجود دارد که تنها یک h1 را نمایش میدهند. به علاوه منوی راهبری برنامه نیز باید بر اساس مسیر فعال جاری، با رنگ مشخصی، فعال بودن مسیریابی گزینهی انتخابی را مشخص کند. در این برنامه اگر کاربر، آدرس نامعتبری را وارد کرد، باید به صفحهی not-found هدایت شود. همچنین میخواهیم تمام عناوین فیلمهای نمایش داده شدهی در جدول، تبدیل به لینکهایی به صفحهی جدید جزئیات آنها شوند. در این صفحه باید یک دکمهی Save هم وجود داشته باشد تا با کلیک بر روی آن، به صورت خودکار به صفحهی movies هدایت شویم.
برپایی پیشنیازها
ابتدا کتابخانهی react-router-dom را نصب میکنیم:
سپس کامپوننت App را با BrowserRouter آن در فایل index.js محصور میکنیم؛ تا کار انتقال مدیریت تاریخچهی مرور صفحات در مرورگر، به درخت کامپوننتهای React انجام شود:
ایجاد کامپوننتهای جدید مورد نیاز
برای تکمیل نیازمندیهایی که در مقدمه عنوان شد، این کامپوننتهای جدید را ایجاد میکنیم:
کامپوننت بدون حالت تابعی src\components\customers.jsx با این محتوا:
کامپوننت بدون حالت تابعی src\components\rentals.jsx با این محتوا:
کامپوننت بدون حالت تابعی src\components\notFound.jsx با این محتوا:
کامپوننت بدون حالت تابعی src\components\movieForm.jsx با این محتوا:
ثبت مسیریابیهای مورد نیاز برنامه
پس از نصب کتابخانهی مسیریابی و راه اندازی آن، اکنون نوبت به تعریف مسیریابیهای مورد نیاز برنامه در فایل app.js است:
- در اینجا ابتدا چهار مسیریابی جدید را جهت نمایش صفحات کامپوننتهایی که ایجاد کردیم، تعریف و سپس نکتهی «مدیریت مسیرهای نامعتبر درخواستی» قسمت قبل را نیز با افزودن کامپوننت Redirect، پیاده سازی کردهایم. به علاوه پیشتر نمایش کامپوننت Movies را داخل container تعریف شده داشتیم که اکنون با وجود این مسیریابیها، نیازی به تعریف المان آن نیست و از return تعریف شده، حذف شدهاست.
تا اینجا اگر برنامه را اجرا کنیم، بلافاصله به http://localhost:3000/not-found هدایت میشویم. از این جهت که هنوز مسیریابی را برای / یا ریشهی سایت که در ابتدا نمایش داده میشود، تنظیم نکردهایم. به همین جهت Redirect زیر را پیش از آخرین Redirect تعریف شده اضافه میکنیم تا با درخواست ریشهی سایت، به آدرس /movies هدایت شویم:
و هانطور که در بخش 1 این قسمت بررسی کردیم، چون این مسیریابی با تمام آدرسهای شروع شدهی با / تطابق پیدا میکند، وجود Switch در اینجا ضروری است؛ تا پس از انطباق با اولین مسیر ممکن، کار مسیریابی به پایان برسد. به علاوه با تعریف این Redirect، اگر مثلا آدرس نامعتبر http://localhost:3000/xyz را درخواست کنیم، به آدرس movies/ هدایت میشویم؛ چون / با xyz/ تطابق پیدا کرده و کار در همینجا به پایان میرسد. به همین جهت ذکر ویژگی exact در تعریف این Redirect ویژه ضروری است؛ تا صرفا به ریشهی سایت پاسخ دهد:
افزودن منوی راهبری به برنامه
ابتدا فایل جدید src\components\navBar.jsx را ایجاد میکنیم؛ با این محتوا:
توضیحات:
- ساختار کلی NavBar ای را که ملاحظه میکنید، دقیقا از مثالهای رسمی مستندات بوت استرپ 4 گرفته شدهاست و تمام classهای آن با className جایگزین شدهاند.
- سپس تمام anchorهای موجود در یک منوی راهبری بوت استرپ را به Link و یا NavLink تبدیل کردهایم تا برنامه به صورت SPA عمل کند؛ یعنی با کلیک بر روی هر لینک، بارگذاری کامل صفحه در مرورگر صورت نگیرد و تنها محل و قسمتی که توسط کامپوننتهای Route مشخص شده، به روز رسانی شوند. تفاوت NavLink با Link در کتابخانهی react-router-dom، افزودن خودکار کلاس active به المانی است که بر روی آن کلیک شدهاست. به این ترتیب بهتر میتوان تشخیص داد که هم اکنون در کجای منوی راهبری قرار داریم.
- پس از تبدیل anchorها به Link و یا NavLink، مرحلهی بعد، تبدیل hrefهای لینکهای قبلی به ویژگی to است که هر کدام باید به یکی از مسیریابیهای تنظیم شده، مقدار دهی گردد.
پس از تعریف کامپوننت منوی راهبری سایت، به app.js بازگشته و این کامپوننت را پیش از مسیریابیهای تعریف شده اضافه میکنیم:
در اینجا چون نیاز به بازگشت دو المان NavBar و main وجود داشت، از React.Fragment برای محصور کردن آنها استفاده کردیم.
به علاوه به فایل index.css برنامه مراجعه کرده و padding این navBar را صفر میکنیم تا از بالای صفحه و بدون فاصلهای نمایش داده شود و container اصلی نیز اندکی از پایین آن فاصله پیدا کند:
با این تغییر، اکنون ظاهر برنامه به صورت زیر در خواهد آمد:
اگر دقت کنید چون آدرس http://localhost:3000/movies در حال نمایش است، در منوی راهبری، گزینهی متناظر با آن، با رنگی دیگر مشخص (فعال) شدهاست.
لینک کردن عناوین فیلمهای نمایش داده شده به کامپوننت movieForm
برای تبدیل عناوین نمایش داده شدهی در جدول فیلمها به لینک، به کامپوننت src\components\moviesTable.jsx مراجعه کرده و تغییرات زیر را اعمال میکنیم:
- در قدم اول باید بجای ذکر خاصیت Title در آرایهی ستونهای جدول:
یک محتوای لینک شده را نمایش دهیم:
در اینجا خاصیت content اضافه شدهاست تا یک المان React را مانند Link، بازگشت دهد و چون میخواهیم id هر فیلم نیز در اینجا ذکر شود، آنرا به صورت arrow function تعریف کردهایم تا شیء movie را گرفته و لینک به آنرا تولید کند. در اینجا از یک template literal برای تولید پویای رشتهی منتسب به to استفاده کردهایم.
همچنین این Link را هم باید در بالای این ماژول import کرد:
تا اینجا عناوین فیلمها را تبدیل به لینکهایی کردیم:
تعریف مسیریابی نمایش جزئیات یک فیلم انتخابی
اگر به تصویر فوق دقت کنید، به آدرسهایی مانند http://localhost:3000/movies/5b21ca3eeb7f6fbccd47181a رسیدهایم که به همراه id هر فیلم هستند. اکنون میخواهیم کلیک بر روی این لینکها را جهت فعالسازی صفحهی نمایش جزئیات فیلم، تنظیم کنیم. به همین جهت به فایل app.js مراجعه کرده و مسیریابی زیر را به ابتدای Switch تعریف شده اضافه میکنیم:
که نیاز به این import را هم دارد:
تکمیل کامپوننت نمایش جزئیات یک فیلم
اکنون میخواهیم صفحهی نمایش جزئیات فیلم، به همراه نمایش id فیلم باشد و همچنین با کلیک بر روی دکمهی Save آن، کاربر را به صفحهی movies هدایت کند. به همین جهت فایل src\components\movieForm.jsx را به صورت زیر ویرایش میکنیم:
توضیحات:
- چون این کامپوننت، یک کامپوننت تابعی بدون حالت است، props را باید از طریق آرگومان خود دریافت کند و البته در همینجا امکان Object Destructuring خواصی که از آن نیاز داریم، مهیا است؛ مانند { match, history } که ملاحظه میکنید.
- سپس شیء match، امکان دسترسی به params ارسالی به صفحه را مانند id فیلم، میسر میکند.
- با استفاده از شیء history و متد push آن میتوان علاوه بر به روز رسانی تاریخچهی مرورگر، به مسیر مشخص شده بازگشت که در همینجا و به صورت inline، تعریف شدهاست.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: sample-17.zip
برپایی پیشنیازها
ابتدا کتابخانهی react-router-dom را نصب میکنیم:
npm i react-router-dom --save
import { BrowserRouter } from "react-router-dom"; //... ReactDOM.render( <BrowserRouter> <App /> </BrowserRouter>, document.getElementById("root") );
برای تکمیل نیازمندیهایی که در مقدمه عنوان شد، این کامپوننتهای جدید را ایجاد میکنیم:
کامپوننت بدون حالت تابعی src\components\customers.jsx با این محتوا:
import React from "react"; const Customers = () => { return <h1>Customers</h1>; }; export default Customers;
کامپوننت بدون حالت تابعی src\components\rentals.jsx با این محتوا:
import React from "react"; const Rentals = () => { return <h1>Rentals</h1>; }; export default Rentals;
کامپوننت بدون حالت تابعی src\components\notFound.jsx با این محتوا:
import React from "react"; const NotFound = () => { return <h1>Not Found</h1>; }; export default NotFound;
کامپوننت بدون حالت تابعی src\components\movieForm.jsx با این محتوا:
import React from "react"; const MovieForm = () => { return ( <div> <h1>Movie Form</h1> <button className="btn btn-primary">Save</button> </div> ); }; export default MovieForm;
ثبت مسیریابیهای مورد نیاز برنامه
پس از نصب کتابخانهی مسیریابی و راه اندازی آن، اکنون نوبت به تعریف مسیریابیهای مورد نیاز برنامه در فایل app.js است:
import "./App.css"; import React from "react"; import { Redirect, Route, Switch } from "react-router-dom"; import Customers from "./components/customers"; import Movies from "./components/movies"; import NotFound from "./components/notFound"; import Rentals from "./components/rentals"; function App() { return ( <main className="container"> <Switch> <Route path="/movies" component={Movies} /> <Route path="/customers" component={Customers} /> <Route path="/rentals" component={Rentals} /> <Route path="/not-found" component={NotFound} /> <Redirect to="/not-found" /> </Switch> </main> ); } export default App;
تا اینجا اگر برنامه را اجرا کنیم، بلافاصله به http://localhost:3000/not-found هدایت میشویم. از این جهت که هنوز مسیریابی را برای / یا ریشهی سایت که در ابتدا نمایش داده میشود، تنظیم نکردهایم. به همین جهت Redirect زیر را پیش از آخرین Redirect تعریف شده اضافه میکنیم تا با درخواست ریشهی سایت، به آدرس /movies هدایت شویم:
<Redirect from="/" to="/movies" />
<Redirect from="/" exact to="/movies" />
افزودن منوی راهبری به برنامه
ابتدا فایل جدید src\components\navBar.jsx را ایجاد میکنیم؛ با این محتوا:
import React from "react"; import { Link, NavLink } from "react-router-dom"; const NavBar = () => { return ( <nav className="navbar navbar-expand-lg navbar-light bg-light"> <Link className="navbar-brand" to="/"> Home </Link> <button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation" > <span className="navbar-toggler-icon" /> </button> <div className="collapse navbar-collapse" id="navbarNavAltMarkup"> <div className="navbar-nav"> <NavLink className="nav-item nav-link" to="/movies"> Movies </NavLink> <NavLink className="nav-item nav-link" to="/customers"> Customers </NavLink> <NavLink className="nav-item nav-link" to="/rentals"> Rentals </NavLink> </div> </div> </nav> ); }; export default NavBar;
- ساختار کلی NavBar ای را که ملاحظه میکنید، دقیقا از مثالهای رسمی مستندات بوت استرپ 4 گرفته شدهاست و تمام classهای آن با className جایگزین شدهاند.
- سپس تمام anchorهای موجود در یک منوی راهبری بوت استرپ را به Link و یا NavLink تبدیل کردهایم تا برنامه به صورت SPA عمل کند؛ یعنی با کلیک بر روی هر لینک، بارگذاری کامل صفحه در مرورگر صورت نگیرد و تنها محل و قسمتی که توسط کامپوننتهای Route مشخص شده، به روز رسانی شوند. تفاوت NavLink با Link در کتابخانهی react-router-dom، افزودن خودکار کلاس active به المانی است که بر روی آن کلیک شدهاست. به این ترتیب بهتر میتوان تشخیص داد که هم اکنون در کجای منوی راهبری قرار داریم.
- پس از تبدیل anchorها به Link و یا NavLink، مرحلهی بعد، تبدیل hrefهای لینکهای قبلی به ویژگی to است که هر کدام باید به یکی از مسیریابیهای تنظیم شده، مقدار دهی گردد.
پس از تعریف کامپوننت منوی راهبری سایت، به app.js بازگشته و این کامپوننت را پیش از مسیریابیهای تعریف شده اضافه میکنیم:
import NavBar from "./components/navBar"; // ... function App() { return ( <React.Fragment> <NavBar /> <main className="container"> // ... </main> </React.Fragment> ); } export default App;
به علاوه به فایل index.css برنامه مراجعه کرده و padding این navBar را صفر میکنیم تا از بالای صفحه و بدون فاصلهای نمایش داده شود و container اصلی نیز اندکی از پایین آن فاصله پیدا کند:
body { margin: 0; padding: 0 0 0 0; font-family: sans-serif; } .navbar { margin-bottom: 30px; } .clickable { cursor: pointer; }
اگر دقت کنید چون آدرس http://localhost:3000/movies در حال نمایش است، در منوی راهبری، گزینهی متناظر با آن، با رنگی دیگر مشخص (فعال) شدهاست.
لینک کردن عناوین فیلمهای نمایش داده شده به کامپوننت movieForm
برای تبدیل عناوین نمایش داده شدهی در جدول فیلمها به لینک، به کامپوننت src\components\moviesTable.jsx مراجعه کرده و تغییرات زیر را اعمال میکنیم:
- در قدم اول باید بجای ذکر خاصیت Title در آرایهی ستونهای جدول:
class MoviesTable extends Component { columns = [ { path: "title", label: "Title" },
class MoviesTable extends Component { columns = [ { path: "title", label: "Title", content: movie => <Link to={`/movies/${movie._id}`}>{movie.title}</Link> },
همچنین این Link را هم باید در بالای این ماژول import کرد:
import { Link } from "react-router-dom";
تعریف مسیریابی نمایش جزئیات یک فیلم انتخابی
اگر به تصویر فوق دقت کنید، به آدرسهایی مانند http://localhost:3000/movies/5b21ca3eeb7f6fbccd47181a رسیدهایم که به همراه id هر فیلم هستند. اکنون میخواهیم کلیک بر روی این لینکها را جهت فعالسازی صفحهی نمایش جزئیات فیلم، تنظیم کنیم. به همین جهت به فایل app.js مراجعه کرده و مسیریابی زیر را به ابتدای Switch تعریف شده اضافه میکنیم:
<Route path="/movies/:id" component={MovieForm} />
import MovieForm from "./components/movieForm";
تکمیل کامپوننت نمایش جزئیات یک فیلم
اکنون میخواهیم صفحهی نمایش جزئیات فیلم، به همراه نمایش id فیلم باشد و همچنین با کلیک بر روی دکمهی Save آن، کاربر را به صفحهی movies هدایت کند. به همین جهت فایل src\components\movieForm.jsx را به صورت زیر ویرایش میکنیم:
import React from "react"; const MovieForm = ({ match, history }) => { return ( <div> <h1>Movie Form {match.params.id} </h1> <button className="btn btn-primary" onClick={() => history.push("/movies")} > Save </button> </div> ); }; export default MovieForm;
- چون این کامپوننت، یک کامپوننت تابعی بدون حالت است، props را باید از طریق آرگومان خود دریافت کند و البته در همینجا امکان Object Destructuring خواصی که از آن نیاز داریم، مهیا است؛ مانند { match, history } که ملاحظه میکنید.
- سپس شیء match، امکان دسترسی به params ارسالی به صفحه را مانند id فیلم، میسر میکند.
- با استفاده از شیء history و متد push آن میتوان علاوه بر به روز رسانی تاریخچهی مرورگر، به مسیر مشخص شده بازگشت که در همینجا و به صورت inline، تعریف شدهاست.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: sample-17.zip