Skip to content

5. العرض ونموذجه

5.1. مقدمة

لنعد إلى بنية تطبيق ASP.NET MVC:

في الفصل السابق، درسنا كيف يقدم ASP.NET MVC معلومات الطلب [1] إلى إجراء [2a] في شكل نموذج قد يحتوي على قيود التحقق من الصحة. تم تمرير هذا النموذج كمدخلات إلى الإجراء، وأطلقنا عليه اسم نموذج الإجراء. سنركز الآن على النتيجة الأكثر شيوعًا للإجراء، وهي نوع [ViewResult]، الذي يتوافق مع عرض V [3] مصحوبًا بنموذجه M [2c]. سيُطلق على هذا النموذج اسم نموذج العرض V، ولا ينبغي الخلط بينه وبين نموذج الإجراء الذي درسناه للتو. أحدهما هو المدخلات للإجراء، والآخر هو المخرجات.

لنبدأ بإنشاء مشروع جديد [Example-03] [1] ضمن نفس الحل، من النوع الأساسي ASP.NET MVC:

لنقم بإنشاء وحدة تحكم باسم [First] [2]. فيما يلي الكود الذي تم إنشاؤه لهذه الوحدة:


using System.Web.Mvc;
 
namespace Exemple_03.Controllers
{
  public class FirstController : Controller
  {
    public ActionResult Index()
    {
      return View();
    }
 
  }
}

  • الأسطر 7–10: تم إنشاء إجراء [Index]. نوع الإرجاع لطريقة [Index] هو نوع فئة [ActionResult]، التي تُشتق منها معظم نتائج الإجراءات المحتملة؛
  • السطر 9: تُرجع طريقة [View] لفئة [Controller] (السطر 5) نوع [ViewResult]، الذي يُشتق من [ActionResult]. تدعم هذه الطريقة العديد من عمليات التحميل الزائد. سنلقي نظرة على بعضها. أهمها هو كما يلي:

Image

  • المعلمة الأولى هي اسم العرض. إذا تم حذفها، فإن العرض المستخدم هو الذي يحمل نفس اسم الإجراء الذي ينتج [ViewResult]، وسيتم البحث عنه في المجلد [/Views/{controller}]، حيث {controller} هو اسم وحدة التحكم؛
  • والثانية هي قالب العرض. إذا تم حذفها، فلن يكون للعرض قالب.

طريقة [Index] أدناه:


public ActionResult Index()
    {
      return View();
}

يطلب عرض طريقة العرض [ /Views/First/Index.cshtml ]. ولا يمرر أي قالب إليها. لنقم بإنشاء [1] المجلد [/Views/First]:

ثم ننشئ عرض [Index] [2] بداخله:

نحدد اسم العرض في [3]. يتم إنشاؤه في [4]. الرمز الذي تم إنشاؤه هو كما يلي:


