হ্যালো হাসিব! এই লেকচারে আমরা Persons-এর ডাটাগুলো খোঁজার জন্য একটি Search Functionality-এর UI এবং বেসিক সেটআপ তৈরি করবো। অর্থাৎ ইউজার ড্রপডাউন থেকে একটি কলাম (যেমন: Person Name বা Email) সিলেক্ট করবে, টেক্সট বক্সে কিছু লিখবে এবং Search বাটনে ক্লিক করলে সেই অনুযায়ী ডাটা ফিল্টার হয়ে আসবে।
চলো প্রথমে পুরো লেকচারটির একটি কুইক সামারি দেখে নিই।
📌 Quick Summary for Revision
- Search UI Setup:
Index.cshtml-এ টেবিলের ঠিক ওপরে একটি<form>তৈরি করা, যার মধ্যে Dropdown (<select>), Search Textbox (<input type="search">), Search Button এবং “Clear all” লিংক রাখা হয়েছে। - Dropdown Data with ViewBag: Controller-এর
Indexমেথড থেকেViewBag.SearchFields-এ একটি Dictionary (Key-Value pair) পাঠানো হয়েছে, যেখানে Key হলো প্রপার্টি নাম (যেমন:PersonName) এবং Value হলো ডিসপ্লে নাম (যেমন:Person Name)। - Dynamic Options:
Index.cshtml-এforeachলুপ চালিয়েViewBag.SearchFieldsথেকে<option>ট্যাগ জেনারেট করা। - Form Submission (GET): ফর্মটিতে
action="~/persons/index"এবংmethod="get"ব্যবহার করা হয়েছে। - Query String Parameter Binding:
<select>-এরname="searchBy"এবং<input>-এরname="searchString"দেওয়া হয়েছে। Controller-এরIndexমেথড যেন এগুলো রিসিভ করতে পারে, সেজন্য মেথডে সেম নামের দুটি প্যারামিটার যুক্ত করা হয়েছে।
🚀 Comprehensive Breakdown & The “Why”
নিচে লেকচারের প্রতিটি কনসেপ্ট বিস্তারিত এবং কারণসহ এক্সপ্লেইন করা হলো:
১. Controller থেকে Search Fields পাঠানো [Priority: 10/10]
The “Why”: আমরা ড্রপডাউনে হার্ডকোড করে অপশনগুলো লিখতে পারতাম। কিন্তু Controller থেকে পাঠালে সুবিধা হলো, ভবিষ্যতে যদি নতুন কোনো কলামে সার্চ করার দরকার হয়, তবে শুধু Controller-এ পরিবর্তন করলেই হবে, View-তে হাত দিতে হবে না।
এখানে Dictionary ব্যবহার করা হয়েছে কারণ ইউজারকে আমরা দেখাবো স্পেস দেওয়া সুন্দর নাম (“Person Name”), কিন্তু সার্চ বাটনে ক্লিক করলে সার্ভারে জমা হবে আসল প্রপার্টির নাম (“PersonName”)।
এখানে nameof(Person.PersonName) ব্যবহার করার কারণ হলো, যদি ভবিষ্যতে Model-এ প্রপার্টির নাম পরিবর্তন হয়, তবে এখানে অটোমেটিক আপডেট হয়ে যাবে (compile-time safety)।
Implementation:
// PersonsController.cs
[Route("persons/index")]
[Route("/")]
// GET রিকোয়েস্টের ডেটা রিসিভ করার জন্য দুটি প্যারামিটার যোগ করা হলো
public IActionResult Index(string searchBy, string searchString)
{
ViewBag.SearchFields = new Dictionary<string, string>()
{
{ nameof(PersonResponse.PersonName), "Person Name" },
{ nameof(PersonResponse.Email), "Email" },
{ nameof(PersonResponse.DateOfBirth), "Date of Birth" },
{ nameof(PersonResponse.Gender), "Gender" },
{ nameof(PersonResponse.CountryID), "Country" },
{ nameof(PersonResponse.Address), "Address" }
};
// Service থেকে ডাটা এনে View-তে পাঠানো হচ্ছে
List<PersonResponse> persons = _personsService.GetAllPersons();
return View(persons);
}
২. Search Form-এর UI তৈরি [Priority: 9/10]
The “Why”: সার্চ করার জন্য অবশ্যই একটি <form> লাগবে। ফর্মের method="get" দেওয়ার কারণ হলো, সার্চ অপারেশন ডাটাবেসে কোনো কিছু পরিবর্তন (Insert/Update/Delete) করে না, এটি শুধু ডাটা রিড করে। GET রিকোয়েস্টে ফর্মের ডাটাগুলো URL-এর Query String হিসেবে সার্ভারে যায় (যেমন: ?searchBy=PersonName&searchString=John)।
ফর্মের ভেতরের ইনপুট ফিল্ডগুলোতে name অ্যাট্রিবিউট দেওয়া বাধ্যতামূলক। কারণ ফর্ম সাবমিট হলে এই name অ্যাট্রিবিউটগুলোর ভ্যালুই Query String-এর Key হিসেবে সার্ভারে যায়।
Implementation:
@* Index.cshtml *@
@model IEnumerable<ServiceContracts.DTO.PersonResponse>
<form action="~/persons/index" method="get">
<div class="box flex">
<div class="flex-1">
@* name="searchBy" প্যারামিটারটি Controller-এর searchBy এর সাথে ম্যাপ হবে *@
<select class="form-input" name="searchBy">
@foreach (var field in ViewBag.SearchFields)
{
<option value="@field.Key">@field.Value</option>
}
</select>
</div>
<div class="flex-1">
@* name="searchString" প্যারামিটারটি Controller-এর searchString এর সাথে ম্যাপ হবে *@
<input type="search" id="Search" class="form-input" placeholder="Search" name="searchString" />
</div>
<div class="flex-1">
<button class="button button-blue-back">Search</button>
<a href="~/persons/index" class="link-hover">Clear all</a>
</div>
</div>
</form>
@* ... Table code follows ... *@
৩. Model Binding (Query String) [Priority: 10/10]
The “Why”: যখন ইউজার Search বাটনে ক্লিক করে, তখন ব্রাউজার URL তৈরি করে:
http://localhost:xxxx/persons/index?searchBy=PersonName&searchString=abc
ASP.NET Core-এর Model Binding ফিচারটি খুব স্মার্ট। এটি URL-এর Query String থেকে searchBy এবং searchString এর ভ্যালু পড়ে এবং Index মেথডের searchBy এবং searchString প্যারামিটারে অ্যাসাইন করে দেয়। তবে শর্ত হলো, HTML-এর name অ্যাট্রিবিউট এবং C# মেথডের প্যারামিটারের নাম হুবহু এক হতে হবে (Case-insensitive)।
🆕 .NET 10 & Modern Approaches
লেকচারে ফর্ম সাবমিট করার জন্য হার্ডকোড করা action="~/persons/index" ব্যবহার করা হয়েছে। এটি কাজ করবে ঠিকই, তবে MVC-এর বেস্ট প্র্যাকটিস হলো Tag Helpers ব্যবহার করা। এটি রাউটিং ফ্রেমওয়ার্কের সাথে যুক্ত থাকে বলে লিংক ভেঙে যাওয়ার ভয় থাকে না।
Modern MVC Approach with Tag Helpers:
<form asp-controller="Persons" asp-action="Index" method="get">
</form>
<a asp-controller="Persons" asp-action="Index" class="link-hover">Clear all</a>
⌨️ IDE Shortcuts
-
Format HTML/C# Code (কোড সাজানো): * Visual Studio:
Ctrl+K,Ctrl+D -
Visual Studio Code:
Shift+Alt+F -
Go to Definition (ViewBag বা Method-এ সরাসরি জাম্প করা): * Visual Studio & VS Code:
F12
💡 Best Practices (Search Implementation)
১. Retain User Input (State Management): ইউজার একবার সার্চ করার পর পেজ রিলোড হলে টেক্সট বক্স এবং ড্রপডাউন রিসেট হয়ে যায়। বেস্ট প্র্যাকটিস হলো Controller থেকে searchBy এবং searchString আবার ViewBag-এ ব্যাক করে পাঠানো, যাতে View সেগুলোকে value হিসেবে সেট করে রাখতে পারে এবং ইউজার বুঝতে পারে সে কী দিয়ে সার্চ করেছিলো। (সম্ভবত লেকচারার পরবর্তী ভিডিওতে এটি দেখাবেন)।
২. ViewBag vs ViewModel: লেকচারে ViewBag.SearchFields ব্যবহার করা হয়েছে। ছোট প্রজেক্টের জন্য ঠিক আছে, তবে বড় প্রজেক্টে ViewBag-এর বদলে একটি স্ট্রংলি টাইপড ViewModel (যেমন: PersonIndexViewModel) তৈরি করা বেস্ট প্র্যাকটিস, যেখানে Persons লিস্ট, SearchFields ডিকশনারি এবং SearchString সবকিছু এক জায়গায় থাকবে।
এই লেকচারে আমরা শুধু UI এবং Controller-এর প্যারামিটার সেটআপ করেছি। আগামী লেকচারেই আমরা এই প্যারামিটারগুলো ব্যবহার করে Service-এর মাধ্যমে আসল সার্চ লজিকটা ইমপ্লিমেন্ট করবো।
এই অংশে তোমার কি কোনো কনফিউশন আছে?