مقدمه في Tag Helpers

-

بنتعلم في هذا الدرس شو يعني Tag Helpers والي هو إضافة جديدة في ASP.NET Core MVC ، الغرض من هذه الاضافة هو الدمج بين server-side code  و HTML elements  في ملفات Razor files.

يتم ذلك عن طريق اضافة تعريفات ل  HTML elements داخل ملفات Razor، وبهذا يمكن تمكين server-side code  للمشاركة في إنشاء عناصر HTML وعرضها في ملفات Razor.

يوجد نوعين من  Tag Helpers:

  • Built-In Tag Helpers - التي تأتي مدمجة مسبقًا في Core Framework ، وتؤدي مهام شائعة مثل إنشاء النماذج creating forms ، وعرض رسائل التحقق من الصحة validation messages ، الربط مع action و controller وما إلى ذلك.
  • Custom Tag Helper - يمكن إنشاؤها بشكل منفصل وخاص عن طريق مطور التطبيق لإجراء التحويل المطلوب على HTML element.

لنبدأ ب  Built-In tag helpers  ثم سننتقل بعد ذلك لتعلم كيفية إنشاء Custom Tag Helper.

سنتابع العمل على المشروع السابق StudentsAcademy 

تأكد من محتوى الملف  class Startup.cs بحيث يحتوي الكود الموضح ادناه:


using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using StudentsAcademy.Services;
using StudentsAcademy.Middleware;
using StudentsAcademy.Share;
using Microsoft.EntityFrameworkCore;
using StudentsAcademy.Interface;
using StudentsAcademy.Repository;
using StudentsAcademy.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Microsoft.AspNetCore.Routing;
using StudentsAcademy.CustomConstraint;
namespace StudentsAcademy
{
    public class Startup
    {
        public IConfiguration Configuration { get; }
        private IWebHostEnvironment env;
        public Startup(IConfiguration configuration, IWebHostEnvironment hostEnv)
        {
            Configuration = configuration;
            env = hostEnv;
        }
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();   
        }
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseStatusCodePages();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }
            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();
            app.UseRouting();
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

اضافة Model 

في مجلد Models في جذر التطبيق تأكد من وجود class باسم CoursesModel.cs ثم تأكد من وجود الكود التالي داخله:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace StudentsAcademy.Models
{
    public class CoursesModel
    {
        public string CourseNumber { get; set; }
        public string CourseName { get; set; }
        public string CourseDescription { get; set; }
        public decimal Price { get; set; }
        public int Capacity { get; set; }
    }
}

انشاء ملف interface

انتقل الى المجلد Interface ثم تأكد من اضافة ملف من نوع Interface باسم ICourses ثم اضف الكود التالي :

using StudentsAcademy.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace StudentsAcademy.Interface
{
    public interface ICourses
    {
        IEnumerable<CoursesModel> coursesModel { get; }
        CoursesModel this[string name] { get; }
        void AddCourse(CoursesModel coursesModel);
        void DeleteCourse(CoursesModel coursesModel);
    }
}
قمنا هنا بتعريف IEnumerable بحيث تحتوي على CoursesModel بالاضافه الى اضافة 2 action للاضافة والحذف من Courses
اضافة Repository 
انتقل الى مجلد Repository ثم تأكد من وجود ملف باسم CourseRepository.cs ثم تأكد من اضافة الكود الوارد أدناه:

using StudentsAcademy.Interface;
using StudentsAcademy.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace StudentsAcademy.Repository
{
    public class CourseRepository : ICourses
    {
        private string guid = System.Guid.NewGuid().ToString();
        public override string ToString()
        {
            return guid;
        }
        private ICourses iCourses;
        public CoursesModel this[string name] => iCourses[name];
        private List<CoursesModel> CoursesModelStore = new List<CoursesModel> {
            new CoursesModel {  CourseNumber = "0001",CourseName="Math",CourseDescription="Math Desc",Capacity=20, Price = 115},
            new CoursesModel { CourseNumber = "0002",CourseName="Sciences",CourseDescription="Sciences Desc",Capacity=30, Price = 120},
            new CoursesModel { CourseNumber = "0003",CourseName="Physics",CourseDescription="Physics Desc",Capacity=25, Price = 140},
        };
        public IEnumerable<CoursesModel> coursesModel => CoursesModelStore;
        public void AddCourse(CoursesModel Courses)
        {
            CoursesModelStore.Add(Courses);
        }
        public void DeleteCourse(CoursesModel Courses) => iCourses.DeleteCourse(Courses);
    }
}

يحتوي CourseRepository.cs على ICourses Interface التي تحتوي على:
  • خاصية IEnumerable للتكرار على جميع Courses.
  • وظيفة تسمى AddCourse لإضافة Courses.
  • يعمل CourseRepository class على تنفيذ ICourses Interface. يتمثل عمل CourseRepository في توفير مستودع مؤقت (تخزين في الذاكرة) للمواد والدروس في التطبيق.
  • يخزن المواد والدروس في قائمة خاصة (باسم CoursesModelStore) ولديه أيضًا طريقة تسمى AddCourse لإضافة مادة جديدة إلى القائمة الخاصة.
  • كما أن لديها خاصية IEnumerable لتكرار المواد 