@{
    Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <div>
 
    </div>
</body>
</html>

هذا هو HTML قياسي باستثناء الأسطر 1-3، وهي كود C#. يُسمى البرنامج الذي يدير العروض محرك العرض. وهو يتعامل مع كل ما ليس HTML ويحوله إلى HTML. وفي النهاية، هذا هو ما سيتم إرساله إلى متصفح العميل. محرك العرض المستخدم هنا هو [Razor]. وهو يسمح لك بتضمين كود C# داخل العرض. يقوم [Razor] بتفسير كود C# هذا وإنشاء HTML منه. فيما يلي بعض القواعد الأساسية لإدراج كود C# في عرض:

  • يحدث الانتقال من HTML إلى C# عند ظهور الحرف @ (السطر 1). إذا كان هذا الحرف يبدأ كتلة كود، يتم استخدام الأقواس المتعرجة (السطران 1 و 3). إذا كان يبدأ متغيرًا تريد استرداد قيمته، فما عليك سوى كتابة @variable؛
  • يحدث الانتقال من C# إلى HTML عند ظهور الحرف < (السطر 5). في بعض الأحيان، قد تحتاج إلى فرض هذا الانتقال، خاصةً عند تضمين نص عادي بدون علامات HTML في الصفحة. في هذه الحالة، استخدم علامة <text> لإدراج النص: <text>نص عادي هنا</text>.

يشير السطر 2 أعلاه إلى أن طريقة العرض [Index] لا تحتوي على صفحة رئيسية.

دعونا نعدل العرض على النحو التالي:


@{
  Layout = null;
  string vue = "Index";
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Index</title>
</head>
<body>
  <div>
    <h3>Vue @vue</h3>
  </div>
</body>
</html>

  • السطر 3: يحدد متغير C#؛
  • السطر 15: يعرض قيمة هذا المتغير.

الآن دعونا نطلب عنوان URL [/First/Index]:

Image

رمز HTML المستلم هو كما يلي:

<!DOCTYPE html>

<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Index</title>
</head>
<body>
  <div>
    <h3>Vue Index</h3>
  </div>
</body>
</html>

هذا مستند HTML خالص. تمت إزالة جميع أكواد C#.

5.2. استخدام [ViewBag] لتمرير المعلومات إلى العرض

نقوم بإنشاء إجراء جديد يسمى [Action01] مرتبط بعرض [Action01.cshtml]:

الإجراء [Action01] هو كما يلي:


    // Action01
    public ViewResult Action01()
    {
      ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
      return View();
}

  • السطر 4: نستخدم خاصية [ViewBag] الخاصة بوحدة التحكم. هذا كائن ديناميكي يمكن إضافة خصائص إليه، كما هو موضح في السطر 4. ومن السمات الرئيسية لهذا الكائن أنه يمكن الوصول إليه من خلال العرض. وبالتالي فهو وسيلة لتمرير المعلومات إلى العرض؛
  • السطر 5: يتم طلب العرض الافتراضي للإجراء. هذا هو العرض [/First/Action01.cshtml]. لا يتم تمرير أي نموذج إليه.

العرض [Action01.cshtml] هو كما يلي:


@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action01</title>
</head>
<body>
  <div>
    <h4>@ViewBag.info</h4>
  </div>
</body>
</html>

  • السطر 14: يتم عرض الخاصية [ViewBag.info].

دعونا نختبرها. نطلب عنوان URL [/First/Action01]:

Image

5.3. استخدام نموذج محدد النوع لتمرير المعلومات إلى العرض

الطريقة السابقة لها عيب يتمثل في عدم السماح باكتشاف الأخطاء قبل التنفيذ. وبالتالي، إذا كانت طريقة العرض [Action01.cshtml] تستخدم الكود


<h4>@ViewBag.Info</h4>

فسيحدث خطأ لأن الخاصية [Info] غير موجودة. أما الخاصية التي تم إنشاؤها بواسطة الإجراء [Action01] فهي تسمى [info]. لذلك يمكننا استخدام نموذج قوي النوع لتجنب هذه المشكلة.

في أحد الأمثلة التي تمت مناقشتها سابقًا، كان الإجراء كما يلي:


    // Action10
    public ContentResult Action10(ActionModel03 modèle)
    {
      string erreurs = getErrorMessagesFor(ModelState);
      string texte = string.Format("email={0}, jour={1}, info1={2}, info2={3}, info3={4}, erreurs={5}",
        modèle.Email, modèle.Jour, modèle.Info1, modèle.Info2, modèle.Info3, erreurs);
      return Content(texte, "text/plain", Encoding.UTF8);
}

كانت الإجراء [Action10] تمرر ستة عناصر من المعلومات (البريد الإلكتروني، اليوم، Info1، Info2، Info3، الأخطاء) إلى عميلها كسلسلة. سنمرر هذه المعلومات إلى نموذج عرض [ViewModel01]. وبما أن هذا النموذج يستخدم معلومات من [ActionModel03]، فسنشتقها من تلك الفئة.

نبدأ بنسخ [ActionModel03] من مشروع [Example-02] إلى مشروع [Example-03] الحالي:

ونقوم بتغيير مساحة الاسم الخاصة به لتتوافق مع مساحة الاسم الخاصة بمشروع [Example-03]:


using System.ComponentModel.DataAnnotations;
namespace Exemple_03.Models
{
  public class ActionModel03
  {
    [Required(ErrorMessage = "Le paramètre email est requis")]
    [EmailAddress(ErrorMessage = "Le paramètre email n'a pas un format valide")]
    public string Email { get; set; }
 
    [Required(ErrorMessage = "Le paramètre jour est requis")]
    [RegularExpression(@"^\d{1,2}$", ErrorMessage = "Le paramètre jour doit avoir 1 ou 2 chiffres")]
    public string Jour { get; set; }
 
    [Required(ErrorMessage = "Le paramètre info1 est requis")]
    [MaxLength(4, ErrorMessage = "Le paramètre info1 ne peut avoir plus de 4 caractères")]
    public string Info1 { get; set; }
 
    [Required(ErrorMessage = "Le paramètre info2 est requis")]
    [MinLength(2, ErrorMessage = "Le paramètre info2 ne peut avoir moins de 2 caractères")]
    public string Info2 { get; set; }
 
    [Required(ErrorMessage = "Le paramètre info3 est requis")]
    [MinLength(4, ErrorMessage = "Le paramètre info3 doit avoir 4 caractères exactement")]
    [MaxLength(4, ErrorMessage = "Le paramètre info3 doit avoir 4 caractères exactement")]
    public string Info3 { get; set; }
  }
}

  • السطر 2: مساحة الاسم الجديدة؛

ثم نقوم بإنشاء فئة [ViewModel01]:

فيما يلي كود [ViewModel01]:


namespace Exemple_03.Models
{
  public class ViewModel01 : ActionModel03
  {
    public string Erreurs { get; set; }
  }
}

  • السطر 3: ترث الفئة من [ActionModel03] وبالتالي ترث الخصائص [Email، Day، Info1، Info2، Info3
  • السطر 5: نضيف الخاصية [Errors] إليها.

نكتب الآن الإجراء [Action02] الذي:

  • يأخذ نموذج الإجراء [ActionModel03] كمدخلات؛
  • ويُرجع نموذج العرض [ViewModel01].

وفيما يلي كودها:


    // Action02
    public ViewResult Action02(ActionModel03 modèle)
    {
      string erreurs = getErrorMessagesFor(ModelState);
      return View(new ViewModel01(){Email=modèle.Email, Jour=modèle.Jour, Info1=modèle.Info1, Info2=modèle.Info2, Info3=modèle.Info3, Erreurs=erreurs});
}

  • السطر 1: [Action02] يستقبل نموذج الإجراء [ActionModel03]. ويعيد نتيجة من النوع [ViewResult
  • السطر 4: يتم تجميع الأخطاء المتعلقة بنموذج الإجراء [ActionModel03] في السلسلة [errors]. تم وصف الأسلوب [getErrorMessagesFor] في الصفحة 60 وتم تضمينه في وحدة التحكم [First] للمشروع الجديد؛
  • السطر 5: يتم استدعاء الأسلوب [View] مع معلمة. هذا هو نموذج العرض. لم يتم تحديد العرض نفسه. لذلك، سيتم استخدام العرض الافتراضي [/Views/First/Action02]. يتم إنشاء مثيل لنموذج العرض [ViewModel01] وتهيئته باستخدام المعلومات الخمس من نموذج الإجراء [ActionModel03] ومعلومات [errors] التي تم إنشاؤها في السطر 4.

نقوم الآن بإنشاء العرض [/First/Action02.cshtml]:

وفيما يلي كودها:


@model Exemple_03.Models.ViewModel01
@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action02</title>
</head>
<body>
  <h3>Informations du modèle de vue</h3>
  <ul>
    <li>Email : @Model.Email</li>
    <li>Jour : @Model.Jour</li>
    <li>Info1 : @Model.Info1</li>
    <li>Info2 : @Model.Info2</li>
    <li>Info3 : @Model.Info3</li>
    <li>Erreurs : @Model.Erreurs</li>
  </ul>
</body>
</html>

  • الميزة الجديدة موجودة في السطر 1. تحدد صيغة [@model] نوع نموذج العرض. ثم تتم الإشارة إلى هذا النموذج بواسطة صيغة [@Model] (السطور 16–21)؛
  • الأسطر 15–22: يتم عرض معلومات النموذج في قائمة.

دعونا نلقي نظرة على بعض الأمثلة لتنفيذ الإجراء [Action02].

أولاً، بدون معلمات:

Image

ثم مع معلمات غير صحيحة:

Image

ثم باستخدام المعلمات الصحيحة:

Image

في هذا المثال، يستخدم نموذج العرض [ViewModel01] المعلومات الواردة من نموذج الإجراء [ActionModel03]. وهذا هو الحال غالبًا. يمكننا إذن استخدام نموذج واحد يعمل كنموذج إجراء ونموذج عرض في آن واحد. نقوم بإنشاء نموذج جديد [ActionModel04]:

Image

والذي سيكون على النحو التالي:


using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace Exemple_03.Models
{
  [Bind(Exclude="Erreurs")]
  public class ActionModel04
  {
    // ---------------------- Action --------------------------------
    [Required(ErrorMessage = "Le paramètre email est requis")]
    [EmailAddress(ErrorMessage = "Le paramètre email n'a pas un format valide")]
    public string Email { get; set; }
 
    [Required(ErrorMessage = "Le paramètre jour est requis")]
    [RegularExpression(@"^\d{1,2}$", ErrorMessage = "Le paramètre jour doit avoir 1 ou 2 chiffres")]
    public string Jour { get; set; }
 
    [Required(ErrorMessage = "Le paramètre info1 est requis")]
    [MaxLength(4, ErrorMessage = "Le paramètre info1 ne peut avoir plus de 4 caractères")]
    public string Info1 { get; set; }
 
    [Required(ErrorMessage = "Le paramètre info2 est requis")]
    [MinLength(2, ErrorMessage = "Le paramètre info2 ne peut avoir moins de 2 caractères")]
    public string Info2 { get; set; }
 
    [Required(ErrorMessage = "Le paramètre info3 est requis")]
    [MinLength(4, ErrorMessage = "Le paramètre info3 doit avoir 4 caractères exactement")]
    [MaxLength(4, ErrorMessage = "Le paramètre info3 doit avoir 4 caractères exactement")]
    public string Info3 { get; set; }
 
    // ---------------------- view --------------------------------
    public string Erreurs { get; set; }
  }
}

  • الأسطر 8–28: نموذج الإجراء مع قيود التكامل الخاصة به. ستكون هذه الحقول أيضًا جزءًا من العرض؛
  • السطر 31: خاصية خاصة بنموذج العرض. تم استبعادها من نموذج الإجراء بواسطة التعليق التوضيحي في السطر 5.

نقوم بإنشاء الإجراء الجديد التالي [Action03]:


    // Action03
    public ViewResult Action03(ActionModel04 modèle)
    {
      modèle.Erreurs = getErrorMessagesFor(ModelState);
      return View(modèle);
}

  • السطر 2: [Action03] يستقبل نموذج الإجراء من النوع [ActionModel04
  • السطر 5: ويعيد هذا النموذج نفسه كنموذج العرض؛
  • السطر 4: مع استكماله بمعلومات [Errors

لم يتبقَ سوى إنشاء صفحة العرض [/First/Action03.cshtml]:

  • في [1]: انقر بزر الماوس الأيمن على كود [Action03]، ثم [Add View
  • في [2]: اسم العرض الافتراضي؛
  • في [3]: حدد أننا نقوم بإنشاء عرض ذي نوع محدد؛
  • في [4]: حدد الفئة الصحيحة من القائمة المنسدلة، في هذه الحالة فئة [ActionModel04
  • في [5]: العرض الذي تم إنشاؤه.

نعطي العرض [Action03] نفس كود العرض [Action02]. يتغير فقط قالب العرض (السطر 1) وعنوان الصفحة (السطر 11):


@model Exemple_03.Models.ActionModel04
@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action03</title>
</head>
<body>
  <h3>Informations du modèle de vue</h3>
  <ul>
    <li>Email : @Model.Email</li>
    <li>Jour : @Model.Jour</li>
    <li>Info1 : @Model.Info1</li>
    <li>Info2 : @Model.Info2</li>
    <li>Info3 : @Model.Info3</li>
    <li>Erreurs : @Model.Erreurs</li>
  </ul>
</body>
</html>

الآن دعونا نستدعي الإجراء [Action03] بدون أي معلمات:

Image

النتائج هي نفسها كما في السابق. من الشائع استخدام النموذج نفسه لكل من الإجراء والعرض، حيث إن نموذج العرض غالبًا ما يعيد استخدام المعلومات الموجودة في نموذج الإجراء. ولذلك، نستخدم نموذجًا أوسع نطاقًا يمكن أن يستخدمه كل من الإجراء والعرض الذي يولده. ويجب أن نحرص على استبعاد المعلومات التي لا تنتمي إلى نموذج الإجراء من عملية ربط البيانات. وإلا، فقد يقوم مستخدم ماهر بتهيئة أجزاء من نموذج العرض دون علمنا.

5.4. [Razor] – البدء

سنقدم الآن بعض عناصر طرق عرض [Razor]، وبشكل أساسي عبارات foreach و if.

لنفترض أننا نريد عرض قائمة بالأشخاص في جدول HTML. يمكن أن يكون نموذج العرض كما يلي [ViewModel02]:


namespace Exemple_03.Models
{
  public class ViewModel02
  {
    public Personne[] Personnes { get; set; }
    public ViewModel02()
    {
      Personnes = new Personne[] { new Personne { Nom = "Pierre", Age = 44 }, new Personne { Nom = "Pauline", Age = 12 } };
    }
  }
 
  public class Personne
  {
    public string Nom { get; set; }
    public int Age { get; set; }
  }
}
  • عرض النموذج هو فئة [ViewModel02]، الأسطر 3–10؛
  • السطر 5: يحتوي النموذج على مصفوفة من الأشخاص من النوع [Person] المحددة في الأسطر 12-16؛
  • الأسطر 6–10: يقوم منشئ النموذج بتهيئة الخاصية [People] من السطر 5 بمصفوفة من شخصين.

سيكون الإجراء الذي ينتج هذا النموذج كناتج هو [Action04] التالي:


    // Action04
    public ViewResult Action04()
    {
      return View(new ViewModel02());
}
  • السطر 2: لا يحتوي الإجراء على نموذج إدخال؛
  • السطر 4: يمرر العرض الافتراضي الخاص به، وهو مثيل لنموذج [ViewModel02] الذي حددناه للتو.

ستعرض طريقة العرض [Action04.cshtml] النموذج [ViewModel02]:

فيما يلي كود العرض [Action04.cshtml]:


@model Exemple_03.Models.ViewModel02
@using Exemple_03.Models
 
@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action04</title>
</head>
<body>
  <table border="1">
    <thead>
      <tr>
        <th>Nom</th>
        <th>Age</th>
      </tr>
    </thead>
    <tbody>
      @foreach (Personne p in Model.Personnes)
      {
        <tr>
          <td>@p.Nom</td>
          <td>@p.Age</td>
        </tr>
      }
    </tbody>
  </table>
</body>
</html>

  • السطر 1: قالب العرض؛
  • السطر 2: استيراد مساحة الاسم لفئة [Person] المستخدمة في السطر 24؛
  • الأسطر 16–32: جدول HTML الذي يعرض الأشخاص من النموذج؛
  • السطر 24: يتم تمييز بداية كود C# بالحرف @. ستقوم عبارة [foreach] بالتكرار عبر جميع الأشخاص في النموذج؛
  • السطران 26-27: ينهي الحرف < كود C# ويبدأ HTML. ثم يظهر الحرف @ مرة أخرى للعودة إلى C# وكتابة اسم الشخص. ثم يظهر الحرف < مرة أخرى للعودة إلى وضع HTML؛
  • السطر 28: يتم كتابة عمر الشخص.

يؤدي تنفيذ الإجراء [Action04] إلى النتيجة التالية:

Image

يمكن ملء عناصر أخرى في طريقة العرض بواسطة مجموعة: القوائم (القوائم المنسدلة أو غيرها)، وأزرار الاختيار، ومربعات الاختيار. انظر المثال الجديد التالي، الذي يعرض قائمة منسدلة.

سيكون النموذج [ViewModel05] كما يلي:


namespace Exemple_03.Models
{
  public class ViewModel05
  {
    public Personne2[] Personnes { get; set; }
    public int SelectedId { get; set; }
 
    public ViewModel05()
    {
      Personnes = new Personne2[] { 
        new Personne2 { Id = 1, Prénom = "Pierre", Nom = "Martino" }, 
        new Personne2 { Id = 2, Prénom = "Pauline", Nom = "Pereiro" }, 
        new Personne2 { Id = 3, Prénom = "Jacques", Nom = "Alfonso" } };
      SelectedId = 2;
    }
  }
 
  public class Personne2
  {
    public int Id { get; set; }
    public string Nom { get; set; }
    public string Prénom { get; set; }
  }
}

  • السطر 18: فئة [Person2] ذات ثلاث خصائص؛
  • السطر 3: نموذج [ViewModel05] للعرض؛
  • السطر 5: قائمة الأشخاص المراد عرضها في القائمة المنسدلة بتنسيق [الاسم الأول الاسم الأخير]؛
  • السطر 6: [Id] للشخص المراد تحديده من القائمة المنسدلة؛
  • الأسطر 8-16: المنشئ الذي ينشئ مصفوفة من ثلاثة أشخاص (الأسطر 10-13) ويحدد [Id] للشخص الذي يجب أن يظهر محددًا.

ستعرض طريقة العرض [Action05.cshtml] هذا القالب:

وإليك كودها:


@model Exemple_03.Models.ViewModel05
@using Exemple_03.Models
 
@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action05</title>
</head>
<body>
  <select>
    @foreach (Personne2 p in Model.Personnes)
    {
      string selected = "";
      if (p.Id == Model.SelectedId)
      {
        selected = "selected=\"selected\"";
      }
      <option value="@p.Id" @selected>@p.Prénom @p.Nom</option>
    }
  </select>
</body>
</html>

تم عرض خصائص قائمة HTML المنسدلة في القسم 2.5.2.6. دعونا نستعرضها:

Combo
<select size="1" name="cmbValues">
<option value="1">الخيار 1</option>
<option selected="selected" value="2">الخيار 2</option>
<option value="3">الخيار 3</option>
</select>

Image

علامة HTML
<select size=".." name="..">
<option [selected="selected"] value=”v”>...</option>
...
</select>
تعرض النص الموجود بين علامتي <option>...</option> في قائمة
السمات
name="cmbValeurs": اسم عنصر التحكم.
size="1": عدد عناصر القائمة المرئية. تجعل قيمة size="1" القائمة مكافئة لمربع القائمة المنسدلة.
selected="selected": إذا كانت هذه الكلمة الرئيسية موجودة لعنصر في القائمة، يظهر هذا العنصر محددًا في القائمة. في المثال أعلاه، يظهر عنصر القائمة choice2 كعنصر محدد في مربع القائمة المنسدلة عند عرضه لأول مرة.
value=”v”: إذا تم تحديد العنصر من قبل المستخدم، يتم إرسال هذه القيمة [v] إلى الخادم. إذا كانت هذه السمة غائبة، يتم إرسال النص المعروض والمحدد إلى الخادم.

يولد الكود في الأسطر 17–25 علامات <option> التي يتم وضعها داخل علامة <select> في السطر 16.

  • السطر 17: نكرر عبر قائمة الأشخاص في النموذج؛
  • السطر 20: نتحقق مما إذا كان الشخص الحالي هو الشخص المراد تحديده. إذا كان الأمر كذلك، فإننا نجهز النص selected="selected" لإدراجه في علامة <option
  • السطر 24: يتم كتابة علامة <option>.

دعونا نسمي الإجراء [Action05]:

  • في [1,2]، يتم عرض الأشخاص بالتنسيق [الاسم الأول الاسم الأخير]؛
  • في [1,2]، الشخص المحدد هو الذي [رقم الهوية] يساوي 2.

دعونا الآن نفحص كود HTML المصدر للصفحة أعلاه:


<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action05</title>
</head>
<body>
  <select>
      <option value="1" >Pierre Martino</option>
      <option value="2" selected=&quot;selected&quot;>Pauline Pereiro</option>
      <option value="3" >Jacques Alfonso</option>
  </select>
</body>
</html>

  • الأسطر 10–12: العلامات الثلاث <option> التي تم إنشاؤها بواسطة كود [Razor
  • السطر 11: تم بالفعل اختيار الشخص الذي يحمل [Id]=2.

يكفي المثالان أعلاه. عند كتابة عرض [Razor]، يجب أن تقاوم إغراء وضع المنطق بداخله. سيسمح لنا كود C# بالقيام بذلك. ومع ذلك، في نموذج MVC، يجب أن يكون المنطق في الإجراء أو في الطبقات السفلية [Business Logic، DAO] وليس في العرض. حتى عند الالتزام بنمط MVC، قد ينتهي بك الأمر بوجود الكثير من المنطق في العرض لحساب القيم الوسيطة. قد يعني هذا أن النموذج المستخدم ليس مفصلاً بما يكفي. يجب أن يحتوي النموذج على القيم النهائية التي يحتاجها العرض حتى لا يضطر إلى حسابها بنفسه. العرض الجيد هو الذي يحتوي على الحد الأدنى من المنطق وبنية HTML واضحة. إذا تم إدراج الكثير من كود C#، فقد تصبح بنية HTML غير قابلة للقراءة.

في المثال أعلاه، يمكن للمستخدم استخدام القائمة المنسدلة، وسنرغب بعد ذلك في معرفة الشخص الذي اختاره. لهذا، نحتاج إلى نموذج.

5.5. النماذج – البدء

سيكون النموذج المعروض على المستخدم كما يلي:

Image

سيكون نموذج العرض هو نموذج [ViewModel05] المستخدم سابقًا. وستكون الإجراء الذي سيعرض هذا العرض كما يلي:


    // Action06-GET
    [HttpGet]
    public ViewResult Action06()
    {
      return View("Action06Get",new ViewModel05());
}

  • السطر 2: لا يمكن طلب الإجراء إلا عبر طلب HTTP GET؛
  • السطر 5: سيتم عرض العرض [/First/Action06Get.cshtml] باستخدام مثيل من النوع [ViewModel05] كنموذج له.

سيكون العرض [/First/Action06Get.cshtml] كما يلي:


@model Exemple_03.Models.ViewModel05
@using Exemple_03.Models
 
@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action06-GET</title>
</head>
<body>
  <h3>Action06 - GET</h3>
  <p>Choisissez une personne</p>
  <form method="post" action="/First/Action06">
    <select name="personneId">
      @foreach (Personne2 p in Model.Personnes)
      {
        string selected = "";
        if (p.Id == Model.SelectedId)
        {
          selected = "selected=\"selected\"";
        }
        <option value="@p.Id" @selected>@p.Prénom @p.Nom</option>
      }
    </select>
    <input name="valider" type="submit" value="Valider" />
  </form>
</body>
</html>

الميزات الجديدة الرئيسية هي كما يلي:

  • السطر 18: لكي يقوم المتصفح بنقل المعلومات التي أدخلها المستخدم، نحتاج إلى نموذج. يتم تحديده بواسطة العلامة <form> في السطرين 18 و 31.

تم تقديم علامة HTML <form> في القسم 2.5.2.1. دعونا نستعرض خصائصها:

نموذج

<form method="post" action="FormulairePost.aspx">
علامة HTML
<form name="..." method="..." action="...">...</form>
السمات
name="frmexample": اسم النموذج - اختياري
method="..." : الطريقة التي يستخدمها المتصفح لإرسال القيم التي تم جمعها في النموذج إلى خادم الويب
action="..." : عنوان URL الذي سيتم إرسال القيم التي تم جمعها في النموذج إليه.
يتم تضمين نموذج الويب بين العلامتين <form>...</form>. يمكن أن يكون للنموذج اسم (name="xx"). ينطبق هذا على جميع عناصر التحكم الموجودة داخل النموذج. الغرض من النموذج هو جمع المعلومات التي أدخلها المستخدم عبر لوحة المفاتيح أو الماوس وإرسالها إلى عنوان URL لخادم الويب. أي عنوان؟ العنوان المشار إليه في السمة action="URL". إذا كانت هذه السمة مفقودة، فسيتم إرسال المعلومات إلى عنوان URL للوثيقة التي تحتوي على النموذج. يمكن لعميل الويب استخدام طريقتين مختلفتين، تسميان POST و GET، لإرسال البيانات إلى خادم الويب. تحدد السمة method="method"، حيث يتم تعيين method إلى GET أو POST، في العلامة <form> للمتصفح الطريقة التي يجب استخدامها لإرسال المعلومات التي تم جمعها في النموذج إلى عنوان URL المحدد بواسطة السمة action="URL". عندما لا يتم تحديد السمة method، يتم استخدام طريقة GET بشكل افتراضي.
  • السطر 18: نرى أن قيم النموذج سيتم إرسالها إلى عنوان URL [/First/Action06] عبر طلب HTTP POST؛
  • السطر 30: يجب أن يحتوي النموذج على زر [submit]. يؤدي هذا الزر إلى إرسال القيم المدخلة إلى عنوان URL المحدد بواسطة السمة [action] لعلامة <form>.

ما الذي سيرسله المتصفح بالضبط عندما ينقر المستخدم على زر [Submit]؟ تم شرح ذلك في القسم 2.5.3.1. دعونا نلخص ما قيل:


عنصر تحكم HTML


القيمة (القيم) المرئية


القيمة (القيم) المُرجعة

<input type="radio" value="Yes" name="R1"/>نعم
<input type="radio" name="R1" value="No" checked="checked"/>لا
R1=نعم
- قيمة سمة value الخاصة بزر الاختيار الذي حدده المستخدم.
<input type="checkbox" name="C1" value="one"/>1
<input type="checkbox" name="C2" value="two" checked="checked"/>2
<input type="checkbox" name="C3" value="three"/>3
C1=واحد
C2=اثنان
- قيم سمات القيمة لمربعات الاختيار التي حددها المستخدم
<input type="text" name="txtInput" size="20" value="بضع كلمات"/>
txtInput=برمجة الويب
- النص الذي كتبه المستخدم في حقل الإدخال. تم استبدال المسافات بعلامة +
<input type="password" name="txtMdp" size="20" value="aPassword"/>
txtPassword=thisIsSecret
- النص الذي يكتبه المستخدم في حقل الإدخال
<textarea rows="2" name="inputArea" cols="20">
السطر 1
السطر 2
السطر 3
</textarea>
حقل_الإدخال=أساسيات_الويب%0D%0A
برمجة+الويب
- نص كتبته المستخدم في حقل الإدخال. %OD%OA هي علامة نهاية السطر. تم استبدال المسافات بعلامة +
<select size="1" name="cmbValues">
<option value='1'>choice1</option>
<option selected="selected" value='2'>الخيار 2</option>
<option value='3'>choice3</option>
</select>
cmbValues=3
- السمة [value] للعنصر الذي اختاره المستخدم
<select size="3" name="lst1">
<option selected="selected" value='1'>list1</option>
<option value='2'>list2</option>
<option value='3'>list3</option>
<option value='4'>list4</option>
<option value='5'>القائمة 5</option>
</select>
lst1=3
- سمة [value] للعنصر الذي اختاره المستخدم
<select size="3" name="lst2" multiple="multiple">
<option selected="selected" value='1'>list1</option>
<option value='2'>list2</option>
<option selected="selected" value='3'>list3</option>
<option value='4'>list4</option>
<option value='5'>list5</option>
</select>
lst2=1
lst2=3
- سمات [value] للعناصر التي اختارها المستخدم
<input type="submit" value="إرسال" name="cmdSubmit"/>
 
cmdRenvoyer=إرسال
- سمة الاسم والقيمة للزر المستخدم لإرسال بيانات النموذج إلى الخادم
<input type="hidden" name="secret" value="aValue"/>
 
secret=aValue
- سمة القيمة للحقل المخفي

في النموذج الخاص بنا، لدينا علامتان يمكنهما إرسال قيمة:


    <select name="personneId">
...
</select>

و


<input name="valider" type="submit" value="Valider" />

إذا اختار المستخدم الشخص رقم 2، فسيتم إرسال القيم بالصيغة التالية:

personneId=2&valider=Valider

أسماء المعلمات هي سمات [name] للعلامات المشاركة في POST. بدون هذه السمة، لا ترسل العلامات أي قيمة. وبالتالي، في المثال أعلاه، يمكننا حذف السمة name="valider" من زر [submit]. القيمة المرسلة هي سمة [value] للزر. هنا، هذه المعلومات ليست ذات صلة بنا. في بعض الأحيان، تحتوي النماذج على عدة أزرار [submit]. في مثل هذه الحالات، من المهم معرفة الزر الذي تم النقر عليه. لذلك، سنقوم بتعيين السمة [name] للأزرار المختلفة.

تتكون علامة <select> من سلسلة من علامات <option>:


    <select name="personneId">
        <option value="1" >Pierre Martino</option>
        <option value="2" selected=&quot;selected&quot;>Pauline Pereiro</option>
        <option value="3" >Jacques Alfonso</option>
</select>

يتم إرسال قيمة السمة [value] للخيار المحدد. إذا كانت هذه السمة مفقودة، يتم إرسال النص المعروض بواسطة الخيار—على سبيل المثال، [Pierre Martino].

السلسلة

personneId=2&valider=Valider

إلى عنوان URL التالي [/First/Action06]:


    // Action06-POST
    [HttpPost]
    public ViewResult Action06(ActionModel06 modèle)
    {
      return View("Action06Post",modèle);
}

قد تتذكر أننا كان لدينا بالفعل إجراء [Action06]:


    // Action06-GET
    [HttpGet]
    public ViewResult Action06()
    {
      return View("Action06Get",new ViewModel05());
}

من الممكن وجود إجراءين يحملان نفس الاسم شريطة ألا يعالجا نفس طلبات HTTP:

  • [Action06] في السطر 3 تعالج طلب POST (السطر 2)؛
  • [Action06] في السطر c يعالج طلب GET (السطر b).

ستتلقى الإجراء [Action06] الذي يعالج طلب POST سلسلة المعلمات التالية:

personneId=2&valider=Valider

نحتاج إلى نموذج إجراء لتغليف هذه القيم. سيكون هذا هو النموذج [ActionModel06] التالي:


using System.ComponentModel.DataAnnotations;
namespace Exemple_03.Models
{
  public class ActionModel06
  {
    [Required(ErrorMessage = "Le paramètre [personneId] est requis")]
    public int PersonneId { get; set; }
 
    [Required(ErrorMessage = "Le paramètre [valider] est requis")]
    public string Valider { get; set; }
  }
}

تستقبل الإجراء [Action06] هذا النموذج وتمرره كما هو إلى عرض [Action06Post] التالي (السطر 5 من الإجراء):


@model Exemple_03.Models.ActionModel06
 
@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action06Post</title>
</head>
<body>
  <h3>Action06 - POST</h3>
  Valeurs postées :
  <ul>
    <li>ID de la personne sélectionnée : @Model.PersonneId</li>
    <li>Commande utilisée : @Model.Valider</li>
  </ul>
</body>
</html>

يتم عرض النموذج في السطرين 18 و 19.

لنلقِ نظرة على مثال:

في [1]، نختار الشخص الثالث الذي [Id] يساوي 3. في [2]، نرسل النموذج. في [3]، القيم المستلمة. في [4،5]، نرى أنه تم استدعاء نفس عنوان URL، أحدهما عبر GET [4]، والآخر عبر POST [5]. هذا غير مرئي في عنوان URL.

في العرض المعروض بعد POST، قد نرغب في الحصول على الاسم الأول واسم العائلة للشخص المحدد بدلاً من رقمه. لذلك يجب علينا تحديث عرض POST ونموذجه.

نقوم بإنشاء إجراء [Action07] للتعامل مع هذه الحالة. سيحتاج هذا الإجراء إلى استخدام جلسة عمل المستخدم لتخزين قائمة الأشخاص. وسنتبع النمط الذي تمت مناقشته في القسم 4.10، والذي يسمح لنا بتضمين بيانات من نطاقي [Application] و[Session] في نموذج الإجراء.

سيكون نموذج الجلسة هو الفئة [SessionModel] التالية:


namespace Exemple_03.Models
{
  public class SessionModel
  {
    public Personne2[] Personnes { get; set; }
  }
}
  • السطر 2: ستخزن الجلسة قائمة الأشخاص المعروضة في القائمة المنسدلة؛

نحتاج إلى ربط النوع السابق [SessionModel] بمربط سنسميه [SessionModelBinder]. سيكون هذا هو نفسه الموصوف في الصفحة 74:

Image


using System.Web.Mvc;
 
namespace Exemple_03.Infrastructure
{
  public class SessionModelBinder : IModelBinder
  {
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
      // render scope data [Session]
      return controllerContext.HttpContext.Session["data"];
    }
  }
}

يتم تعريف الارتباط بين نموذج [SessionModel] و [SessionModelBinder] الخاص به في [Global.asax]:


public class MvcApplication : System.Web.HttpApplication
  {
    protected void Application_Start()
    {
      ...
 
      // model binders
      ModelBinders.Binders.Add(typeof(SessionModel), new SessionModelBinder());
    }
    // Session
    public void Session_Start()
    {
      Session["data"] = new SessionModel();
    }
  }
  • السطر 8: يتم ربط النموذج بمُربطه في [Application_Start
  • السطر 13: تتم إضافة مثيل من النوع [SessionModel] إلى الجلسة المرتبطة بالمفتاح [data].

بمجرد الانتهاء من ذلك، يكون الإجراء [Action07] كما يلي:


    // Action07-GET
    [HttpGet]
    public ViewResult Action07(SessionModel session)
    {
      ViewModel05 modèleVue = new ViewModel05();
      session.Personnes= modèleVue.Personnes;
      return View("Action07Get", modèleVue);
}
  • السطر 3: تسترد الإجراء نوع [SessionModel]، أي بيانات نطاق [Session] المرتبطة بمفتاح [data
  • السطر 5: نقوم بإنشاء نموذج العرض؛
  • السطر 6: يتم وضع مصفوفة الأشخاص في الجلسة. سنحتاج إليها في الطلب التالي، وهو طلب POST. بروتوكول HTTP هو بروتوكول عديم الحالة. يجب استخدام جلسة للحفاظ على الحالة بين الطلبات. الجلسة خاصة بالمستخدم وتديرها خادم الويب؛
  • السطر 7: يتم عرض العرض [Action07Get.cshtml]. وهو كما يلي:

@model Exemple_03.Models.ViewModel05
@using Exemple_03.Models
...
<body>
  <h3>Action07 - GET</h3>
  <p>Choisissez une personne</p>
  <form method="post" action="/First/Action07">
....
  </form>
</body>
</html>

وهو مطابق لعرض [Action06Get.cshtml] الذي درسناه سابقًا. والفرق الرئيسي يكمن في السطر 7: عنوان URL الذي سيتم إرسال قيم النموذج إليه. وسيتم معالجتها بواسطة الإجراء [Action07] التالي:


    // Action07-POST
    [HttpPost]
    public ViewResult Action07(SessionModel session, ActionModel06 modèle)
    {
      Personne2 personne = session.Personnes.Where(p => p.Id == modèle.PersonneId).First<Personne2>();
      string strPersonne = string.Format("{0} {1}", personne.Prénom, personne.Nom);
      return View("Action07Post", (object)strPersonne);
}
  • السطر 3: القيم المُدرجة مُدمجة في نموذج الإجراء [ActionModel06] الذي سبق استخدامه (أدناه):

using System.ComponentModel.DataAnnotations;
namespace Exemple_03.Models
{
  public class ActionModel06
  {
    [Required(ErrorMessage = "Le paramètre [personneId] est requis")]
    public int PersonneId { get; set; }
 
    [Required(ErrorMessage = "Le paramètre [valider] est requis")]
    public string Valider { get; set; }
  }
}

  • السطر 3: المعلمة الأولى هي بيانات نطاق [Session] المرتبطة بمفتاح [data
  • السطر 5: يسترد استعلام LINQ الشخص الذي يحمل [Id] الذي تم نشره؛
  • السطر 6: نقوم بإنشاء السلسلة التي سيتم عرضها بواسطة عرض [Action07Post] (السطر 8)؛
  • السطر 7: لاستدعاء منشئ [View] الصحيح، يجب تحويل النوع [string] إلى [object].

طريقة العرض [Action07Post.cshtml] هي كما يلي:


@model string
 
@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action07-Post</title>
</head>
<body>
  <h3>Action07-POST</h3>
  Vous avez sélectionné [@Model].
</body>
</html>

  • السطر 1: النموذج من النوع [string
  • السطر 16: يتم عرض السلسلة.

فيما يلي مثال على التنفيذ:

Image

Image

5.6. نموذج – مثال كامل

في القسم 2.5.2.1، قمنا بفحص نموذج HTML التالي:

Image

سنقوم بفحص إجراء [Action08Get] الذي يعرض (GET) هذا النموذج وإجراء [Action08Post] الذي يعالج (POST) القيم التي أدخلها المستخدم. إعداد كلاسيكي.

سيكون نموذج العرض [1] أعلاه مثيلًا لفئة [ViewModel08]. ستعمل هذه الفئة ككل من:

  • نموذج العرض الناتج عن طلب GET على الإجراء [Action08Get
  • نموذج الإجراء [Action08Post] لطلب POST.

5.6.1. نموذج نطاق [Application]

سنفترض أن العناصر المعروضة بواسطة أزرار الاختيار ومربعات الاختيار والقوائم المختلفة هي بيانات نطاق [Application]. سيناريو شائع. تأتي هذه المعلومات من ملف تكوين أو قاعدة بيانات يتم الوصول إليها عند بدء تشغيل التطبيق في الأسلوب [Application_Start] في [Global.asax]. يتطور هذا الأسلوب على النحو التالي:


    protected void Application_Start()
    {
....
 
      // model binders
      ModelBinders.Binders.Add(typeof(SessionModel), new SessionModelBinder());
      ModelBinders.Binders.Add(typeof(ApplicationModel), new ApplicationModelBinder());
 
      // scope data [Application]
      Application["data"] = new ApplicationModel();
}

  • السطر 7: يرتبط النوع [ApplicationModel]، الذي سنصفه بعد قليل، بربط البيانات [ApplicationModelBinder] الذي قدمناه بالفعل في الصفحة 74؛
  • السطر 10: يتم تخزين مثيل من النوع [ApplicationModel] في قاموس التطبيق، مرتبط بالمفتاح [data].

تُستخدم فئة [ApplicationModel] لتغليف جميع البيانات ضمن نطاق [Application]. وهنا، ستقوم بتغليف البيانات التي يحتاج النموذج إلى عرضها:


namespace Exemple_03.Models
{
  public class ApplicationModel
  {
    // the collections to be displayed in the form
    public Item[] RadioButtonFieldItems { get; set; }
    public Item[] CheckBoxesFieldItems { get; set; }
    public Item[] DropDownListFieldItems { get; set; }
    public Item[] SimpleChoiceListFieldItems { get; set; }
    public Item[] MultipleChoiceListFieldItems { get; set; }
 
    // initializing fields and collections
    public ApplicationModel()
    {
      RadioButtonFieldItems = new Item[]{
        new Item {Value="1",Label="oui"},
        new Item {Value="2", Label="non"}
      };
      CheckBoxesFieldItems = new Item[]{
        new Item {Value="1",Label="1"},
        new Item {Value="2", Label="2"},
        new Item {Value="3", Label="3"}
      };
      DropDownListFieldItems = new Item[]{
        new Item {Value="1",Label="choix1"},
        new Item {Value="2", Label="choix2"},
        new Item {Value="3", Label="choix3"}
      };
      SimpleChoiceListFieldItems = new Item[]{
        new Item {Value="1",Label="liste1"},
        new Item {Value="2", Label="liste2"},
        new Item {Value="3", Label="liste3"},
        new Item {Value="4", Label="liste4"},
        new Item {Value="5", Label="liste5"}
      };
      MultipleChoiceListFieldItems = new Item[]{
        new Item {Value="1",Label="liste1"},
        new Item {Value="2", Label="liste2"},
        new Item {Value="3", Label="liste3"},
        new Item {Value="4", Label="liste4"},
        new Item {Value="5", Label="liste5"}
      };
    }
    // the collections item
    public class Item
    {
      public string Label { get; set; }
      public string Value { get; set; }
    }
 
  }
}

  • الأسطر 45–49: عنصر التحكم في النموذج الذي يمثل المجموعات المختلفة. [Label] هو النص الذي يعرضه عنصر التحكم في النموذج، و[Value] هي القيمة التي يرسلها هذا العنصر عند تحديده؛
  • السطر 6: المجموعة التي يعرضها زر الاختيار؛
  • السطر 7: المجموعة التي تعرضها مربعات الاختيار؛
  • السطر 8: المجموعة التي تعرضها القائمة المنسدلة؛
  • السطر 9: المجموعة التي تعرضها قائمة الاختيار الفردي؛
  • السطر 10: المجموعة التي تعرضها قائمة الاختيار المتعدد؛
  • الأسطر 13–43: يتم تهيئة هذه المجموعات بواسطة منشئ الفئة بدون معلمات.

ستملأ المجموعات المختلفة النموذج التالي:

5.6.2. نموذج الإجراء [Action08Get]

سيتم عرض النموذج السابق بواسطة الإجراء [Action08Get] التالي:


    // Action08-GET
    [HttpGet]
    public ViewResult Action08Get(ApplicationModel application)
    {
      ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
      return View("Formulaire", new ViewModel08(application));
}
  • السطر 2: [Action08Get] ستستجيب فقط لطلب [GET
  • السطر 3: يتلقى كمعلمة نموذج التطبيق الذي وصفناه للتو؛
  • السطر 5: يقوم بتهيئة البيانات في الحاوية الديناميكية [ViewBag
  • السطر 6: يعرض العرض [/First/Formulaire.cshtml] باستخدام نموذج [ViewModel08]. هذا النموذج هو النموذج المأخوذ من النموذج المقدم سابقًا. للقيام بذلك، نمرر نموذج التطبيق — الذي يحدد العناصر المراد عرضها — إلى المنشئ.

5.6.3. نموذج العرض [Form]

ستكون فئة [ViewModel08] هي نموذج النموذج. هذه الفئة هي كما يلي:


using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
using Exemple_03.Models;
 
namespace Exemple_03.Models
{
  public class ViewModel08
  {
    // input fields
    public string RadioButtonField { get; set; }
    public string[] CheckBoxesField { get; set; }
    public string TextField { get; set; }
    public string PasswordField { get; set; }
    public string TextAreaField { get; set; }
    public string DropDownListField { get; set; }
    public string SimpleChoiceListField { get; set; }
    public string[] MultipleChoiceListField { get; set; }
 
    // the collections to be displayed in the form
    public ApplicationModel.Item[] RadioButtonFieldItems { get; set; }
    public ApplicationModel.Item[] CheckBoxesFieldItems { get; set; }
    public ApplicationModel.Item[] DropDownListFieldItems { get; set; }
    public ApplicationModel.Item[] SimpleChoiceListFieldItems { get; set; }
    public ApplicationModel.Item[] MultipleChoiceListFieldItems { get; set; }
 
    // manufacturers
    public ViewModel08()
    {
    }
 
    public ViewModel08(ApplicationModel application)
    {
      // initialization collections
      RadioButtonFieldItems = application.RadioButtonFieldItems;
      CheckBoxesFieldItems = application.CheckBoxesFieldItems;
      DropDownListFieldItems = application.DropDownListFieldItems;
      SimpleChoiceListFieldItems = application.SimpleChoiceListFieldItems;
      MultipleChoiceListFieldItems = application.MultipleChoiceListFieldItems;
      // field initialization
      RadioButtonField = "2";
      CheckBoxesField = new string[] { "2" };
      TextField = "quelques mots";
      PasswordField = "secret";
      TextAreaField = "ligne1\nligne2";
      DropDownListField = "2";
      SimpleChoiceListField = "3";
      MultipleChoiceListField = new string[] { "1", "3" };
    }
  }
}

  • في النموذج، هناك نوعان من العناصر: العناصر المعروضة والعناصر التي يتم إدخالها؛
  • تحدد الأسطر 20–24 العناصر المراد عرضها. وهي عبارة عن المجموعات المختلفة للنموذج. وتوجد هذه المجموعات في نموذج التطبيق (الأسطر 34–38)؛
  • الأسطر 10-17: تحدد حقول الإدخال في النموذج؛
  • السطر 10: [RadioButtonField] سيسترد القيمة التي تم إرسالها بواسطة الأسطر التالية من النموذج:


        <!-- radio buttons -->
        <tr>
          <td>Etes-vous marié(e)</td>
          <td>
<input type="radio" name="RadioButtonField" value="1" />oui              
<input type="radio" name="RadioButtonField" value="2" checked=&quot;checked&quot;/>non              
          </td>
</tr>

لاحظ في السطرين 5 و 6 أن السمة [name] لزرَي الاختيار هي اسم الخاصية التي سيتم تهيئتها. في البيانات المرسلة، ستجد سلسلة على الشكل التالي:


param1=val1&RadioButtonField=2&param2=val2

إذا كان المستخدم قد حدد الخيار المسمى [no]. في الواقع، السمة [value] للخيار المحدد هي التي يتم إرسالها.

  • السطر 11: ستسترد [CheckBoxesField] القيم التي تم إرسالها بواسطة الأسطر التالية من النموذج:


        <!-- checkboxes -->
        <tr>
          <td>Cases à cocher</td>
          <td>
<input type="checkbox" name="CheckBoxesField" value="1" />1              
<input type="checkbox" name="CheckBoxesField" value="2" checked=&quot;checked&quot;/>2              
<input type="checkbox" name="CheckBoxesField" value="3" />3              
</td>

لاحظ في السطرين 5 و 6 أن السمة [name] لمربعات الاختيار هي اسم الخاصية التي سيتم تهيئتها. في البيانات المرسلة، ستجد سلسلة بالصيغة التالية:


param1=val1&CheckBoxesField=2&CheckBoxesField=3&param2=val2

إذا كان المستخدم قد حدد مربعي الاختيار المسميين [2] و [3]. يتم نشر السمة [value] للخيارات المحددة. نظرًا لإمكانية نشر معلمات متعددة تحمل الاسم نفسه، فإن [CheckBoxesField] عبارة عن مصفوفة من القيم بدلاً من قيمة واحدة. إذا لم يتم تحديد أي مربعات اختيار، فستكون المعلمة [CheckBoxesField] غائبة عن السلسلة المنشورة، ولن يتم تهيئة خاصية النموذج التي تحمل الاسم نفسه. قد يشكل هذا مشكلة، كما سنرى.

  • السطر 12: ستسترد [TextField] القيمة التي تم إرسالها بواسطة الأسطر التالية من النموذج:


          <!-- single-line text input field -->
          <tr>
            <td>Champ de saisie</td>
            <td>
              <input type="text" name="TextField" value="quelques mots" size="30" />
            </td>
</tr>

السطر 5: السمة [name] لحقل الإدخال هي اسم الخاصية التي سيتم تهيئتها. في البيانات المرسلة، ستجد سلسلة نصية بالصيغة التالية:


param1=val1&TextField=abcdef&param2=val2

إذا أدخل المستخدم [abcdef] في حقل الإدخال.

  • السطر 13: سيسترد [PasswordField] القيمة التي تم إرسالها بواسطة الأسطر التالية من النموذج:


        <!-- password entry field -->
        <tr>
          <td>Mot de passe</td>
          <td>
            <input type="password" name="PasswordField" value="secret" size="30" />
          </td>
</tr>

السطر 5: السمة [name] لحقل الإدخال هي اسم الخاصية التي سيتم تهيئتها. في البيانات المرسلة، ستجد سلسلة نصية بالصيغة التالية:


param1=val1&PasswordField=abcdef&param2=val2

إذا أدخل المستخدم [abcdef] في حقل الإدخال.

  • السطر 14: سيسترد [TextAreaField] القيمة التي تم إرسالها بواسطة الأسطر التالية من النموذج:


        <!-- le champ de saisie texte multilignes -->
        <tr>
          <td>Boîte de saisie</td>
          <td>
            <textarea name="TextAreaField" cols="40" rows="3">ligne1
ligne2</textarea>
          </td>
</tr>

السطر 5: السمة [name] لحقل الإدخال هي اسم الخاصية التي سيتم تهيئتها. في البيانات المرسلة، ستجد سلسلة نصية بالصيغة التالية:


param1=val1&TextAreaField=abcdef%0D%OAhijk&param2=val2

إذا أدخل المستخدم [abcdef] متبوعًا بفاصل أسطر و[ijk] في حقل الإدخال.

  • السطر 15: سيسترد [DropDownListField] القيمة التي تم إرسالها بواسطة الأسطر التالية من النموذج:


        <!-- the drop-down list -->
        <tr>
          <td>Liste déroulante</td>
          <td>
            <select name="DropDownListField">
<option value="1" >choix1</option>
<option value="2" selected=&quot;selected&quot;>choix2</option>
<option value="3" >choix3</option>
            </select>
</tr>

السطر 5: السمة [name] لعلامة <select> هي اسم الخاصية التي سيتم تهيئتها. في البيانات المرسلة، ستجد سلسلة نصية بالصيغة التالية:


param1=val1&DropDownListField=1&param2=val2

إذا كان المستخدم قد حدد الخيار [choice1]. يتم إرسال السمة [value] للخيار المحدد.

  • السطر 16: سيقوم [SingleChoiceListField] باسترداد القيمة التي تم إرسالها بواسطة الأسطر التالية من النموذج:


        <!-- single-choice list -->
        <tr>
          <td>Liste à choix unique</td>
          <td>
            <select name="SimpleChoiceListField" size="3">
<option value="1" >liste1</option>
<option value="2" >liste2</option>
<option value="3" selected=&quot;selected&quot;>liste3</option>
<option value="4" >liste4</option>
<option value="5" >liste5</option>
 
            </select>
</tr>

السطر 5: السمة [name] لعلامة <select> هي اسم الخاصية التي سيتم تهيئتها. تضمن السمة [size="3"] عدم وجود قائمة منسدلة. في البيانات المنشورة، سنجد سلسلة بالصيغة التالية:


param1=val1&SimpleChoiceListField=3&param2=val2

إذا كان المستخدم قد حدد الخيار [liste3]. يتم إرسال السمة [value] للخيار المحدد. قد لا تظهر المعلمة [SingleChoiceListField] في السلسلة المرسلة إذا لم يتم تحديد أي عنصر.

  • السطر 17: سيقوم [MultipleChoiceListField] باسترداد القيم التي تم إرسالها بواسطة الأسطر التالية من النموذج:


        <!-- multiple choice list -->
        <tr>
          <td>Liste à choix multiple</td>
          <td>
            <select name="MultipleChoiceListField" size="3" multiple="multiple">
<option value="1" selected=&quot;selected&quot;>liste1</option>
<option value="2" >liste2</option>
<option value="3" selected=&quot;selected&quot;>liste3</option>
<option value="4" >liste4</option>
<option value="5" >liste5</option>
            </select>
</tr>

السطر 5: السمة [name] لعلامة <select> هي اسم الخاصية التي سيتم تهيئتها. تضمن السمة [size="3"] عدم وجود قائمة منسدلة، وتسمح السمة [multiple] للمستخدم بتحديد عناصر متعددة عن طريق الضغط باستمرار على مفتاح [Ctrl]. في البيانات المنشورة، ستجد سلسلة بالصيغة التالية:


param1=val1&MultipleChoiceListField=1&MultipleChoiceListField=3&param2=val2

إذا كان المستخدم قد حدد الخيارين [list1] و [list3]. يتم نشر السمة [value] للخيارات المحددة. نظرًا لإمكانية نشر معلمات متعددة تحمل الاسم نفسه، فإن [MultipleChoiceListField] عبارة عن مصفوفة من القيم بدلاً من قيمة واحدة. إذا لم يتم تحديد أي خانة اختيار، فستكون المعلمة [MultipleChoiceListField] غائبة عن السلسلة المنشورة، ولن يتم تهيئة خاصية النموذج التي تحمل الاسم نفسه.

ستتلقى حقول الإدخال المختلفة التي تم عرضها سابقًا القيم التي تم إرسالها بواسطة النموذج. يمكن أيضًا تهيئتها قبل إرسال النموذج. وهذا ما تم القيام به هنا:


      // initialisation champs
      RadioButtonField = "2";
      CheckBoxesField = new string[] { "2" };
      TextField = "quelques mots";
      PasswordField = "secret";
      TextAreaField = "ligne1\nligne2";
      DropDownListField = "2";
      SimpleChoiceListField = "3";
MultipleChoiceListField = new string[] { "1", "3" };

إذا تم الحصول على هذه القيم بعد طلب POST من النموذج، فهذا يعني أن المستخدم قد قام بما يلي:

  • السطر 2: حدد خيار [لا] من أزرار الاختيار؛
  • السطر 3: حدد الخيار [2] في مربعات الاختيار؛
  • السطر 4: كتب [بضع كلمات] في حقل الإدخال؛
  • السطر 5: أدخل [secret] ككلمة مرور؛
  • السطر 6: كتابة [السطر 1\nالسطر 2] في حقل الإدخال متعدد الأسطر؛
  • السطر 7: حدد الخيار [option2] من القائمة المنسدلة؛
  • السطر 8: حدد الخيار [list3] من قائمة الاختيار الفردي؛
  • السطر 9: تم تحديد الخيارين [list1] و [list3] من القائمة متعددة الخيارات؛

سنمضي قدماً كما لو تم إرسال طلب POST وكأننا نريد إرجاع النموذج تماماً كما تم إدخاله. وهذا هو بالضبط ما يحدث عندما يتم إرجاع نموذج غير صحيح إلى المستخدم. يتم إرجاع النموذج تماماً كما تم إدخاله.

5.6.4. طريقة العرض [Form]

تعرض طريقة العرض [/First/Formulaire.cshtml] النموذج:


@model Exemple_03.Models.ViewModel08
@using Exemple_03.Models
@{
  Layout = null;
}
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Formulaire</title>
</head>
<body>
  <form method="post" action="Action08Post">
    <h2>Formulaire ASP.NET MVC</h2>
    <h3>Affiché par : @ViewBag.info</h3>
    <table>
      <thead></thead>
      <tbody>
        <!-- radio buttons -->
        <tr>
          <td>Etes-vous marié(e)</td>
          <td>
            @foreach (ApplicationModel.Item item in @Model.RadioButtonFieldItems)
            {
              string strChecked = item.Value == @Model.RadioButtonField ? "checked=\"checked\"" : "";
              <input type="radio" name="RadioButtonField" value="@item.Value" @strChecked/>@item.Label
              <text/>
            }
          </td>
        </tr>
...
      </tbody>
    </table>
    <input type="submit" value="Valider" />
  </form>
</body>
</html>
  • السطر 1: [ViewModel08] هو نموذج النموذج؛
  • السطر 12: علامة <form> للنموذج. سيتم إرسال هذا النموذج باستخدام طريقة [POST] (سمة الطريقة) إلى عنوان URL [/First/Action08Post] (سمة الإجراء
  • السطر 33: زر [submit] المستخدم لإرسال النموذج؛
  • الأسطر 22–27: عرض أزرار الاختيار:

Image

  • السطر 22: نقوم بالتكرار عبر المجموعة المعروضة بواسطة زر الاختيار؛
  • السطر 24: يجب تحديد الزر الذي تتوافق سمة [value] الخاصة به مع قيمة الخاصية [RadioButtonField]. للقيام بذلك، يجب أن يحتوي على السمة [checked="checked"]؛
  • السطر 25: ينشئ العلامة <input type="radio"> بالقيمة [@item.Value] والتسمية [@item.Label
  • السطر 26: العلامة <text/> ليست علامة HTML معترف بها. وهي موجودة من أجل [Razor]. عند مواجهتها، سيقوم [Razor] بإنشاء فاصل أسطر. لا يؤثر هذا على النموذج المعروض ولكنه يؤثر على كود HTML الذي تم إنشاؤه. تصبح علامات <input type="radio"> بعد ذلك في سطرين مختلفين بدلاً من أن تكون في نفس السطر. وهذا يجعل الكود أكثر قابلية للقراءة عند عرض كود المصدر للصفحة المعروضة في المتصفح؛

دعونا نراجع العناصر الأخرى للعرض:


        <!-- checkboxes -->
        <tr>
          <td>Cases à cocher</td>
          <td>
            @{
              foreach (ApplicationModel.Item item in @Model.CheckBoxesFieldItems)
              {
                string strChecked = @Model.CheckBoxesField.Contains(item.Value) ? "checked=\"checked\"" : "";
              <input type="checkbox" name="CheckBoxesField" value="@item.Value" @strChecked/>@item.Label
              <text/>
              }
            }
</td>
  • السطر 6: نقوم بالتكرار عبر المجموعة المعروضة بواسطة مربعات الاختيار؛
  • السطر 8: يجب تحديد مربع الاختيار الذي تكون سمة [value] الخاصة به إحدى قيم الخاصية [CheckBoxesField]. للقيام بذلك، يجب أن يحتوي على السمة [checked="checked"]. نستخدم تعبير LINQ لتحديد ما إذا كانت القيمة موجودة في المصفوفة؛
  • السطر 25: يُنشئ العلامة <input type="checkbox"> بالقيمة [@item.Value] والتسمية [@item.Label

<!-- single-line text input field -->
          <tr>
            <td>Champ de saisie</td>
            <td>
              <input type="text" name="TextField" value="@Model.TextField" size="30" />
            </td>
          </tr>
        <!-- password entry field -->
        <tr>
          <td>Mot de passe</td>
          <td>
            <input type="password" name="PasswordField" value="@Model.PasswordField" size="30" />
          </td>
        </tr>
        <!-- multiline text input field -->
        <tr>
          <td>Boîte de saisie</td>
          <td>
            <textarea name="TextAreaField" cols="40" rows="3">@Model.TextAreaField</textarea>
          </td>
        </tr>
  • السطران 5 و 12: نعيّن قيمة النموذج إلى سمة [value] للعلامة؛
  • السطر 19: كما هو مذكور أعلاه، ولكن باستخدام صيغة مختلفة.

        <!-- the drop-down list -->
        <tr>
          <td>Liste déroulante</td>
          <td>
            <select name="DropDownListField">
              @{
                foreach (ApplicationModel.Item item in @Model.DropDownListFieldItems)
                {
                  string strChecked = item.Value == @Model.DropDownListField ? "selected=\"selected\"" : "";
                <option value="@item.Value" @strChecked>@item.Label</option>
                }
              }
            </select>
</tr>
  • السطر 7: نقوم بالتكرار عبر المجموعة المعروضة في القائمة المنسدلة؛
  • السطر 9: يجب بعد ذلك تحديد خيار تتطابق سمة [value] الخاصة به مع قيمة الخاصية [DropDownListField]. للقيام بذلك، يجب أن يحتوي على السمة [selected="selected"]؛
  • السطر 25: يُنشئ علامة <option value="value">label</option> بالقيمة [@item.Value] والتسمية [@item.Label

        <!-- single-choice list -->
        <tr>
          <td>Liste à choix unique</td>
          <td>
            <select name="SimpleChoiceListField" size="3">
              @{
                foreach (ApplicationModel.Item item in @Model.SimpleChoiceListFieldItems)
                {
                  string strChecked = item.Value == @Model.SimpleChoiceListField ? "selected=\"selected\"" : "";
                <option value="@item.Value" @strChecked>@item.Label</option>
                }
              }
            </select>
</tr>

التفسير هو نفسه كما في القائمة المنسدلة.


        <!-- multiple choice list -->
        <tr>
          <td>Liste à choix multiple</td>
          <td>
            <select name="MultipleChoiceListField" size="3" multiple="multiple">
              @{
                foreach (ApplicationModel.Item item in @Model.MultipleChoiceListFieldItems)
                {
                  string strChecked = @Model.MultipleChoiceListField.Contains(item.Value) ? "selected=\"selected\"" : "";
                <option value="@item.Value" @strChecked>@item.Label</option>
                }
              }
            </select>
</tr>
  • السطر 7: نقوم بالتكرار عبر المجموعة المعروضة في القائمة؛
  • السطر 9: يجب تحديد خيار يكون سمة [value] الخاصة به إحدى قيم الخاصية [MultipleChoiceListField]. للقيام بذلك، يجب أن يحتوي على السمة [selected="selected"]. نستخدم تعبير LINQ لتحديد ما إذا كانت القيمة موجودة في مصفوفة؛
  • السطر 10: إنشاء علامة <option value="value">label</option> مع [@item.Value] كقيمة و[@item.Label] كملصق؛

5.6.5. معالجة نموذج POST

لقد رأينا أن النموذج سيتم إرساله إلى الإجراء [Action08Post]:


  <form method="post" action="Action08Post">

الإجراء [Action08Post] هو كما يلي:


    // Action08-POST
    [HttpPost]
    public ViewResult Action08Post(ApplicationModel application, FormCollection posted)
    {
      ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
      ViewModel08 modèle = new ViewModel08(application);
      TryUpdateModel(modèle,posted);
      return View("Formulaire", modèle);
}
  • السطر 3: يتم تمرير نموذج التطبيق كمعلمة، جنبًا إلى جنب مع القيم المرسلة. وتكون هذه القيم متاحة في نوع [FormCollection]. يتم الحصول على قيمة المعلمة [RadioButtonField] المرسلة باستخدام التعبير posted["RadioButtonField"]. وهذا يعيد سلسلة أو مؤشر null. إذا كتبت posted["CheckBoxesField"]، فستحصل على مصفوفة من السلاسل أو مؤشر null؛
  • فلماذا لا تكتب:

public ViewResult Action08Post(ApplicationModel application, ViewModel08 posted)

هناك سببان:

  • الأول هو أن إطار العمل سيقوم بإنشاء مثيل لنموذج [ViewModel08] باستخدام منشئ بدون معلمات، مما سيؤدي إلى عدم تهيئة مجموعات النموذج؛
  • والثاني هو أننا نريد التحكم في ما يدخل في النموذج. نحن نعلم أن هناك أربعة مصادر محتملة للنموذج: معلمات طلب GET أو POST، والمسار المستخدم، وتلك الموجودة في ملف تم تحميله. هنا، نريد تهيئة النموذج فقط بالقيم المنشورة.
  • السطر 6: نقوم بإنشاء مثيل للنموذج باستخدام المنشئ الصحيح؛
  • السطر 7: نقوم بتهيئته بالقيم المرسلة. بعد هذه العملية، يتوافق النموذج مع مدخلات المستخدم؛
  • السطر 8: نعرض النموذج مرة أخرى. سيراه المستخدم تمامًا كما تم إدخاله.

لنلقِ نظرة على مثال:

في [2]، تعكس نتيجة [POST] بدقة ما تم إدخاله في [1].

5.6.6. معالجة أخطاء POST

ذكرنا أنه إذا لم يتم تحديد أو اختيار أي قيمة لحقول [CheckBoxesField، SimpleChoiceListField، MultipleChoiceListField]، فإن المعلمات المقابلة لم تكن جزءًا من السلسلة المرسلة، وبالتالي لم يتم تهيئة خصائص النموذج التي تحمل الأسماء نفسها.

لنلقِ نظرة على المثال التالي:

  • في [1]، لم يتم تحديد أي مربع اختيار؛
  • في [2]، يعرض [POST] مربع اختيار محددًا.

والتفسير هو كما يلي:

  • نظرًا لعدم تحديد أي مربعات اختيار، لا يتم تضمين المعلمة [CheckBoxesField] في القيم المرسلة؛
  • يتم تنفيذ الإجراء [Action08Post] على النحو التالي:

    [HttpPost]
    public ViewResult Action08Post(ApplicationModel application, FormCollection posted)
    {
      ViewBag.info = ...
      ViewModel08 modèle = new ViewModel08(application);
      TryUpdateModel(modèle,posted);
      return View("Formulaire", modèle);
}
  • السطر 5: يتم إنشاء مثيل لنموذج النموذج. ومع ذلك، فإن المنشئ المستخدم يعين المصفوفة ["2"] إلى الخاصية [CheckBoxesField
  • السطر 6: يتم حفظ القيم المرسلة في النموذج. ونظرًا لأن المعلمة [CheckBoxesField] ليست جزءًا من القيم المرسلة، لا يتم تعيين قيمة للخاصية التي تحمل الاسم نفسه. وبالتالي، تحتفظ بقيمتها ["2"]، مما يعني أنه عند عرض النموذج، يتم تحديد مربع الاختيار رقم 2 في حين أنه لا ينبغي ذلك.

يمكن حل هذه المشكلة بعدة طرق. نختار حلها في كود الإجراء [Action08Post]:


// Action08-POST
    [HttpPost]
    public ViewResult Action08Post(ApplicationModel application, FormCollection posted)
    {
      ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
      ViewModel08 modèle = new ViewModel08(application);
      TryUpdateModel(modèle,posted);
      // processing of non-posted values
      if (posted["CheckBoxesField"] == null)
      {
        modèle.CheckBoxesField = new string[] { };
      }
      if (posted["SimpleChoiceListField"] == null)
      {
        modèle.SimpleChoiceListField = "";
      }
      if (posted["MultipleChoiceListField"] == null)
      {
        modèle.MultipleChoiceListField = new string[] { };
      }
      // form display
      return View("Formulaire", modèle);
    }
  • الأسطر 9–20: نتحقق مما إذا كانت معلمات معينة قد تم إرسالها أم لا. إذا لم يتم إرسالها، نقوم بتهيئتها بالقيمة المطابقة لعدم إدخال المستخدم لأي شيء. لم يتم إجراء هذا الفحص للقائمة المنسدلة، التي يكون فيها عنصر محدد دائمًا، على عكس القوائم الأخرى.

ندعو القراء إلى تجربة هذه النسخة الجديدة.

5.7. استخدام طرق متخصصة في إنشاء النماذج

5.7.1. النموذج الجديد

نقوم بإنشاء نموذج جديد [Form2.cshtml] سيقوم بإنشاء نموذج مطابق للنموذج السابق:

دعونا نراجع الكود المستخدم لإنشاء القائمة المنسدلة للنموذج:


        <!-- the drop-down list -->
        <tr>
          <td>Liste déroulante</td>
          <td>
            <select name="DropDownListField">
              @{
                foreach (ApplicationModel.Item item in @Model.DropDownListFieldItems)
                {
                  string strChecked = item.Value == @Model.DropDownListField ? "selected=\"selected\"" : "";
                <option value="@item.Value" @strChecked>@item.Label</option>
                }
              }
            </select>
</tr>

يحتوي هذا الرمز على عيبين:

  • أهمها أن طبيعة المكون — في هذه الحالة، قائمة منسدلة — تضيع بسبب تعقيد الكود؛
  • السطر 5: إذا أخطأت في اسم خاصية النموذج التي سيتم استخدامها كسمة [name]، فلن تلاحظ ذلك حتى وقت التشغيل.

يوفر ASP.NET MVC طرقًا متخصصة تسمى [مساعدات HTML] والتي، كما يوحي اسمها، مصممة لتسهيل إنشاء HTML، خاصةً للنماذج. باستخدام هذه الفئات، تُكتب القائمة المنسدلة الموضحة أعلاه على النحو التالي:


        <!-- the drop-down list -->
        <tr>
          <td>Liste déroulante</td>
          <td>@Html.DropDownListFor(m => m.DropDownListField,
           new SelectList(@Model.DropDownListFieldItems, "Value", "Label"))
          </td>
</tr>

يتم إنشاء القائمة المنسدلة بواسطة السطرين 4 و 5. ويكون الكود أقل تعقيدًا بشكل ملحوظ. وفيما يلي كود HTML الذي تم إنشاؤه للقائمة المنسدلة:


        <!-- the drop-down list -->
        <tr>
          <td>Liste déroulante</td>
          <td><select id="DropDownListField" name="DropDownListField"><option value="1">choix1</option>
<option selected="selected" value="2">choix2</option>
<option value="3">choix3</option>
</select></td>
</tr>
  • السطر 4: السمة [name] صحيحة؛
  • الأسطر 4-6: تم إنشاء الخيارات بشكل صحيح، وتم تحديد الخيار الصحيح.

لنعد إلى الكود الذي أنشأ أسطر HTML هذه:


@Html.DropDownListFor(m => m.DropDownListField, new SelectList(@Model.DropDownListFieldItems, "Value", "Label"))
  • المعلمة الأولى هي دالة لامدا (هذا هو اسمها)، حيث يمثل `m` نموذج العرض و`m.DropDownListField` هي خاصية لهذا النموذج. سيستخدم مولد كود HTML اسم هذه الخاصية لإنشاء سمات [id] و [name] لعنصر [select] الذي سيتم إنشاؤه. إذا تم استخدام خاصية غير موجودة، فسيحدث خطأ في وقت التحويل البرمجي بدلاً من وقت التشغيل. وهذا يمثل تحسناً مقارنة بالحل السابق، حيث كانت أخطاء التسمية تُكتشف فقط في وقت التشغيل؛
  • يُستخدم المعامل الثاني لتحديد مجموعة العناصر التي ستملأ القائمة المنسدلة. تتيح لك فئة [SelectList] إنشاء هذه المجموعة:
    • المعلمة الأولى هي أي مجموعة من العناصر. لدينا هنا مجموعة من النوع [Item
    • المعلمة الثانية هي خاصية العناصر التي ستوفر قيمة علامة <option>. هنا، هي خاصية [Value] لفئة [Item
    • المعلمة الثالثة هي خاصية العناصر التي ستوفر التسمية لعلامة <option>. هنا، هي خاصية [Label] لفئة [Item
  • لتحديد الخيار الذي يجب تحديده (السمة المحددة)، يقوم إطار العمل بما نقوم به: فهو يقارن قيمة الخيار بالقيمة الحالية لخاصية [DropDownListField].

الآن دعونا نلقي نظرة على الطرق الأخرى التي يمكننا استخدامها:

أزرار الاختيار

الرمز الجديد هو كما يلي:


        <!-- les boutons radio -->
        <tr>
          <td>Etes-vous marié(e)</td>
          <td>
            @{
    foreach (ApplicationModel.Item item in @Model.RadioButtonFieldItems)
    {
              @Html.RadioButtonFor(m => m.RadioButtonField, @item.Value)@item.Label
              <text/>
    }
            }
          </td>
</tr>

كود HTML الذي تم إنشاؤه هو كما يلي:


        <!-- radio buttons -->
        <tr>
          <td>Etes-vous marié(e)</td>
          <td>
<input id="RadioButtonField" name="RadioButtonField" type="radio" value="1" />oui              
<input checked="checked" id="RadioButtonField" name="RadioButtonField" type="radio" value="2" />non              
          </td>
</tr>

الطريقة المستخدمة هي [Html.RadioButtonFor]:

@Html.RadioButtonFor(m => m.RadioButtonField, @item.Value)
  • المعلمة الأولى هي خاصية النموذج التي سيتم ربطها بزر الاختيار (السمة [name])؛
  • المعلمة الثانية هي القيمة التي سيتم تعيينها لزر الاختيار (السمة [value]).

مربعات الاختيار

يتغير الكود على النحو التالي:


        <!-- les cases à cocher -->
        <tr>
          <td>Cases à cocher</td>
          <td>
            @{
              @Html.CheckBoxFor(m=>m.CheckBoxField1) @Model.CheckBoxesFieldItems[0].Label
              @Html.CheckBoxFor(m=>m.CheckBoxField2) @Model.CheckBoxesFieldItems[1].Label
              @Html.CheckBoxFor(m=>m.CheckBoxField3) @Model.CheckBoxesFieldItems[2].Label
            }
</td>

الطريقة المستخدمة لإنشاء مربعات الاختيار هي [Html.CheckBoxFor]:

Html.CheckBoxFor(m=>m.Propriété)

المعلمة هي الخاصية المنطقية للنموذج التي سيتم ربطها بمربع الاختيار. إذا كانت [Property=true]، فسيتم تحديد مربع الاختيار. إذا كانت [Property=false]، فلن يتم تحديد مربع الاختيار. في جميع الحالات، يتم تعيين السمة [value] على true. رمز HTML الذي تم إنشاؤه هو كما يلي:


<input id="Propriété" name="Propriété" type="checkbox" value="true" />
<input name="Propriété" type="hidden" value="false" />
  • السطر 1: مربع الاختيار مع السمة [value="true"]؛
  • السطر 2: حقل مخفي (type=hidden) يحمل نفس اسم مربع الاختيار [Property]، مع السمة [value="false"]. لماذا توجد علامتا [input] تحملان نفس الاسم؟ هناك حالتان:
  • مربع الاختيار في السطر 1 محدد. عندئذٍ تكون سلسلة المعلمات المرسلة هي Property=true&Property=false (السطران 1 و 2). وبما أن الخاصية [Property] تتوقع قيمة واحدة فقط، فقد يعتقد المرء أن الإطار يعين القيمة [true] إلى [Property]. وسيحتاج ببساطة إلى إجراء عملية "أو" منطقية بين القيم المستلمة لتحقيق ذلك؛
  • مربع الاختيار في السطر 1 غير محدد. في هذه الحالة، تكون سلسلة المعلمات المرسلة هي Property=false (السطر 2 فقط)، ويتم تعيين القيمة [false] للخاصية [Property]، وهو الأمر الصحيح (لم يتم تحديد مربع الاختيار).

حقل إدخال من سطر واحد

الرمز الجديد هو كما يلي:


          <!-- le champ de saisie texte monoligne -->
          <tr>
            <td>Champ de saisie</td>
            <td>
              @Html.TextBoxFor(m => m.TextField, new { size = "30" })
            </td>
</tr>

كود HTML الذي تم إنشاؤه هو كما يلي:


          <!-- single-line text input field -->
          <tr>
            <td>Champ de saisie</td>
            <td>
              <input id="TextField" name="TextField" size="30" type="text" value="quelques mots" />
            </td>
</tr>

الطريقة المستخدمة هي كما يلي:


@Html.TextBoxFor(m => m.TextField, new { size = "30" })

  • تحدد المعلمة الأولى خاصية النموذج المرتبطة بحقل الإدخال. سيتم استخدام اسم الخاصية في سمات [name] و [id] لعلامة <input> التي تم إنشاؤها، وسيتم تعيين قيمتها إلى السمة [value
  • المعلمة الثانية هي فئة مجهولة تحدد بعض سمات علامة HTML التي تم إنشاؤها، وهي في هذه الحالة السمة [size].

حقل إدخال كلمة المرور

الرمز الجديد هو كما يلي:


        <!-- le champ de saisie d'un mot de passe -->
        <tr>
          <td>Mot de passe</td>
          <td>
            @Html.PasswordFor(m => m.PasswordField, new { size = "15" })
          </td>
</tr>

الرمز HTML الذي تم إنشاؤه هو كما يلي:


        <!-- password entry field -->
        <tr>
          <td>Mot de passe</td>
          <td>
            <input id="PasswordField" name="PasswordField" size="15" type="password" />
          </td>
</tr>

الطريقة المستخدمة هي كما يلي:


@Html.PasswordFor(m => m.PasswordField, new { size = "15" })

السلوك مشابه لسلوك الطريقة [Html.TextBoxFor].

حقل إدخال متعدد الأسطر

الرمز الجديد هو كما يلي:


        <!-- le champ de saisie texte multilignes -->
        <tr>
          <td>Boîte de saisie</td>
          <td>
            @Html.TextAreaFor(m => m.TextAreaField, new { cols = "30", rows = "5" })
          </td>
</tr>

فيما يلي كود HTML الذي تم إنشاؤه:


        <!-- le champ de saisie texte multilignes -->
        <tr>
          <td>Boîte de saisie</td>
          <td>
            <textarea cols="30" id="TextAreaField" name="TextAreaField" rows="5">
ligne1
ligne2</textarea>
          </td>
</tr>

الطريقة المستخدمة هي كما يلي:


@Html.TextAreaFor(m => m.TextAreaField, new { cols = "30", rows = "5" })

السلوك مشابه لسلوك طريقة [Html.TextBoxFor].

قائمة الاختيار الفردي

الرمز الجديد هو كما يلي:


        <!-- single-choice list -->
        <tr>
          <td>Liste à choix unique</td>
          <td>
          @Html.DropDownListFor(m => m.SimpleChoiceListField, new SelectList(@Model.SimpleChoiceListFieldItems, "Value", "Label"), new { size = "3" })
</tr>

والكود HTML الذي تم إنشاؤه هو كما يلي:


        <!-- single-choice list -->
        <tr>
          <td>Liste à choix unique</td>
          <td>
          <select id="SimpleChoiceListField" name="SimpleChoiceListField" size="3">
<option value="1">liste1</option>
<option value="2">liste2</option>
<option selected="selected" value="3">liste3</option>
<option value="4">liste4</option>
<option value="5">liste5</option>
</select>
</tr>

لقد تناولنا بالفعل طريقة [Html.DropDownListFor]. والفرق الوحيد هنا هو المعلمة الثالثة، التي تُستخدم لتحديد سمة [size] بخلاف 1. وهذه الميزة هي التي تحول القائمة المنسدلة [size=1] إلى قائمة بسيطة.

قائمة التحديد المتعدد

الرمز الجديد هو كما يلي:


        <!-- multiple choice list -->
        <tr>
          <td>Liste à choix multiple</td>
          <td>
          @Html.ListBoxFor(m => m.MultipleChoiceListField, new SelectList(@Model.MultipleChoiceListFieldItems, "Value", "Label"), new { size = "5" })
</tr>

والكود HTML الذي تم إنشاؤه هو كما يلي:


        <!-- multiple choice list -->
        <tr>
          <td>Liste à choix multiple</td>
          <td>
          <select id="MultipleChoiceListField" multiple="multiple" name="MultipleChoiceListField" size="5">
<option selected="selected" value="1">liste1</option>
<option value="2">liste2</option>
<option selected="selected" value="3">liste3</option>
<option value="4">liste4</option>
<option value="5">liste5</option>
</select>
</tr>

الطريقة


@Html.ListBoxFor(m => m.MultipleChoiceListField, new SelectList(@Model.MultipleChoiceListFieldItems, "Value", "Label"), new { size = "5" })

تعمل مثل طريقة [Html.DropDownListFor]، باستثناء أنها تنشئ قائمة متعددة الاختيارات. الخيارات المحددة هي تلك التي توجد قيمتها (سمة value) في صفيف [MultipleChoiceListField].

يمكن أيضًا إنشاء علامة <form> باستخدام طريقة:


  @using (Html.BeginForm("Action09Post", "First"))
  {
...
  }

فيما يلي كود HTML الذي تم إنشاؤه:


<form action="/First/Action09Post" method="post">    
    ...
</form>

الطريقة


Html.BeginForm("Action09Post", "First")

تأخذ اسم الإجراء كمعلمة أولى واسم وحدة التحكم كمعلمة ثانية.

5.7.2. الإجراءات والنموذج

سيتم عرض النموذج بواسطة الإجراء [Action09Get] التالي:


    // Action09-GET
    [HttpGet]
    public ViewResult Action09Get(ApplicationModel application)
    {
      ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
      return View("Formulaire2", new ViewModel09(application));
}

العرض المعروض في السطر 6 هو [Form2] المرتبط بنموذج [ViewModel09] التالي:


using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
using Exemple_03.Models;
 
namespace Exemple_03.Models
{
  public class ViewModel09
  {
    // input fields
    public string RadioButtonField { get; set; }
    public bool CheckBoxField1 { get; set; }
    public bool CheckBoxField2 { get; set; }
    public bool CheckBoxField3 { get; set; }
    public string TextField { get; set; }
    public string PasswordField { get; set; }
    public string TextAreaField { get; set; }
    public string DropDownListField { get; set; }
    public string SimpleChoiceListField { get; set; }
    public string[] MultipleChoiceListField { get; set; }
 
    // collections to be displayed in the form
    public ApplicationModel.Item[] RadioButtonFieldItems { get; set; }
    public ApplicationModel.Item[] CheckBoxesFieldItems { get; set; }
    public ApplicationModel.Item[] DropDownListFieldItems { get; set; }
    public ApplicationModel.Item[] SimpleChoiceListFieldItems { get; set; }
    public ApplicationModel.Item[] MultipleChoiceListFieldItems { get; set; }
 
    // manufacturers
    public ViewModel09()
    {
    }
 
    public ViewModel09(ApplicationModel application)
    {
      // initialization collections
      RadioButtonFieldItems = application.RadioButtonFieldItems;
      CheckBoxesFieldItems = application.CheckBoxesFieldItems;
      DropDownListFieldItems = application.DropDownListFieldItems;
      SimpleChoiceListFieldItems = application.SimpleChoiceListFieldItems;
      MultipleChoiceListFieldItems = application.MultipleChoiceListFieldItems;
      // field initialization
      RadioButtonField = "2";
      CheckBoxField2 = true;
      TextField = "quelques mots";
      PasswordField = "secret";
      TextAreaField = "ligne1\nligne2";
      DropDownListField = "2";
      SimpleChoiceListField = "3";
      MultipleChoiceListField = new string[] { "1", "3" };
    }
  }
}

يختلف [ViewModel09] عن [ViewModel08] في طريقة تعامله مع مربعات الاختيار. فبدلاً من استخدام مصفوفة مكونة من ثلاثة مربعات اختيار، تم استخدام ثلاثة مربعات اختيار منفصلة (الأسطر 11–13).

سيتم معالجة النموذج بواسطة الإجراء [Action09Post] التالي:


    // Action09-POST
    [HttpPost]
    public ViewResult Action09Post(ApplicationModel application, FormCollection posted)
    {
      ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
      ViewModel09 modèle = new ViewModel09(application);
      TryUpdateModel(modèle, posted);
      // processing of non-posted values
      if (posted["SimpleChoiceListField"] == null)
      {
        modèle.SimpleChoiceListField = "";
      }
      if (posted["MultipleChoiceListField"] == null)
      {
        modèle.MultipleChoiceListField = new string[] { };
      }
      // form display
      return View("Formulaire2", modèle);
}

تتشابه الإجراء [Action09Post] مع الإجراء [Action08Post] باستثناء نقطتين:

  • السطر 18: يتم استخدام عرض [Form2] بدلاً من عرض [Form
  • لم يعد هناك أي معالجة لمربعات الاختيار غير المحددة. يتم الآن معالجة ذلك بشكل صحيح بواسطة طريقة [Html.CheckBoxFor].

5.8. إنشاء نموذج من بيانات التعريف الخاصة بالنموذج

هناك طرق أخرى إلى جانب تلك المذكورة أعلاه لإنشاء نموذج. تتضمن إحدى هذه الطرق ربط المعلومات بخاصية نموذج تخبر إطار عمل MVC بعلامة الإدخال التي يجب إنشاؤها. تسمى هذه المعلومات البيانات الوصفية.

لننظر إلى نموذج العرض التالي [ViewModel10]:


using System;
using System.ComponentModel.DataAnnotations;
using System.Drawing;
 
namespace Exemple_03.Models
{
  public class ViewModel10
  {
    [Display(Name="Text")]
    [DataType(DataType.Text)]
    public string Text { get; set; }
 
    [Display(Name = "TextArea")]
    [DataType(DataType.MultilineText)]
    public string MultiLineText { get; set; }
 
    [Display(Name = "Number")]
    public int Number { get; set; }
 
    [Display(Name = "Decimal")]
    [UIHint("Decimal")]
    public double Decimal { get; set; }
 
    [Display(Name = "Tel")]
    [DataType(DataType.PhoneNumber)]
    public string Tel { get; set; }
 
    [Display(Name = "Date")]
    [DataType(DataType.Date)]
    public DateTime Date { get; set; }
 
    [Display(Name = "Time")]
    [DataType(DataType.Time)]
    public DateTime Time { get; set; }
 
    [Display(Name = "HiddenInput")]
    [UIHint("HiddenInput")]
    public string HiddenInput { get; set; }
 
    [Display(Name = "Boolean")]
    [UIHint("Boolean")]
    public bool Boolean { get; set; }
 
    [Display(Name = "Email")]
    [DataType(DataType.EmailAddress)]
    public string Email{ get; set; }
 
    [Display(Name = "Url")]
    [DataType(DataType.Url)]
    public string Url { get; set; }
 
    [Display(Name = "Password")]
    [DataType(DataType.Password)]
    public string Password { get; set; }
 
    [Display(Name = "Currency")]
    [DataType(DataType.Currency)]
    public double Currency { get; set; }
 
    [Display(Name = "CreditCard")]
    [DataType(DataType.CreditCard)]
    public string CreditCard { get; set; }
 
    // manufacturer
    public ViewModel10()
    {
      Text = "tra la la";
      MultiLineText = "ligne1\nligne2";
      Number = 4;
      Decimal = 10.2;
      Tel = "0617181920";
      Date = DateTime.Now;
      Time = DateTime.Now;
      HiddenInput = "caché";
      Boolean = true;
      Email = "x@y.z";
      Url = "http://istia.univ-angers.fr";
      Password = "mdp";
      Currency = 4.2;
      CreditCard = "0123456789012345";
    }
  }
}

تتكون البيانات الوصفية من العلامات [Display، DataType، UIHint].

سيتم إنشاء قالب العرض هذا بواسطة الإجراء [Action10Get] التالي:


    // Action10-GET
    [HttpGet]
    public ViewResult Action10Get()
    {
      return View(new ViewModel10());
}

في السطر 5 أعلاه، نوجه العرض الافتراضي للإجراء [/First/Action10Get.cshtml] لعرض نموذج العرض من النوع [ViewModel10]. هذا العرض هو كما يلي:


@model Exemple_03.Models.ViewModel10
 
@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action10Get</title>
</head>
<body>
  <h3>Formulaire ASP.NET MVC - 2</h3>
  @using (Html.BeginForm("Action10Post", "First"))
  {
    <table>
      <thead>
        <tr>
          <th>LabelFor</th>
          <th>EditorFor</th>
          <th>DisplayFor</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>@Html.LabelFor(m => m.Text)</td>
          <td>@Html.EditorFor(m => m.Text)</td>
          <td>@Html.DisplayFor(m => m.Text)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.MultiLineText)</td>
          <td>@Html.EditorFor(m => m.MultiLineText)</td>
          <td>@Html.DisplayFor(m => m.MultiLineText)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Number)</td>
          <td>@Html.EditorFor(m => m.Number)</td>
          <td>@Html.DisplayFor(m => m.Number)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Decimal)</td>
          <td>@Html.EditorFor(m => m.Decimal)</td>
          <td>@Html.DisplayFor(m => m.Decimal)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Tel)</td>
          <td>@Html.EditorFor(m => m.Tel)</td>
          <td>@Html.DisplayFor(m => m.Tel)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Date)</td>
          <td>@Html.EditorFor(m => m.Date)</td>
          <td>@Html.DisplayFor(m => m.Date)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Time)</td>
          <td>@Html.EditorFor(m => m.Time)</td>
          <td>@Html.DisplayFor(m => m.Time)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.HiddenInput)</td>
          <td>@Html.EditorFor(m => m.HiddenInput)</td>
          <td>@Html.DisplayFor(m => m.HiddenInput)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Boolean)</td>
          <td>@Html.EditorFor(m => m.Boolean)</td>
          <td>@Html.DisplayFor(m => m.Boolean)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Email)</td>
          <td>@Html.EditorFor(m => m.Email)</td>
          <td>@Html.DisplayFor(m => m.Email)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Url)</td>
          <td>@Html.EditorFor(m => m.Url)</td>
          <td>@Html.DisplayFor(m => m.Url)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Password)</td>
          <td>@Html.EditorFor(m => m.Password)</td>
          <td>@Html.DisplayFor(m => m.Password)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Currency)</td>
          <td>@Html.EditorFor(m => m.Currency)</td>
          <td>@Html.DisplayFor(m => m.Currency)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.CreditCard)</td>
          <td>@Html.EditorFor(m => m.CreditCard)</td>
          <td>@Html.DisplayFor(m => m.CreditCard)</td>
        </tr>
      </tbody>
    </table>
    <input type="submit" value="Valider" />
  }
</body>
</html>

بالنسبة لكل خاصية في النموذج، نستخدم الطريقة:

  • Html.LabelFor لعرض قيمة بيانات تعريف [DisplayName] للخاصية؛
  • Html.EditorFor لإنشاء علامة إدخال HTML لقيمة الخاصية. تستخدم هذه الطريقة بيانات تعريف [DataType] و [UIHint] الخاصة بالخاصية؛
  • Html.DisplayFor لعرض قيمة الخاصية بالتنسيق المحدد بواسطة بيانات التعريف [DataType].

فيما يلي مثال على كيفية عمل ذلك في متصفح Chrome:

Image

قد تختلف الصفحات باختلاف المتصفح المستخدم. ويرجع ذلك إلى أن العرض الذي تم إنشاؤه يستخدم العلامات الجديدة التي أدخلتها النسخة 5 من HTML، والمعروفة باسم HTML5. ولا تدعم جميع المتصفحات هذه النسخة حتى الآن. في المثال أعلاه، يدعم متصفح Chrome هذه النسخة جزئيًا.

5.8.1. [POST] النموذج

يتم التعامل مع [POST] النموذج بواسطة الإجراء [Action10Post] التالي:


    // Action10-POST
    [HttpPost]
    public ContentResult Action10Post(ViewModel10 modèle)
    {
      string erreurs = getErrorMessagesFor(ModelState);
      string texte = string.Format("Contrôleur={0}, Action={1}, valide={2}, erreurs={3}", RouteData.Values["controller"], RouteData.Values["action"], ModelState.IsValid, erreurs);
      return Content(texte, "text/plain", Encoding.UTF8);
}
  • السطر 3: تستخدم الإجراء [Action10Post] النموذج المقدم كنموذج إدخال؛
  • السطر 5: نسترد أخطاء التحقق من صحة البيانات من هذا النموذج؛
  • السطر 6: يتم إعداد استجابة النص للعميل؛
  • السطر 7: نرسلها.

دعونا الآن نفحص خصائص نموذج [ViewModel10] واحدة تلو الأخرى ونرى كيف تؤثر البيانات الوصفية المرتبطة به على HTML الذي تم إنشاؤه والتحقق من صحة حقول الإدخال.

5.8.2. خاصية [Text]

التعريف


    [Display(Name="Text")]
    [DataType(DataType.Text)]
    public string Text { get; set; }
...
Text = "tra la la";

عرض


      <tr>
        <td>@Html.LabelFor(m => m.Text)</td>
        <td>@Html.EditorFor(m => m.Text)</td>
        <td>@Html.DisplayFor(m => m.Text)</td>
</tr>

مرئي

Image

HTML الذي تم إنشاؤه


      <tr>
        <td><label for="Text">Text</label></td>
        <td><input class="text-box single-line" id="Text" name="Text" type="text" value="tra la la" /></td>
        <td>tra la la</td>
</tr>

تعليقات

  • أنتجت الطريقة [Html.LabelFor] العلامة <label> في السطر 2. قيمة السمة [for] هي اسم خاصية المعلمة للطريقة [Html.LabelFor]


public string Text { get; set; }

النص المعروض بين بداية العلامة ونهايتها هو نص البيانات الوصفية


[Display(Name="Text")]

تعمل طريقة [Html.LabelFor] دائمًا بهذه الطريقة. لن نعود إلى هذا الأمر بالنسبة للخصائص الأخرى.

  • قامت طريقة [Html.EditorFor] بإنشاء علامة <input> في السطر 3. لاحظ أن لها سمة [class] تربط فئة CSS [text-box single-line] بالعلامة. تحتوي السمتان [id] و [name] على القيمة [Text]، وهي اسم خاصية المعلمة لطريقة [Html.EditorFor]. تحتوي السمة [type] على القيمة [text] بسبب البيانات الوصفية


[DataType(DataType.Text)]

  • البيانات الوصفية، قامت الطريقة [Html.DisplayFor] بإنشاء النص في السطر 4. هذه هي قيمة خاصية المعلمة للطريقة [Html.DisplayFor]. تتأثر هذه الطريقة بالبيانات الوصفية


[DataType(DataType.Text)]

، مما يؤدي إلى عرض القيمة كنص غير منسق.

5.8.3. خاصية [MultiLineText]

التعريف


    [Display(Name = "TextArea")]
    [DataType(DataType.MultilineText)]
public string MultiLineText { get; set; }

عرض


      <tr>
        <td>@Html.LabelFor(m => m.MultiLineText)</td>
        <td>@Html.EditorFor(m => m.MultiLineText)</td>
        <td>@Html.DisplayFor(m => m.MultiLineText)</td>
</tr>

مرئي

Image

HTML الذي تم إنشاؤه


      <tr>
        <td><label for="MultiLineText">TextArea</label></td>
        <td><textarea class="text-box multi-line" id="MultiLineText" name="MultiLineText">
ligne1
ligne2</textarea></td>
        <td>ligne1
ligne2</td>
</tr>

تعليقات

  • قامت طريقة [Html.EditorFor] بإنشاء العلامة <textarea> في السطر 3. لاحظ أن هذه العلامة تحتوي على سمة [class] تربط فئة CSS [text-box multi-line] بالعلامة. أما السمتان [id] و[name] فتحتويان على القيمة [MultiLineText]، وهي اسم خاصية المعلمة لطريقة [Html.EditorFor]. وهذا هو الحال دائمًا. ولن نذكر ذلك مرة أخرى. العلامة التي تم إنشاؤها هي <textarea> بسبب البيانات الوصفية


[DataType(DataType.MultilineText)]

التي حددت أن الخاصية كانت نصًا متعدد الأسطر.

  • قامت طريقة [Html.DisplayFor] بإنشاء النص للسطرين 4 و5. هذه هي قيمة خاصية المعلمة لطريقة [Html.DisplayFor].

5.8.4. خاصية [Number]

التعريف


    [Display(Name = "Number")]
public int Number { get; set; }

عرض


      <tr>
        <td>@Html.LabelFor(m => m.Number)</td>
        <td>@Html.EditorFor(m => m.Number)</td>
        <td>@Html.DisplayFor(m => m.Number)</td>
</tr>

مرئي

Image

HTML الذي تم إنشاؤه


<tr>
        <td><label for="Number">Number</label></td>
        <td><input class="text-box single-line" data-val="true" data-val-number="Le champ Number doit être un nombre." data-val-required="Le champ Number est requis." id="Number" name="Number" type="number" value="4" /></td>
        <td>4</td>
      </tr>

تعليقات

  • قامت طريقة [Html.EditorFor] بإنشاء علامة <input> في السطر 3 مع تعيين السمة [type] إلى [number]. ويبدو أن السبب في ذلك هو أن الخاصية من النوع [int]. لا يتعرف HTML5 على السمات [data-val] و[data-val-number] و[data-val-required]. وهي تُستخدم بواسطة إطار عمل للتحقق من صحة البيانات في JavaScript من جانب العميل؛
  • قامت الطريقة [Html.DisplayFor] بإنشاء النص الموجود في السطر 4، وهو قيمة الخاصية.

التحقق من الصحة

تؤثر سمات [data-x] على التحقق من صحة البيانات من جانب العميل. فيما يلي مثالان:

ندخل رقمًا غير صحيح ونرسل:

Image

في المثال أعلاه، تم التحقق من صحة البيانات من جانب العميل. لن يتم إرسال النموذج حتى يتم تصحيح الخطأ.

مثال آخر: لا ندخل أي شيء:

في [1] أعلاه، يُبلغ [Action10Post] عن وجود خطأ. قد تتذكر أننا حققنا هذا السلوك سابقًا باستخدام السمة [Required] على الخاصية المراد التحقق من صحتها (انظر 63)، وهي في هذه الحالة الخاصية [Number]. هنا، لم نضطر إلى القيام بذلك.

5.8.5. الخاصية [Decimal]

التعريف


    [Display(Name = "Decimal")]
    [UIHint("Decimal")]
public double Decimal { get; set; }

عرض


      <tr>
        <td>@Html.LabelFor(m => m.Decimal)</td>
        <td>@Html.EditorFor(m => m.Decimal)</td>
        <td>@Html.DisplayFor(m => m.Decimal)</td>
</tr>

مرئي

Image

HTML الذي تم إنشاؤه


      <tr>
        <td><label for="Decimal">Decimal</label></td>
        <td><input class="text-box single-line" data-val="true" data-val-number="Le champ Decimal doit être un nombre." data-val-required="Le champ Decimal est requis." id="Decimal" name="Decimal" type="text" value="10,20" /></td>
        <td>10,20</td>
</tr>

تعليقات

  • قامت طريقة [Html.EditorFor] بإنشاء العلامة <input> في السطر 3 مع سمة [type] من النوع [text]. السمايات الأخرى مطابقة لتلك التي تم إنشاؤها للخاصية [Number] السابقة. البيانات الوصفية:


[UIHint("Decimal")]

تعرض قيمة الخاصية برقمين عشريين لكل من طريقتي [Html.EditorFor] و [Html.DisplayFor]

التحقق من الصحة

لا يتم الإبلاغ عن أخطاء التحقق من الصحة على جانب العميل، على عكس الحالة السابقة. يتم الإبلاغ عن الخطأ فقط بواسطة الإجراء [Action10Post]. مرة أخرى، الرقم العشري مطلوب دون الحاجة إلى تعيين السمة [Required].

5.8.6. خاصية [Tel]

التعريف


    [Display(Name = "Tel")]
    [DataType(DataType.PhoneNumber)]
public string Tel { get; set; }

عرض


      <tr>
        <td>@Html.LabelFor(m => m.Tel)</td>
        <td>@Html.EditorFor(m => m.Tel)</td>
        <td>@Html.DisplayFor(m => m.Tel)</td>
</tr>

مرئي

Image

HTML الذي تم إنشاؤه


      <tr>
        <td><label for="Tel">Tel</label></td>
        <td><input class="text-box single-line" id="Tel" name="Tel" type="tel" value="0617181920" /></td>
        <td>0617181920</td>
</tr>

تعليقات

  • أنتجت طريقة [Html.EditorFor] العلامة <input> في السطر 3 مع سمة [type] من النوع [tel]. تم إنشاء هذه القيمة استنادًا إلى البيانات الوصفية:


[DataType(DataType.PhoneNumber)]

يعد النوع [tel] لعلامة <input> ميزة جديدة في HTML5. تعامل متصفح Chrome معها على أنها علامة <input> بنوع [text].

التحقق من الصحة

لم يتم الإبلاغ عن أي أخطاء في التحقق من الصحة على جانب العميل أو الخادم. يمكنك إدخال أي شيء.

5.8.7. خاصية [التاريخ]

التعريف


    [Display(Name = "Date")]
    [DataType(DataType.Date)]
public DateTime Date { get; set; }

عرض


      <tr>
        <td>@Html.LabelFor(m => m.Date)</td>
        <td>@Html.EditorFor(m => m.Date)</td>
        <td>@Html.DisplayFor(m => m.Date)</td>
</tr>

مرئي

Image

HTML الذي تم إنشاؤه


      <tr>
        <td><label for="Date">Date</label></td>
        <td><input class="text-box single-line" data-val="true" data-val-date="Le champ Date doit être une date." data-val-required="Le champ Date est requis." id="Date" name="Date" type="date" value="11/10/2013" /></td>
        <td>11/10/2013</td>
</tr>

التعليقات

  • قامت طريقة [Html.EditorFor] بإنشاء العلامة <input> في السطر 3 مع سمة [type] من النوع [date]. تم إنشاء هذه القيمة استنادًا إلى البيانات الوصفية:


[DataType(DataType.Date)]

يعد النوع [date] لعلامة <input> ميزة جديدة في HTML5. يتعرف متصفح Chrome عليها ويسمح بإدخال التاريخ باستخدام التقويم. علاوة على ذلك، يتم عرض التاريخ الذي تم إدخاله بتنسيق [dd/mm/yyyy]، مما يعني أن Chrome يكيّف تنسيق التاريخ مع [locale] المتصفح.

  • كما قامت طريقة [Html.DisplayFor] بكتابة التاريخ بتنسيق [dd/mm/yyyy]، وذلك مرة أخرى بسبب وجود البيانات الوصفية [Date].

التحقق من الصحة

يتم وضع علامة على التاريخ غير الصحيح من جانب العميل [1]، مما يمنع إرسال النموذج إلى الخادم.

لا يتم الإبلاغ عن عدم وجود تاريخ على جانب العميل، ولكن يتم الإبلاغ عنه على جانب الخادم [2].

5.8.8. خاصية [Time]

التعريف


    [Display(Name = "Time")]
    [DataType(DataType.Time)]
public DateTime Time { get; set; }

عرض


      <tr>
        <td>@Html.LabelFor(m => m.Time)</td>
        <td>@Html.EditorFor(m => m.Time)</td>
        <td>@Html.DisplayFor(m => m.Time)</td>
</tr>

مرئي

Image

HTML الذي تم إنشاؤه


      <tr>
        <td><label for="Time">Time</label></td>
        <td><input class="text-box single-line" data-val="true" data-val-required="Le champ Time est requis." id="Time" name="Time" type="time" value="11:17" /></td>
        <td>11:17</td>
</tr>

تعليقات

  • أنتجت طريقة [Html.EditorFor] العلامة <input> في السطر 3 مع سمة [type] من النوع [time]. تم إنشاء هذه القيمة استنادًا إلى البيانات الوصفية:


[DataType(DataType.Time)]

يعد النوع [time] لعلامة <input> ميزة جديدة في HTML5. يتعرف متصفح Chrome عليها ويسمح لك بإدخال الوقت بالتنسيق [hh:mm

  • كما عرضت طريقة [Html.DisplayFor] الوقت بتنسيق [hh:mm]، وذلك مرة أخرى بسبب وجود البيانات الوصفية [Time].

التحقق من الصحة

من المستحيل تقنيًا إدخال وقت غير صالح. يتم الإبلاغ عن عدم وجود وقت من جانب الخادم:

Image

5.8.9. خاصية [HiddenInput]

التعريف


    [Display(Name = "HiddenInput")]
    [UIHint("HiddenInput")]
public string HiddenInput { get; set; }

عرض


      <tr>
        <td>@Html.LabelFor(m => m.HiddenInput)</td>
        <td>@Html.EditorFor(m => m.HiddenInput)</td>
        <td>@Html.DisplayFor(m => m.HiddenInput)</td>
</tr>

مرئي

Image

HTML مُنشأ


      <tr>
        <td><label for="HiddenInput">HiddenInput</label></td>
        <td>cach&#233;<input id="HiddenInput" name="HiddenInput" type="hidden" value="caché" /></td>
        <td>cach&#233;</td>
</tr>

تعليقات

  • أنتجت طريقة [Html.EditorFor] العلامة <input> في السطر 3 مع تعيين السمة [type] إلى [hidden]، أي حقل مخفي (ولكنه لا يزال يتم إرساله). تم إنشاء هذه القيمة بسبب البيانات الوصفية:


[UIHint("HiddenInput")]

  • قامت الطريقة [Html.DisplayFor] بكتابة قيمة الحقل المخفي.

5.8.10. خاصية [Boolean]

التعريف


    [Display(Name = "Boolean")]
public bool Boolean { get; set; }

عرض


      <tr>
        <td>@Html.LabelFor(m => m.Boolean)</td>
        <td>@Html.EditorFor(m => m.Boolean)</td>
        <td>@Html.DisplayFor(m => m.Boolean)</td>
</tr>

مرئي

Image

HTML الذي تم إنشاؤه


      <tr>
        <td><label for="Boolean">Boolean</label></td>
        <td><input checked="checked" class="check-box" data-val="true" data-val-required="Le champ Boolean est requis." id="Boolean" name="Boolean" type="checkbox" value="true" /><input name="Boolean" type="hidden" value="false" /></td>
        <td><input checked="checked" class="check-box" disabled="disabled" type="checkbox" /></td>
</tr>

تعليقات

  • أنتجت الطريقة [Html.EditorFor] العلامة <input> في السطر 3 مع تعيين السمة [type] إلى [checkbox]، أي مربع اختيار. تم إنتاج هذه القيمة لأن الخاصية من النوع المنطقي:


public bool Boolean { get; set; }

  • أنتجت طريقة [Html.DisplayFor] السطر 4، وهو أيضًا مربع اختيار (السمة type) ولكنه معطل (السمة disabled).

5.8.11. خاصية [Email]

التعريف


    [Display(Name = "Email")]
    [DataType(DataType.EmailAddress)]
public string Email{ get; set; }

عرض


      <tr>
        <td>@Html.LabelFor(m => m.Email)</td>
        <td>@Html.EditorFor(m => m.Email)</td>
        <td>@Html.DisplayFor(m => m.Email)</td>
</tr>

مرئي

Image

HTML الذي تم إنشاؤه


      <tr>
        <td><label for="Email">Email</label></td>
        <td><input class="text-box single-line" id="Email" name="Email" type="email" value="x@y.z" /></td>
        <td><a href="mailto:x@y.z">x@y.z</a></td>
</tr>

التعليقات

  • قامت طريقة [Html.EditorFor] بإنشاء العلامة <input> في السطر 3 مع سمة [type] من النوع [email]. هذا النوع جديد في HTML5. تم إنشاء هذا النوع بسبب البيانات الوصفية:


[DataType(DataType.EmailAddress)]

يبدو أن Chrome تعامل مع هذا النوع على أنه نوع [text].

  • قامت طريقة [Html.DisplayFor] بإنشاء السطر 4: رابط إلى عنوان البريد الإلكتروني.

التحقق من الصحة

تم الإبلاغ عن عنوان غير صالح على جانب العميل [1]:

ترك الحقل فارغًا لا يؤدي إلى حدوث خطأ.

5.8.12. خاصية [Url]

التعريف


    [Display(Name = "Url")]
    [DataType(DataType.Url)]
public string Url { get; set; }

عرض


      <tr>
        <td>@Html.LabelFor(m => m.Url)</td>
        <td>@Html.EditorFor(m => m.Url)</td>
        <td>@Html.DisplayFor(m => m.Url)</td>
</tr>

مرئي

Image

HTML الذي تم إنشاؤه


      <tr>
        <td><label for="Url">Url</label></td>
        <td><input class="text-box single-line" id="Url" name="Url" type="url" value="http://istia.univ-angers.fr" /></td>
        <td><a href="http://istia.univ-angers.fr">http://istia.univ-angers.fr</a></td>
</tr>

تعليقات

  • أنتجت طريقة [Html.EditorFor] العلامة <input> في السطر 3 مع سمة [type] من النوع [url]. هذا النوع جديد في HTML5. تم إنشاؤه بسبب البيانات الوصفية:


[DataType(DataType.Url)]

يبدو أن Chrome تعامل هذا النوع على أنه نوع [text].

  • قامت طريقة [Html.DisplayFor] بإنشاء السطر 4: رابط إلى عنوان URL.

التحقق من الصحة

تم الإبلاغ عن عنوان URL غير صالح على جانب العميل [1]:

عدم إدخال أي قيمة لا يؤدي إلى حدوث خطأ.

5.8.13. خاصية [Password]

التعريف


    [Display(Name = "Password")]
    [DataType(DataType.Password)]
public string Password { get; set; }

عرض


      <tr>
        <td>@Html.LabelFor(m => m.Password)</td>
        <td>@Html.EditorFor(m => m.Password)</td>
        <td>@Html.DisplayFor(m => m.Password)</td>
</tr>

مرئي

Image

HTML مُنشأ


      <tr>
        <td><label for="Password">Password</label></td>
        <td><input class="text-box single-line password" id="Password" name="Password" type="password" value="mdp" /></td>
        <td>mdp</td>
</tr>

تعليقات

  • قامت طريقة [Html.EditorFor] بإنشاء العلامة <input> في السطر 3 مع سمة [type] من النوع [password]. تم إنشاء هذا النوع استنادًا إلى البيانات الوصفية:


[DataType(DataType.Password)]

  • قامت الطريقة [Html.DisplayFor] بإنشاء السطر 4.

5.8.14. خاصية [Currency]

التعريف


    [Display(Name = "Currency")]
    [DataType(DataType.Currency)]
public double Currency { get; set; }

عرض


      <tr>
        <td>@Html.LabelFor(m => m.Currency)</td>
        <td>@Html.EditorFor(m => m.Currency)</td>
        <td>@Html.DisplayFor(m => m.Currency)</td>
</tr>

مرئي

Image

HTML الذي تم إنشاؤه


      <tr>
        <td><label for="Currency">Currency</label></td>
        <td><input class="text-box single-line" data-val="true" data-val-number="Le champ Currency doit être un nombre." data-val-required="Le champ Currency est requis." id="Currency" name="Currency" type="text" value="4,2" /></td>
        <td>4,20 €</td>
</tr>

تعليقات

  • أنتجت طريقة [Html.EditorFor] العلامة <input> في السطر 3 مع تعيين السمة [type] إلى [text

  • قامت الطريقة [Html.DisplayFor] بإنشاء السطر 4، وهو رقم به رقمان عشريان ورمز عملة. تم استخدام هذا التنسيق بسبب البيانات الوصفية:


[DataType(DataType.Currency)]

التحقق من الصحة

يتم الإبلاغ عن قيمة غير صالحة [1] أو قيمة مفقودة [2] على جانب الخادم:

5.8.15. خاصية [CreditCard]

التعريف


    [Display(Name = "CreditCard")]
    [DataType(DataType.CreditCard)]
public string CreditCard { get; set; }

عرض


      <tr>
        <td>@Html.LabelFor(m => m.CreditCard)</td>
        <td>@Html.EditorFor(m => m.CreditCard)</td>
        <td>@Html.DisplayFor(m => m.CreditCard)</td>
</tr>

مرئي

Image

HTML الذي تم إنشاؤه


      <tr>
        <td><label for="CreditCard">CreditCard</label></td>
        <td><input class="text-box single-line" id="CreditCard" name="CreditCard" type="text" value="0123456789012345" /></td>
        <td>0123456789012345</td>
</tr>

تعليقات

  • أنتجت طريقة [Html.EditorFor] العلامة <input> في السطر 3 مع سمة [type] من النوع [text]. أنتجت طريقة [Html.DisplayFor] السطر 4. ليس من الواضح هنا ما الذي تساهم به البيانات الوصفية:


[DataType(DataType.CreditCard)]

التحقق من الصحة

لا يتم إجراء أي تحقق من الصحة، سواء على جانب العميل أو على جانب الخادم.

5.9. التحقق من صحة النموذج

لقد تناولنا بالفعل مسألة التحقق من صحة نموذج الإجراء في القسم 4.5 والأقسام التالية. ونعود إلى هذه المسألة في سياق النموذج:

  • كيفية إخطار المستخدم بأخطاء الإدخال؛
  • إجراء عمليات التحقق من الصحة على جانب العميل وجانب الخادم لإبلاغ المستخدم بالأخطاء بشكل أسرع.

5.9.1. التحقق من الصحة من جانب الخادم

لننظر إلى النموذج التالي:


using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Net.Mail;
 
namespace Exemple_03.Models
{
  public class ViewModel11 : IValidatableObject
  {
 
    [Required(ErrorMessage = "Information requise")]
    [Display(Name = "Chaîne d'au moins quatre caractères")]
    [RegularExpression(@"^.{4,}$", ErrorMessage = "Information incorrecte")]
    public string Chaine1 { get; set; }
 
    [Display(Name = "Chaîne d'au plus quatre caractères")]
    [Required(ErrorMessage = "Information requise")]
    [RegularExpression(@"^.{1,4}$", ErrorMessage = "Information incorrecte")]
    public string Chaine2 { get; set; }
 
    [Required(ErrorMessage = "Information requise")]
    [Display(Name = "Chaîne de quatre caractères exactement")]
    [RegularExpression(@"^.{4,4}$", ErrorMessage = "Information incorrecte")]
    public string Chaine3 { get; set; }
 
    [Required(ErrorMessage = "Information requise")]
    [Display(Name = "Nombre entier")]
    public int Entier1 { get; set; }
 
    [Display(Name = "Nombre entier dans l'intervalle [1,100]")]
    [Required(ErrorMessage = "Information requise")]
    [Range(1, 100, ErrorMessage = "Information incorrecte")]
    public int Entier2 { get; set; }
 
    [Display(Name = "Nombre réel")]
    [Required(ErrorMessage = "Information requise")]
    public double Reel1 { get; set; }
 
    [Display(Name = "Nombre réel dans l'intervalle [10.2, 11.3]")]
    [Required(ErrorMessage = "Information requise")]
    [Range(10.2, 11.3, ErrorMessage = "Information incorrecte")]
    public double Reel2 { get; set; }
 
    [Display(Name = "Adresse mail")]
    [Required(ErrorMessage = "Information requise")]
    public string Email1 { get; set; }
 
    [Display(Name = "Date sous la forme dd/jj/aaaa")]
    [RegularExpression(@"\s*\d{2}/\d{2}/\d{4}\s*", ErrorMessage = "Information incorrecte")]
    [Required(ErrorMessage = "Information requise")]
    public string Regexp1 { get; set; }
 
    [Display(Name = "Date postérieure à celle d'aujourd'hui")]
    [Required(ErrorMessage = "Information requise")]
    [DataType(DataType.Date)]
    public DateTime Date1 { get; set; }
 
    // validation
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
      List<ValidationResult> résultats = new List<ValidationResult>();
      // Date 1
      if (Date1.Date <= DateTime.Now.Date)
      {
        résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Date1" }));
      }
      // Email1
      try
      {
        new MailAddress(Email1);
      }
      catch
      {
        résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Email1" }));
      }
      // return the list of errors
      return résultats;
    }
  }
}

سيتم عرض هذا النموذج من خلال العرض التالي [Action11Get.cshtml]:


@model Exemple_03.Models.ViewModel11
@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action11Get</title>
  <link rel="stylesheet" href="~/Content/Site.css" />
</head>
<body>
  <h3>Formulaire ASP.NET MVC – Validation 1</h3>
  @using (Html.BeginForm("Action11Post", "First"))
  {
    <table>
      <thead>
        <tr>
          <th>Type attendu</th>
          <th>Valeur saisie</th>
          <th>Message d'erreur</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>@Html.LabelFor(m => m.Chaine1)</td>
          <td>@Html.EditorFor(m => m.Chaine1)</td>
          <td>@Html.ValidationMessageFor(m => m.Chaine1)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Chaine2)</td>
          <td>@Html.EditorFor(m => m.Chaine2)</td>
          <td>@Html.ValidationMessageFor(m => m.Chaine2)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Chaine3)</td>
          <td>@Html.EditorFor(m => m.Chaine3)</td>
          <td>@Html.ValidationMessageFor(m => m.Chaine3)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Entier1)</td>
          <td>@Html.EditorFor(m => m.Entier1)</td>
          <td>@Html.ValidationMessageFor(m => m.Entier1)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Entier2)</td>
          <td>@Html.EditorFor(m => m.Entier2)</td>
          <td>@Html.ValidationMessageFor(m => m.Entier2)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Reel1)</td>
          <td>@Html.EditorFor(m => m.Reel1)</td>
          <td>@Html.ValidationMessageFor(m => m.Reel1)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Reel2)</td>
          <td>@Html.EditorFor(m => m.Reel2)</td>
          <td>@Html.ValidationMessageFor(m => m.Reel2)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Email1)</td>
          <td>@Html.EditorFor(m => m.Email1)</td>
          <td>@Html.ValidationMessageFor(m => m.Email1)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Regexp1)</td>
          <td>@Html.EditorFor(m => m.Regexp1)</td>
          <td>@Html.ValidationMessageFor(m => m.Regexp1)</td>
        </tr>
        <tr>
          <td>@Html.LabelFor(m => m.Date1)</td>
          <td>@Html.EditorFor(m => m.Date1)</td>
          <td>@Html.ValidationMessageFor(m => m.Date1)</td>
        </tr>
      </tbody>
    </table>
    <p>
      <input type="submit" value="Valider" />
    </p>
  }
</body>
</html>

  • السطر 12: يشير إلى ورقة الأنماط [Site.css]. بشكل افتراضي، تحتوي على فئات تُستخدم لإبراز أخطاء إدخال البيانات في النماذج؛
  • الأسطر 18-25: جدول من ثلاثة أعمدة:
    • يعرض العمود 1 النص باستخدام طريقة [Html.LabelFor
    • يعرض العمود 2 المدخلات باستخدام طريقة [Html.EditorFor
    • العمود 3 يعرض أي أخطاء في الإدخال باستخدام طريقة [Html.ValidationMessageFor

يُستخدم الإجراء [Action11Get] لعرض النموذج:


    // Action11-GET
    [HttpGet]
    public ViewResult Action11Get()
    {
      return View("Action11Get", new ViewModel11());
}

يُستخدم الإجراء [Action11Post] لإعادة عرض النموذج مع أي أخطاء في الإدخال:


    // Action11-POST
    [HttpPost]
    public ViewResult Action11Post(ViewModel11 modèle)
    {
      return View("Action11Get", modèle);
}

  • السطر 3: يتم إنشاء النموذج [ViewModel11] ثم تهيئته بالقيم المرسلة. قد تحدث أخطاء بعد ذلك. ترتبط كل خاصية غير صالحة P في النموذج برسالة خطأ. هذه هي الرسالة التي ترجعها طريقة [Html.ValidationMessageFor] الخاصة بالنموذج.

فيما يلي مثال على التنفيذ:

Image

Image

فيما يلي مثال آخر:

Image

لاحظ أن كلا التاريخين غير صحيحين (التاريخ الحالي هو 10/11/2013) ولكن لم يتم الإبلاغ عن الأخطاء. يتم الكشف عن هذه الأخطاء بواسطة طريقة [Validate] في النموذج:


    // validation
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
      List<ValidationResult> résultats = new List<ValidationResult>();
      // Date 1
      if (Date1.Date <= DateTime.Now.Date)
      {
        résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Date1" }));
      }
      // Email1
      try
      {
        new MailAddress(Email1);
      }
      catch
      {
        résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Email1" }));
      }
      // Regexp1
      try
      {
        DateTime.ParseExact(Regexp1, "dd/MM/yyyy", CultureInfo.CreateSpecificCulture("fr-FR"));
      }
      catch
      {
        résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Regexp1" }));
      }
 
      // on rend la liste des erreurs
      return résultats;
}

لا يتم تنفيذ طريقة [Validate] إلا بعد اجتياز جميع عمليات التحقق من صحة السمات. ويتضح ذلك في المثال الأخير:

Image

5.9.2. التحقق من الصحة من جانب العميل

تم إجراء جميع عمليات التحقق من الصحة السابقة على جانب الخادم. وهذا يتطلب رحلة ذهاب وإياب بين العميل والخادم حتى يتمكن المستخدم من رؤية أخطائه. يستخدم التحقق من الصحة من جانب العميل كود JavaScript لإعلام المستخدم بالأخطاء في أقرب وقت ممكن، وفي أي حال قبل طلب POST. لا يمكن أن يحدث طلب POST إلا بعد تصحيح جميع الأخطاء المكتشفة.

سنستخدم النموذج [ViewModel11] السابق، ولكننا سنعرضه الآن باستخدام طريقة العرض [Action12Get.cshtml] التالية:


@model Exemple_03.Models.ViewModel11
@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action12Get</title>
  <link rel="stylesheet" href="~/Content/Site.css" />
  <script type="text/javascript" src="~/Scripts/jquery-1.8.2.min.js" ></script>
  <script type="text/javascript" src="~/Scripts/jquery.validate.min.js" ></script>
  <script type="text/javascript" src="~/Scripts/jquery.validate.unobtrusive.min.js" ></script>
</head>
<body>
  <h3>Formulaire ASP.NET MVC - Validation 1</h3>
  @using (Html.BeginForm("Action11Post", "First"))
  {
    <table>
      <thead>
        <tr>
          <th>Type attendu</th>
          <th>Valeur saisie</th>
          <th>Message d'erreur</th>
        </tr>
      </thead>
      <tbody>
...
      </tbody>
    </table>
    <p>
      <input type="submit" value="Valider" />
    </p>
  }
</body>
</html>

ملاحظة: السطر 13 — اضبط إصدار jQuery ليتوافق مع الإصدار الذي لديك مع إصدار Visual Studio الخاص بك (انظر أدناه).

يتطلب التحقق من صحة البيانات من جانب العميل وجود السطر 3 أدناه في ملف [Web.config] الخاص بالتطبيق.


  <appSettings>
    ...
    <add key="ClientValidationEnabled" value="true" />
</appSettings>

  • الأسطر 1–4: يجب أن يكون قسم [appSettings] تابعًا مباشرًا لقسم [configuration] في ملف [Web.config

تطابق طريقة العرض [Action12Get] طريقة العرض [Action11Get] السابقة باستثناء الأسطر 13–15. تتضمن هذه الأسطر نصوص JavaScript المطلوبة للتحقق من صحة البيانات من جانب العميل في طريقة العرض. توجد هذه النصوص في مجلد [Scripts] الخاص بالمشروع:

يحتوي كل نص برمجي على نسخة عادية [.js] ونسخة مصغرة [min.js]. النسخة الأخيرة أصغر حجمًا ولكنها غير قابلة للقراءة. وهي تُستخدم في الإنتاج. أما النسخة القابلة للقراءة فتُستخدم في التطوير.

سيتم عرض طريقة العرض [Action12Get.cshtml] بواسطة الإجراء [Action12Get] التالي:


    // Action12-GET
    [HttpGet]
    public ViewResult Action12Get()
    {
      return View("Action12Get", new ViewModel11());
}

سيتم معالجة النموذج المرسَل بواسطة الإجراء [Action12Post] التالي:


    // Action12-POST
    [HttpPost]
    public ViewResult Action12Post(ViewModel11 modèle)
    {
      return View("Action12Get", modèle);
}

لنرى كيف يعمل هذا من خلال مثال:

بمجرد كتابة حرف في [1]، تظهر الرسالة في [2] لأن القيمة المتوقعة يجب أن تكون بطول أربعة أحرف على الأقل. وبالتالي، يتم التحقق من الصحة مع كل حرف جديد يتم كتابته. تختفي رسالة الخطأ بعد كتابة الحرف الرابع. بعد الانتهاء من ذلك، دعونا نرسل النموذج:

يُظهر لنا عنوان URL [3] أن عملية [POST] لم تتم. لكن النقر على زر [التحقق من الصحة] أدى إلى تشغيل جميع عمليات التحقق من الصحة من جانب العميل، وظهرت رسائل خطأ جديدة.

دعونا نلقي نظرة على HTML الذي تم إنشاؤه للإدخال الأول، على سبيل المثال:


        <tr>
          <td><label for="Chaine1">Cha&#238;ne d&#39;au moins quatre caract&#232;res</label></td>
          <td><input class="text-box single-line" data-val="true" data-val-regex="Information incorrecte" data-val-regex-pattern="^.{4,}$" data-val-required="Information requise" id="Chaine1" name="Chaine1" type="text" value="" /></td>
          <td><span class="field-validation-valid" data-valmsg-for="Chaine1" data-valmsg-replace="true"></span></td>
</tr>

  • يحتوي السطر 3 على:
    • رسالة الخطأ في حالة عدم وجود إدخال [data-val-required
    • رسالة الخطأ عند وجود خطأ في الإدخال [data-val-regex
    • التعبير العادي للسلسلة المدخلة [data-val-regex-pattern
  • السطر 4، سمات أخرى [data-x] تُستخدم لعرض أي رسائل خطأ؛

يتم استخدام سمات [data-x] للعلامات التي تم إنشاؤها بواسطة JavaScript الذي قمنا بتضمينه في العرض. إذا كان هذا JavaScript مفقودًا، فسيتم تجاهل هذه السمات ببساطة، ولن يكون هناك تحقق من صحة البيانات من جانب العميل. يعمل الأمر بنفس الطريقة كما في المثال السابق. ومن هنا جاء مصطلح [غير تدخلي] لهذه التقنية.

5.10. التعامل مع روابط التنقل والإجراءات

سننشئ العرضين التاليين لتوضيح إدارة الروابط داخل العرض:

  • في [1] و[2]، لدينا رابطان للتنقل؛
  • في [3]، يوجد رابط إجراء يقوم بإرسال النموذج. ولا يُستخدم للتنقل.

يتم إنشاء الصفحة 1 بواسطة طريقة العرض [Action16Get.cshtml] التالية:


@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action16Get</title>
  <script>
    function postForm() {
      // on récupère le formulaire du document
      var form = document.forms[0];
      // soumission
      form.submit();
    }
  </script>
</head>
<body>
  <h3>Navigation - page 1</h3>
  <h4>@ViewBag.info</h4>
  @using (Html.BeginForm("Action16Post", "Second"))
  {
    @Html.Label("data", "Tapez un texte")
    @Html.TextBox("data")
    <a href="javascript:postForm()">Valider</a>
  }
  <p>
    @Html.ActionLink("Page 2", "Action17Get", "Second")
  </p>
</body>
</html>

  • السطر 22: معلومات تم تهيئتها بواسطة الإجراء الذي سيقوم بعرض العرض؛
  • الأسطر 23–28: نموذج؛
  • السطر 25: تسمية لحقل [data
  • السطر 26: حقل إدخال باسم [data
  • السطر 27: رابط [submit]. عند النقر عليه، يتم تنفيذ دالة JavaScript [postForm] (سمة href). يتم تعريف هذه الدالة في الأسطر 12–17؛
  • السطر 14: يتم استرداد مرجع للنموذج الأول في المستند، وهو النموذج الموجود في السطر 23؛
  • السطر 16: يتم إرسال هذا النموذج. في النهاية، يتصرف النموذج كما لو تم النقر على زر [submit]. يتم إرسال النموذج إلى وحدة التحكم والإجراء المحدد في السطر 23؛
  • السطر 30: رابط تنقل. كود HTML الذي تم إنشاؤه هو كما يلي:


    <a href="/Second/Action17Get">Page 2</a>

الطريقة المستخدمة هي ActionLink(Text, Action, Controller).

يتم إنشاء الصفحة 2 بواسطة العرض التالي [Action17Get.cshtml]:


@{
  Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Action17Get</title>
</head>
<body>
  <h3>Navigation - Page 2</h3>
  <h4>@ViewBag.info</h4>
  <p>
    @Html.ActionLink("Page 1", "Action16Get", "Second")
  </p>
</body>
</html>

والإجراءات التي تؤدي إلى ظهور هذه المشاهدات هي كما يلي:


      // Action16-GET
      [HttpGet]
      public ViewResult Action16Get()
      {
        ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
        return View("Action16Get");
      }
 
      // Action16-POST
      [HttpPost]
      public ViewResult Action16Post(string data)
      {
        ViewBag.info = string.Format("Contrôleur={0}, Action={1}, Data={2}", RouteData.Values["controller"], RouteData.Values["action"], data);
        return View("Action16Get");
      }
 
      // Action17-GET
      [HttpGet]
      public ViewResult Action17Get()
      {
        ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
        return View();
}

  • السطر 6، تقوم الإجراء [Action16Get] بعرض العرض [Action16Get.cshtml]، أي الصفحة 1 من المثال. يعتمد هذا العرض على [ViewBag] (السطر 5)؛
  • السطر 19: يقوم الإجراء [Action17Get] بإنشاء العرض [Action17Get.cshtml]، أي الصفحة 2 من المثال. يعتمد هذا العرض على [ViewBag] (السطر 21)؛
  • السطر 11: تعالج الإجراء [Action16Post] طلب POST من النموذج في عرض [Action16Get.cshtml]. وتستقبل المعلمة المسماة [data]. تذكر أن هذا هو اسم حقل الإدخال في النموذج؛
  • السطر 13: يتم وضع المعلومات في [ViewBag
  • السطر 14: يتم عرض عرض [Action16Get.cshtml].

نشجع القراء على تجربة هذا المثال.