হ্যালো হাসিব! কোর্স আউটলাইন অনুযায়ী, আমরা এখন Section 11: View Components-এর তৃতীয় লেকচার “View Components with ViewData” (Lecture 122)-এ আছি। এই লেকচারে আমরা শিখবো কীভাবে View Component Class (C#) থেকে ডাটা তৈরি করে ViewData বা ViewBag-এর মাধ্যমে তার নিজস্ব Partial View-তে ডাটা পাস করতে হয়।

চলো, পুরো প্রসেসটিকে ধাপে ধাপে ডিকোড করে ফেলি।


সারসংক্ষেপ (Quick Revision List)

  • The Goal: View Component Class-এর ভেতরে ডাটা (Model) তৈরি করে সেটিকে Partial View (Sample.cshtml)-এ পাঠানো।
  • Crucial Rule: View Component Class এবং এর Partial View-এর নিজস্ব একটি আলাদা ViewData অবজেক্ট থাকে। এটি Controller-এর ViewData থেকে সম্পূর্ণ স্বাধীন (Independent)।
  • Sending Data: Class ফাইলে ViewData["key"] = data; ব্যবহার করে ডাটা পাঠানো হয়।
  • Receiving Data: Partial View-তে Type Casting-এর ঝামেলা এড়াতে ViewData-এর বদলে ViewBag.key ব্যবহার করে খুব সহজেই ডাটা অ্যাক্সেস করা যায়।
  • Intellisense Tip: foreach লুপ চালানোর সময় var এর বদলে স্পেসিফিক ক্লাসের নাম (যেমন Person) লিখলে Visual Studio-তে ভালো Intellisense (Auto-completion) পাওয়া যায়।

Comprehensive Breakdown

১. Model Classes তৈরি করা [Priority: 10/10]

যেহেতু আমরা ডাটা পাস করতে চাই, তাই আগে ডাটার স্ট্রাকচার (Model) বানাতে হবে।

  • Models ফোল্ডারে দুটি ক্লাস তৈরি করা হয়েছে: একটি হলো Person (যার ডেটা থাকবে) এবং আরেকটি হলো PersonGridModel (যা লিস্ট এবং টাইটেল ধারণ করবে)।

Code Implementation (Models/Person.cs & Models/PersonGridModel.cs):

namespace ViewComponentsExample.Models
{
    public class Person
    {
        public string? PersonName { get; set; } // C# 8/9+ feature: Nullable string
        public string? JobTitle { get; set; }
    }
 
    public class PersonGridModel
    {
        public string GridTitle { get; set; } = string.Empty;
        public List<Person> Persons { get; set; } = new List<Person>();
    }
}
 

২. View Component Class-এ ডাটা সেট করা [Priority: 10/10]

এখন GridViewComponent ক্লাসের InvokeAsync মেথডের ভেতরে আমরা ডাটাবেস থেকে ডাটা আনার বদলে হার্ডকোড করে কিছু ডাটা তৈরি করবো এবং সেটাকে ViewData-তে সেট করবো।

Code Implementation (ViewComponents/GridViewComponent.cs):

using ViewComponentsExample.Models; // Namespace import
 
public class GridViewComponent : ViewComponent
{
    public async Task<IViewComponentResult> InvokeAsync()
    {
        // ১. Model অবজেক্ট তৈরি এবং ইনিশিয়ালাইজ করা
        PersonGridModel gridModel = new PersonGridModel()
        {
            GridTitle = "Persons List",
            Persons = new List<Person>()
            {
                new Person() { PersonName = "John", JobTitle = "Manager" },
                new Person() { PersonName = "Jones", JobTitle = "Asst. Manager" },
                new Person() { PersonName = "Smith", JobTitle = "Clerk" }
            }
        };
 
        // ২. ViewData-তে অবজেক্টটি অ্যাসাইন করা
        ViewData["Grid"] = gridModel;
 
        // ৩. ভিউ রিটার্ন করা
        return View("Sample"); 
    }
}
 

৩. Partial View-তে ডাটা রিসিভ ও রেন্ডার করা [Priority: 10/10]

এই অংশটি খুব গুরুত্বপূর্ণ। আমরা Sample.cshtml-এ ViewData["Grid"] রিসিভ করবো।

The Type Casting Problem (কেন ViewData ব্যবহার করবো না?): যদি আমরা Partial View-তে সরাসরি ViewData["Grid"].GridTitle লিখতে যাই, তাহলে এরর খাবে। কারণ ViewData ডাটাকে object টাইপে সেভ করে। প্রপার্টি অ্যাক্সেস করতে হলে আগে তাকে কাস্ট (Typecast) করতে হয়:

<!-- Very Complex! -->
<h2>@(((PersonGridModel)ViewData["Grid"]).GridTitle)</h2> 
 

The Smart Solution (ViewBag): এই ঝামেলা এড়ানোর জন্য আমরা ViewBag ব্যবহার করবো। ViewBag হলো ViewData-এরই একটি ডাইনামিক র্যাপার (Dynamic Wrapper)।

<!-- Very Clean! -->
<h2>@ViewBag.Grid.GridTitle</h2>
 

Code Implementation (Views/Shared/Components/Grid/Sample.cshtml):

<!-- ViewImports ফাইলে '@using ViewComponentsExample.Models' ইমপোর্ট করা থাকতে হবে -->
 
<div class="box">
    <!-- ViewBag দিয়ে টাইটেল প্রিন্ট করা -->
    <h2>@ViewBag.Grid.GridTitle</h2>
    
    <table class="w-100">
        <thead>
            <tr>
                <th>Person Name</th>
                <th>Job Title</th>
            </tr>
        </thead>
        <tbody>
            <!-- ViewBag থেকে লিস্টটি নিয়ে লুপ চালানো -->
            <!-- var এর বদলে Person লিখলে intellisense সাপোর্ট পাওয়া যায় -->
            @foreach (Person person in ViewBag.Grid.Persons)
            {
                <tr>
                    <td>@person.PersonName</td>
                    <td>@person.JobTitle</td>
                </tr>
            }
        </tbody>
    </table>
</div>
 

৪. Execution Flow (ডাটা কীভাবে ফ্লো হচ্ছে?) [Priority: 8/10]

লেকচারার আবার ব্রেকপয়েন্ট দিয়ে ফ্লো-টা ক্লিয়ার করেছেন:

  1. Home Controller: Index একশন কল হয়।
  2. Index View: সেখানে <vc:grid> ট্যাগ হেল্পার বা InvokeAsync এর মাধ্যমে View Component কল হয়।
  3. View Component Class: GridViewComponent এর InvokeAsync মেথডে আসে। ডাটা তৈরি হয় এবং ViewData-তে সেট হয়।
  4. Partial View (Sample.cshtml): মেথড থেকে return View() কল হওয়ায় এটি রেন্ডার হওয়া শুরু হয়। এখানে সে ViewBag এর মাধ্যমে ডাটাগুলো এক্সট্রাক্ট করে লুপ চালিয়ে HTML তৈরি করে।
  5. Back to Index View: তৈরি হওয়া HTML-টা প্যারেন্ট ভিউয়ের নির্দিষ্ট জায়গায় বসে যায় এবং ফাইনালি লেআউটসহ ইউজার ব্রাউজারে দেখতে পায়।

VS / VS Code Shortcuts (For robust coding)

  • Check Definition: Person ক্লাসের উপর Ctrl + Click করলে অথবা F12 চাপলে সরাসরি Person.cs ফাইলে চলে যাবে।
  • Auto Implement Properties: ক্লাসের ভেতরে prop লিখে Tab + Tab চাপলে অটোমেটিক public int MyProperty { get; set; } তৈরি হয়ে যায়।

Best Practices & .NET 10 Context

Best Practices for Passing Data in View Components:

  1. Avoid ViewData/ViewBag in View Components: এই লেকচারে যদিও ViewData দিয়ে ডাটা পাস করা দেখানো হয়েছে, প্রফেশনাল লেভেলে এটি একটি Anti-pattern বা Bad Practice। View Component-এ ডাটা পাস করার ডিফল্ট এবং বেস্ট ওয়ে হলো Strongly Typed Models ব্যবহার করা। (যা তুমি ঠিক এর পরের লেকচারেই শিখবে)।
  2. Independent ViewData: লেকচারার যে কথাটি বারবার বলেছেন তা মনে রাখবে— View Component এর ViewData আর Controller এর ViewData এক নয়। তারা সম্পূর্ণ আলাদা মেমোরি শেয়ার করে।

.NET 10 Context: ASP.NET Core 10-এও এই মেকানিজমটি সেম। তবে আধুনিক C# 12/13 (যা .NET 8, 9 এবং 10 এ ব্যবহৃত হচ্ছে)-তে আমরা new কি-ওয়ার্ডকে আরও শর্ট করে ফেলি (Target-typed new expression):

// Modern .NET 10 style
PersonGridModel gridModel = new() // 'PersonGridModel' দুবার লিখতে হয় না
{
    GridTitle = "Persons List",
    Persons = new()
    {
        new() { PersonName = "John", JobTitle = "Manager" }
    }
};
 

হাসিব, View Component-এ ViewData দিয়ে ডাটা পাস করার কনসেপ্ট কি ক্লিয়ার হয়েছে? তুমি রেডি হলে আমরা এর সবচেয়ে প্রফেশনাল এপ্রোচ “Strongly Typed View Components”-এ মুভ করবো!