تسجيل Repository ك Service
نحتاج الى تسجيل هذا المستودع في ملف Startup class  لذلك انتقل الى هذا الملف وقم باضافة الكود التالي في ConfigureServices : 
الكود السابق يعمل على تسجّيل مستودع المواد كخدمة باستخدام singleton life cycle. سيساعد هذا وحدات التحكم في جلب المنتجات من المستودع دون إنشاء كائن لها. تُعرف هذه الميزة باسم حقن التبعية والتي قمنا بشرحها لك بالتفصيل في درس حقن التبعية.

كود ملف Startup 

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using StudentsAcademy.Services;
using StudentsAcademy.Middleware;
using StudentsAcademy.Share;
using Microsoft.EntityFrameworkCore;
using StudentsAcademy.Interface;
using StudentsAcademy.Repository;
using StudentsAcademy.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Microsoft.AspNetCore.Routing;
using StudentsAcademy.CustomConstraint;
namespace StudentsAcademy 
{
    public class Startup
    {
      public void ConfigureServices(IServiceCollection services)
        {
              services.AddSingleton<ICourses, CourseRepository>();
              services.AddControllersWithViews();
        }
  public void Configure(IApplicationBuilder app, IWebHostEnvironment env)      
  {
            // keep it unchanged
        }
    }
}

إضافة  Controller  
انتقل الى مجلد Controllers  في جذر التطبيق ثم اضف Controller  جديد باسم CoursesController.



اضف الكود التالي الى هذا Controller :

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using StudentsAcademy.Interface;
using StudentsAcademy.Models;
using StudentsAcademy.Repository;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace StudentsAcademy.Controllers
{
    public class CoursesController : Controller
    {
        private ICourses Coursesrepository;
        public CoursesController(ICourses repo)
        {
            Coursesrepository = repo;
        }
        public IActionResult Index()
        {
            return View(Coursesrepository.coursesModel);
        }
    }
}

لاحظ constructor الذي يأخذ ICourses object. ستعمل ميزة حقن التبعية تلقائيًا على إنشاء كائن من Coursesrepository class  عند بدء تشغيل CoursesController.
السبب في ذلك هو تسجيل Coursesrepository class  كخدمة تستخدم singleton life cycle ، في ConfigureServices ، في ملف StartUp
هذا الكود يعني اننا أخبرنا ASP.NET Core بالعمل على توفير object من Coursesrepository class تلقائيًا كلما تم استدعاء الكود لإنشاء كائن من ICourses interface.
ثم في Index Action تم اضافة كود يعمل على إرجاع IEnumerable التي تحتوي على كافة المواد والدروس في المستودع.

إضافة ViewStart.cshtml, _ViewImports.cshtml

تأكد من اضافة كلا الملفين التالي الى Views folder في جذر التطبيق:




تأكد من اضافة الكود التالي في ملف ViewStart.cshtml_

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<link href="~/lib/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" /> 

تأكد من اضافة الكود التالي في ملف ViewImports.cshtml_

@using StudentsAcademy
@using StudentsAcademy.Models
@using Microsoft.AspNetCore.Http
@inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor
@using TagHelpers.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 
وظيفة addTagHelper@ هي توفير Built-In Tag Helpers ، والمعرفه في assembly تسمى  Microsoft.AspNetCore.Mvc.TagHelpers.
لقد قمنا أيضًا باستخدام مجموعه من namespace وذلك حتي لا يتم استخدماها في View في التطبيق حيث يتم تعريفها لمره واحده فقط في هذا الملف وهي : 
@using StudentsAcademy
@using StudentsAcademy.Models
@using Microsoft.AspNetCore.Http
@inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor
@using TagHelpers.Models
أنشاء Layout File

انتقل الى Shared folder داخل Views folder وداخل هذا المجلد  تأكد من وجود ملف  Layout.cshtml_.



قم بتغيير الكود كما هو موضح أدناه:

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    <link rel="stylesheet" asp-href-include="lib/bootstrap/css/bootstrap.min.css" />
</head>
<body>
    <div class="container-fluid">
        @RenderBody()
    </div>
</body>
</html>
لاحظ  إضافة ملف bootstrap.min.css  في head section. 
وأيضا تم اضافة  bootstrap class المسمي class="container-fluid" إلى div
الغرض من هذه الاضافة هو تحسين شكل عرض الView 

إضافة View
انتقل الى مجلد Courses داخل مجلد Views. بعد ذلك ، أضف Index View  إلى Courses folder


ثم اضف الكود التالي:

model IEnumerable<CoursesModel>
<table class="table table-sm table-bordered">
    <thead class="bg-dark text-white">
        <tr>
            <th>CourseNumber</th>
            <th>CourseName</th>
            <th>CourseDescription</th>
            <th>Capacity</th>
            <th>Price</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var product in Model)
        {
        <tr>
            <td>@product.CourseNumber</td>
            <td>@product.CourseName</td>
            <td>@product.CourseDescription</td>
            <td>@product.Capacity</td>
            <td>@product.Price</td>
        </tr>
        }
    </tbody>
</table>

يستقبل هذا view الدروس والمواد من CoursesModel على شكل IEnumerable، والتي سيتم الحصول عليها من Index action في CoursesController. ثم سنقوم بعرض هذه البيانات في جدول HTML 

ثم يتم قراءة البيانات باستخدام foreach وعرضها في الجدول 

لنقم بتشغيل التطبيق بالانتقال الى الرابط : 

https://localhost:44382/Courses

ستكون النتيجة