Skip to content

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

5.1. Introduction

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

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

لنبدأ بإنشاء مشروع جديد [Exemple-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]. تسمح هذه الطريقة بالعديد من التحميلات الزائدة. سنستعرض بعضها. أهمها هو التالي:
 
  • المعلمة الأولى هي اسم العرض. إذا كانت غائبة، فإن العرض المستخدم هو الذي يحمل نفس اسم الإجراء الذي ينتج [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]:

 

الرمز 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] هو كما يلي:


    // الإجراء 01
    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]:

 

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

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


<h4>@ViewBag.Info</h4>

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

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


    // الإجراء 10
    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] من المشروع [Exemple-02] إلى المشروع الحالي [Exemple-03]:

ونقوم بتغيير مساحة الاسم الخاصة به لتتوافق مع تلك الخاصة بالمشروع [Exemple-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, Jour, Info1, Info2, Info3
  • السطر 5: نضيف إليها الخاصية [Erreurs].

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

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

ورمزها هو التالي:


    // الإجراء 02
    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] في سلسلة الأحرف [erreurs]. تم وصف الطريقة [getErrorMessagesFor] في الصفحة 65 وتم تضمينها في وحدة التحكم [First] للمشروع الجديد؛
  • السطر 5: يتم استدعاء الطريقة [View] بمعلمة واحدة. وهي نموذج العرض. لم يتم تحديد هذا الأخير. لذلك سيتم استخدام العرض الافتراضي [/Views/First/Action02]. يتم إنشاء مثيل لنموذج العرض [ViewModel01] وتهيئته باستخدام المعلومات الخمسة لنموذج الإجراء [ActionModel03] والمعلومات [erreurs] التي تم إنشاؤها في السطر 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].

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

 

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

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

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

  

والذي سيكون كما يلي:


using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace Exemple_03.Models
{
  [Bind(Exclude="Erreurs")]
  public class ActionModel04
  {
    // ---------------------- الإجراء --------------------------------
    [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; }

    // ---------------------- عرض --------------------------------
    public string Erreurs { get; set; }
  }
}
  • الأسطر 8-28: نموذج الإجراء مع قيود التكامل الخاصة به. ستكون هذه الحقول أيضًا جزءًا من العرض؛
  • السطر 31: خاصية خاصة بنموذج العرض. تم استبعادها من نموذج الإجراء بواسطة التعليق التوضيحي في السطر 5.

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


    // الإجراء 03
    public ViewResult Action03(ActionModel04 modèle)
    {
      modèle.Erreurs = getErrorMessagesFor(ModelState);
      return View(modèle);
}
  • السطر 2: تتلقى [Action03] نموذج الإجراء من النوع [ActionModel04
  • السطر 5: ويقدم هذا النموذج نفسه كنموذج عرض؛
  • السطر 4: مكمل بالمعلومات [Erreurs

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

  • في [1]: انقر بزر الماوس الأيمن على كود [Action03] ثم [Ajouter une vue
  • إلى [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] بدون معلمات:

 

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

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: يحتوي النموذج على مصفوفة من الأشخاص من النوع [Personne] المحددة في الأسطر 12-16؛
  • الأسطر 6-10: يقوم منشئ النموذج بتهيئة الخاصية [Personnes] في السطر 5 بمصفوفة من شخصين.

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


    // الإجراء 04
    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: استيراد مساحة اسم الفئة [Personne] المستخدمة في السطر 24؛
  • الأسطر 16-32: المصفوفة HTML التي تعرض الأشخاص في النموذج؛
  • السطر 24: يشار إلى بداية كود C# بالحرف @. ستقوم التعليمات [foreach] بالتكرار على جميع الأشخاص في النموذج؛
  • السطور 26-27: الحرف < يوقف C# ويبدأ HTML. ثم مرة أخرى، الحرف @ للتبديل إلى C# وكتابة اسم الشخص. ثم مرة أخرى الحرف < الذي يبدل إلى وضع HTML؛
  • السطر 28: يتم كتابة عمر الشخص.

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

 

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

سيكون النموذج [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: فئة [Personne2] مع ثلاث خصائص؛
  • السطر 3: النموذج [ViewModel05] للعرض؛
  • السطر 5: قائمة الأشخاص المراد عرضهم في القائمة المنسدلة بالشكل [Prénom Nom
  • السطر 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="cmbValeurs">
<option value="1">الخيار 1</option>
<option selected="selected" value="2">الخيار 2</option>
<option value="3">الخيار 3</option>
</select>
 
علامة HTML
<select size=".." name="..">
<option [selected="selected"] value=”v”>...</option>
...
</select>
يعرض في قائمة النصوص الموجودة بين العلامات <option>...</option>
السمات
name="cmbValeurs": اسم عنصر التحكم.
size="1": عدد عناصر القائمة المرئية. size="1" يجعل القائمة تعادل مربع الاختيار.
selected="selected": إذا كانت هذه الكلمة الرئيسية موجودة لعنصر في القائمة، يظهر هذا العنصر محددًا في القائمة. في المثال أعلاه، يظهر عنصر القائمة choix2 كعنصر محدد في مربع الاختيار عند عرضه لأول مرة.
value=”v”: إذا تم تحديد العنصر من قبل المستخدم، فإن القيمة [v] هي التي يتم إرسالها إلى الخادم. في حالة عدم وجود هذا السمة، يتم إرسال النص المعروض والمحدد إلى الخادم.

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

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

نطلب الإجراء [Action05]:

  • في [1,2]، يتم عرض الأشخاص بالشكل [Prénom Nom
  • في [1,2]، الشخص المحدد هو الذي يساوي [Id] فيه 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، يجب أن تكون المنطقية في الإجراء أو في الطبقات السفلية [Metier, DAO] ولكن ليس في العرض. حتى عند الالتزام بنموذج MVC، قد نجد أنفسنا أمام الكثير من المنطق في العرض لحساب القيم الوسيطة. قد يعني هذا أن النموذج المستخدم ليس مفصلاً بما يكفي. يجب أن يحتوي هذا النموذج على القيم النهائية التي يحتاجها العرض حتى لا يضطر إلى حسابها بنفسه. العرض الجيد هو العرض الذي يحتوي على الحد الأدنى من المنطق وتبقى فيه بنية العرض HTML واضحة. إذا تم إدراج الكثير من كود C#، فقد تصبح بنية العرض HTML غير قابلة للقراءة.

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

5.5. النموذج – الخطوات الأولى

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

 

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


    // الإجراء 06-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="frmexemple": اسم النموذج - اختياري
method="..." : الطريقة التي يستخدمها المتصفح لإرسال القيم التي تم جمعها في النموذج إلى خادم الويب
action="..." : URL التي سيتم إرسال القيم التي تم جمعها في النموذج إليها.
يُحاط النموذج الإلكتروني بعلامات <form>...</form>. يمكن أن يكون للنموذج اسم (name="xx"). وينطبق ذلك على جميع عناصر التحكم الموجودة في النموذج. الغرض من النموذج هو جمع المعلومات التي يدخلها المستخدم عبر لوحة المفاتيح/الماوس وإرسالها إلى عنوان خادم ويب. أي عنوان؟ العنوان المشار إليه في السمة action="URL". إذا كان هذا السمة غير موجود، فسيتم إرسال المعلومات إلى عنوان الويب الخاص بالوثيقة التي يوجد فيها النموذج. يمكن لعميل الويب استخدام طريقتين مختلفتين تسميان POST و GET لإرسال البيانات إلى خادم ويب. السمة method="méthode"، مع method يساوي GET أو POST، في العلامة <form> يحدد للمتصفح الطريقة التي يجب استخدامها لإرسال المعلومات التي تم جمعها في النموذج إلى URL المحدد بواسطة السمة action="URL". عندما لا يتم تحديد السمة method، يتم استخدام الطريقة GET بشكل افتراضي.
  • السطر 18: نرى أن قيم النموذج ستُرسل إلى URL [/First/Action06] بواسطة أمر HTTP POST؛
  • السطر 30: يجب أن يحتوي النموذج على زر من النوع [submit]. وهو الذي يطلق عملية إرسال القيم المدخلة إلى URL المحددة بواسطة السمة [action] لعلامة <form>.

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


عنصر التحكم HTML


مرئي


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

<input type="radio" value="نعم" name="R1"/>نعم
<input type="radio" name="R1" value="لا" checked="checked"/>لا
R1=نعم
- قيمة السمة value للزر الإذاعي الذي حدده المستخدم.
<input type="checkbox" name="C1" value="واحد"/>1
<input type="checkbox" name="C2" value="deux" checked="checked"/>2
<input type="checkbox" name="C3" value="ثلاثة"/>3
C1=واحد
C2=اثنان
- قيم سمات value للمربعات التي حددها المستخدم
<input type="text" name="txtSaisie" size="20" value="بضع كلمات"/>
txtSaisie=برمجة+الويب
- النص الذي كتبه المستخدم في حقل الإدخال. تم استبدال المسافات بعلامة +
<input type="password" name="txtMdp" size="20" value="unMotDePasse"/>
txtMdp=هذا_سر
- النص الذي يكتبه المستخدم في حقل الإدخال
<textarea rows="2" name="areaSaisie" cols="20">
السطر 1
السطر 2
السطر 3
</textarea>
areaSaisie=أساسيات+البرمجة+الويب%0D%0A
البرمجة+الويب
- نص كتبته المستخدم في حقل الإدخال. %OD%OA هي علامة نهاية السطر. تم استبدال المسافات بعلامة +
<select size="1" name="cmbValeurs">
<option value='1'>الخيار 1</option>
<option selected="selected" value='2'>الخيار 2</option>
<option value='3'>الخيار 3</option>
</select>
cmbValeurs=3
- سمة [value] للعنصر الذي اختاره المستخدم
<select size="3" name="lst1">
<option selected="selected" value='1'>القائمة 1</option>
<option value='2'>القائمة 2</option>
<option value='3'>القائمة 3</option>
<option value='4'>القائمة 4</option>
<option value='5'>القائمة 5</option>
</select>
lst1=3
- سمة [value] للعنصر الذي اختاره المستخدم
<select size="3" name="lst2" multiple="multiple">
<option selected="selected" value='1'>القائمة 1</option>
<option value='2'>القائمة 2</option>
<option selected="selected" value='3'>القائمة 3</option>
<option value='4'>القائمة 4</option>
<option value='5'>القائمة 5</option>
</select>
lst2=1
lst2=3
- سمات [value] للعناصر التي اختارها المستخدم
<input type="submit" value="إرسال" name="cmdRenvoyer"/>
 
cmdRenvoyer=إرسال
- اسم وسمة value للزر الذي استُخدم لإرسال بيانات النموذج إلى الخادم
<input type="hidden" name="secret" value="uneValeur"/>
 
secret=قيمة
- سمة value للحقل المخفي

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


    <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] التالية:


    // الإجراء 06-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، قد نرغب في عرض nom و prénom للشخص المحدد بدلاً من رقمه. لذلك، يجب تطوير عرض POST ونموذجه.

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

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


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

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

  

using System.Web.Mvc;

namespace Exemple_03.Infrastructure
{
  public class SessionModelBinder : IModelBinder
  {
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
      // نقوم بإرجاع بيانات النطاق [Session]
      return controllerContext.HttpContext.Session["data"];
    }
  }
}

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


public class MvcApplication : System.Web.HttpApplication
  {
    protected void Application_Start()
    {
      ...

      // مربط النماذج
      ModelBinders.Binders.Add(typeof(SessionModel), new SessionModelBinder());
    }
    // الجلسة
    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: يتم عرض سلسلة الأحرف.

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

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

لقد درسنا في الفقرة 2.5.2.1 النموذج HTML التالي:

1
 

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

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

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

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

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


    protected void Application_Start()
    {
....

      // مربط النماذج
      ModelBinders.Binders.Add(typeof(SessionModel), new SessionModelBinder());
      ModelBinders.Binders.Add(typeof(ApplicationModel), new ApplicationModelBinder());

      // بيانات النطاق [Application]
      Application["data"] = new ApplicationModel();
}
  • السطر 7: النوع [ApplicationModel] الذي سنصفه قريبًا مرتبط بمربط البيانات [ApplicationModelBinder] الذي قدمناه بالفعل في الصفحة 82؛
  • السطر 10: يتم تسجيل مثيل من النوع [ApplicationModel] في قاموس التطبيق، مرتبطًا بالمفتاح [data].

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


namespace Exemple_03.Models
{
  public class ApplicationModel
  {
    // المجموعات المراد عرضها في النموذج
    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; }

    // تهيئة الحقول والمجموعات
    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"}
      };
    }
    // عنصر المجموعات
    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. نموذج العرض [Formulaire]

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


using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
using Exemple_03.Models;

namespace Exemple_03.Models
{
  public class ViewModel08
  {
    // حقول الإدخال
    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; }

    // المجموعات المراد عرضها في النموذج
    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; }

    // المنشئون
    public ViewModel08()
    {
    }

    public ViewModel08(ApplicationModel application)
    {
      // تهيئة المجموعات
      RadioButtonFieldItems = application.RadioButtonFieldItems;
      CheckBoxesFieldItems = application.CheckBoxesFieldItems;
      DropDownListFieldItems = application.DropDownListFieldItems;
      SimpleChoiceListFieldItems = application.SimpleChoiceListFieldItems;
      MultipleChoiceListFieldItems = application.MultipleChoiceListFieldItems;
      // تهيئة الحقول
      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] القيمة التي تم إرسالها عبر الأسطر التالية من النموذج:

        <!-- أزرار الاختيار -->
        <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

إذا قام المستخدم بتحديد الخيار المسمى [non]. ففي الواقع، يتم إرسال السمة [value] للخيار المحدد.

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

        <!-- مربعات الاختيار -->
        <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] القيمة التي تم إرسالها من خلال الأسطر التالية من النموذج:

          <!-- حقل إدخال النص أحادي السطر -->
          <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] القيمة التي تم إدخالها في الأسطر التالية من النموذج:

        <!-- حقل إدخال كلمة المرور -->
        <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] القيمة التي تم إرسالها من خلال الأسطر التالية من النموذج:

        <!-- حقل إدخال النص متعدد الأسطر -->
        <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] القيمة المرسلة من خلال الأسطر التالية من النموذج:

        <!-- القائمة المنسدلة -->
        <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

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

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

        <!-- قائمة الاختيار الأحادي -->
        <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] القيم التي تم إرسالها من خلال الأسطر التالية من النموذج:

        <!-- قائمة الاختيار من متعدد -->
        <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

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

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


      // تهيئة الحقول
      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: حدد الخيار [non] من زر الاختيار؛
  • السطر 3: حدد الخيار [2] من مربعات الاختيار؛
  • السطر 4: أدخل [quelques mots] في حقل الإدخال؛
  • السطر 5: كتابة [secret] ككلمة مرور؛
  • السطر 6: اكتب [ligne1\nligne2] في حقل الإدخال متعدد الأسطر؛
  • السطر 7: حدد الخيار [choix2] من القائمة المنسدلة؛
  • السطر 8: حدد الخيار [liste3] من قائمة الاختيار الفردي؛
  • السطر 9: حدد الخيارين [liste1] و [liste3] من قائمة الاختيار المتعدد؛

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

5.6.4. عرض [Formulaire]

تعرض طريقة العرض [/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>
        <!-- أزرار الاختيار -->
        <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] (السمة method) إلى URL [/First/Action08Post] (السمة action
  • السطر 33: الزر من النوع [submit] الذي يستخدم لإرسال النموذج؛
  • الأسطر 22-27: تعرض أزرار الاختيار:
  
  • السطر 22: يتم تصفح المجموعة المعروضة بواسطة زر الاختيار؛
  • السطر 24: يجب تحديد الزر الذي يحتوي على السمة [value] بقيمة الخاصية [RadioButtonField]. ولذلك، يجب أن يحتوي على السمة [checked="checked"]؛
  • السطر 25: إنشاء العلامة <input type="radio"> بقيمة [@item.Value] وتسمية [@item.Label
  • السطر 26: العلامة <text/> ليست علامة HTML معترف بها. وهي موجودة من أجل [Razor]. عند ظهورها، ستقوم [Razor] بإنشاء فاصل أسطر. لا يؤثر ذلك على النموذج المعروض ولكنه يؤثر على الكود HTML الذي تم إنشاؤه. تصبح علامات <input type="radio"> عندئذٍ على سطرين مختلفين بدلاً من أن تكون على نفس السطر. وهذا يجعل الكود أكثر قابلية للقراءة، عندما نطلب من المتصفح عرض الكود المصدري للصفحة المعروضة؛

نستعرض العناصر الأخرى في العرض:


        <!-- مربعات الاختيار -->
        <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

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

        <!-- القائمة المنسدلة -->
        <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="valeur">libellé</option> بقيمة [@item.Value] وتسمية [@item.Label

        <!-- قائمة الاختيار الفردي -->
        <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>

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


        <!-- قائمة الاختيار المتعدد -->
        <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="valeur">libellé</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، والمسار المستخدم، ومعلمات ملف uploadé. هنا، نريد تهيئة النموذج فقط بالقيم التي تم إرسالها.
  • السطر 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);
      // معالجة القيم غير المرسلة
      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[] { };
      }
      // عرض النموذج
      return View("Formulaire", modèle);
    }
  • الأسطر 9-20: يتم التحقق مما إذا كانت بعض المعلمات قد تم إرسالها أم لا. إذا لم يكن الأمر كذلك، يتم تهيئتها بالقيمة التي تتوافق مع عدم إدخال المستخدم لأي بيانات. لم يتم إجراء الاختبار على القائمة المنسدلة التي تحتوي دائمًا على عنصر محدد، وهو ما لا ينطبق على القوائم الأخرى.

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

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

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

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

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


        <!-- القائمة المنسدلة -->
        <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 Helpers] والتي، كما يوحي اسمها، تهدف إلى تسهيل إنشاء HTML، خاصةً بالنسبة للنماذج. باستخدام هذه الفئات، تُكتب القائمة المنسدلة السابقة على النحو التالي:


        <!-- القائمة المنسدلة -->
        <tr>
          <td>Liste déroulante</td>
          <td>@Html.DropDownListFor(m => m.DropDownListField,
           new SelectList(@Model.DropDownListFieldItems, "Value", "Label"))
          </td>
</tr>

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


        <!-- القائمة المنسدلة -->
        <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"))
  • المعلمة الأولى هي دالة lambda (هذا هو اسمها) حيث يمثل m نموذج العرض و m.DropDowListField هي خاصية لهذا النموذج. سيستخدم مولد الكود HTML اسم هذه الخاصية لتوليد السمات [id] و [name] لـ [select] الذي سيتم توليده. إذا تم استخدام خاصية غير موجودة، فسيظهر خطأ عند التحويل البرمجي وليس عند التنفيذ. وهذا يمثل تحسناً مقارنة بالحل السابق حيث لم تكن أخطاء التسمية تُكتشف إلا عند التنفيذ؛
  • يستخدم المعلمة الثانية لتعيين مجموعة العناصر التي ستغذي القائمة المنسدلة. تسمح الفئة [SelectList] بإنشاء هذه المجموعة:
    • المعلمة الأولى هي مجموعة عشوائية من العناصر. هنا لدينا مجموعة من النوع [Item
    • المعلمة الثانية هي خاصية العناصر التي ستوفر قيمة العلامة <option>. هنا، هي الخاصية [Value] للفئة [Item
    • المعلمة الثالثة هي خاصية العناصر التي ستوفر نص العلامة <option>. هنا، هي الخاصية [Label] للفئة [Item
  • لمعرفة الخيار الذي يجب تحديده (السمة selected)، يتصرف إطار العمل مثلنا: فهو يقارن قيمة الخيار بالقيمة الحالية للخاصية [DropDownListField].

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

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

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


        <!-- أزرار الاختيار -->
        <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 الذي تم إنشاؤه هو كما يلي:


        <!-- أزرار الاختيار -->
        <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]).

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

يتطور الرمز على النحو التالي:


        <!-- مربعات الاختيار -->
        <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é)

المعلمة هي الخاصية المنطقية للنموذج التي سيتم ربطها بمربع الاختيار. إذا كان الرمز [Propriété=true]، فسيتم تحديد المربع. إذا كان الرمز [Propriété=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) يحمل نفس اسم [Propriété] مثل مربع الاختيار الذي يحمل السمة [value="false"]. لماذا يوجد علامتان [input] تحملان نفس الاسم؟ هناك حالتان:
  • مربع الاختيار في السطر 1 محدد. وبالتالي، فإن سلسلة المعلمات المرسلة هي Property=true&Property=false (السطران 1 و2). نظرًا لأن الخاصية [Propriété] لا تتوقع سوى قيمة واحدة، يمكننا أن نفترض أن إطار العمل يعين القيمة [true] إلى [Propriété]. ويكفيه إجراء عملية منطقية OU بين القيم المستلمة لتحقيق ذلك؛
  • ولكن المربع في السطر 1 غير محدد. وبالتالي، فإن سلسلة المعلمات المرسلة هي Property=false (السطر 2 فقط)، ومن ثم تتلقى الخاصية [Propriété] القيمة [false]، وهو أمر صحيح (لم يتم تحديد المربع).

حقل إدخال أحادي السطر

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


          <!-- حقل إدخال النص أحادي السطر -->
          <tr>
            <td>Champ de saisie</td>
            <td>
              @Html.TextBoxFor(m => m.TextField, new { size = "30" })
            </td>
</tr>

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


          <!-- حقل إدخال النص أحادي السطر -->
          <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].

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

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


        <!-- حقل إدخال كلمة المرور -->
        <tr>
          <td>Mot de passe</td>
          <td>
            @Html.PasswordFor(m => m.PasswordField, new { size = "15" })
          </td>
</tr>

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


        <!-- حقل إدخال كلمة المرور -->
        <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.TexBoxFor].

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

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


        <!-- حقل إدخال النص متعدد الأسطر -->
        <tr>
          <td>Boîte de saisie</td>
          <td>
            @Html.TextAreaFor(m => m.TextAreaField, new { cols = "30", rows = "5" })
          </td>
</tr>

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


        <!-- حقل إدخال النص متعدد الأسطر -->
        <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.TexBoxFor].

قائمة الاختيار الأحادي

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


        <!-- قائمة الاختيار الفردي -->
        <tr>
          <td>Liste à choix unique</td>
          <td>
          @Html.DropDownListFor(m => m.SimpleChoiceListField, new SelectList(@Model.SimpleChoiceListFieldItems, "Value", "Label"), new { size = "3" })
</tr>

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


        <!-- قائمة الاختيار الفردي -->
        <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] إلى قائمة بسيطة.

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

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


        <!-- قائمة الاختيار المتعدد -->
        <tr>
          <td>Liste à choix multiple</td>
          <td>
          @Html.ListBoxFor(m => m.MultipleChoiceListField, new SelectList(@Model.MultipleChoiceListFieldItems, "Value", "Label"), new { size = "5" })
</tr>

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


        <!-- قائمة الاختيار المتعدد -->
        <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 هو [Formulaire2] المرتبط بالنموذج [ViewModel09] التالي:


using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
using Exemple_03.Models;

namespace Exemple_03.Models
{
  public class ViewModel09
  {
    // حقول الإدخال
    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; }

    // المجموعات المراد عرضها في النموذج
    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; }

    // المنشئون
    public ViewModel09()
    {
    }

    public ViewModel09(ApplicationModel application)
    {
      // تهيئة المجموعات
      RadioButtonFieldItems = application.RadioButtonFieldItems;
      CheckBoxesFieldItems = application.CheckBoxesFieldItems;
      DropDownListFieldItems = application.DropDownListFieldItems;
      SimpleChoiceListFieldItems = application.SimpleChoiceListFieldItems;
      MultipleChoiceListFieldItems = application.MultipleChoiceListFieldItems;
      // تهيئة الحقول
      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);
      // معالجة القيم غير المرسلة
      if (posted["SimpleChoiceListField"] == null)
      {
        modèle.SimpleChoiceListField = "";
      }
      if (posted["MultipleChoiceListField"] == null)
      {
        modèle.MultipleChoiceListField = new string[] { };
      }
      // عرض النموذج
      return View("Formulaire2", modèle);
}

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

  • السطر 18: يتم استخدام العرض [Formulaire2] بدلاً من العرض [Formulaire
  • لم يعد هناك إدارة لمربعات الاختيار التي لم يتم تحديدها. يتم الآن إدارة ذلك بشكل صحيح بواسطة الطريقة [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; }

    // المنشئ
    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>

الصورة

 

تم إنشاء 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]. تم تعيين قيمة [text] للسمة [type] بسبب البيانات الوصفية

[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>

الصورة

 

تم إنشاء 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>

الصورة

 

تم إنشاء 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]. السمات [data-val] و [data-val-number] و [data-val-required] هي سمات غير معترف بها من قبل HTML5. يتم استخدامها بواسطة إطار عمل جافا سكريبت للتحقق من صحة البيانات من جانب العميل؛
  • وقد أنتجت الطريقة [Html.DisplayFor] نص السطر 4، وهو قيمة الخاصية.

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

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

يتم إدخال رقم خاطئ والتحقق من صحته:

 

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

مثال آخر، لم يتم إدخال أي شيء:

في [1] أعلاه، يشير [Action10Post] إلى وجود خطأ. ربما نتذكر أننا حصلنا بالفعل على هذا السلوك باستخدام السمة [Required] على الخاصية المراد التحكم فيها (انظر الصفحة 69)، وهي هنا الخاصية [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>

الصورة

 

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>

الصورة

 

تم إنشاء 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. خاصية [Date]

التعريف


    [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>

الصورة

 

تم إنشاء 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 ويسمح بإدخال التاريخ باستخدام تقويم. من ناحية أخرى، يتم عرض التاريخ الذي تم إدخاله بتنسيق [jj/mm/aaaa]، أي أن Chrome يقوم بتكييف تنسيق التاريخ مع تنسيق [locale] الخاص بالمتصفح.

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

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

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

لا يتم الإبلاغ عن عدم وجود تاريخ من جانب العميل ولكن يتم الإبلاغ عنه من جانب الخادم [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>

الصورة

 

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].

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

من الناحية الفنية، لا يمكن إدخال وقت غير صالح. يتم الإبلاغ عن عدم وجود وقت من جانب الخادم:

 

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>

الصورة

 

تم إنشاء HTML


      <tr>
        <td><label for="HiddenInput">HiddenInput</label></td>
        <td>cach&#233;<input id="HiddenInput" name="HiddenInput" type="hidden" value="مخفي" /></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>

الصورة

 

تم إنشاء 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>

الصورة

 

تم إنشاء 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>

الصورة

 

تم إنشاء 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>

الصورة

 

تم إنشاء 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>

الصورة

 

تم إنشاء 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>

الصورة

 

تم إنشاء 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; }

    // التحقق
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
      List<ValidationResult> résultats = new List<ValidationResult>();
      // التاريخ 1
      if (Date1.Date <= DateTime.Now.Date)
      {
        résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Date1" }));
      }
      // البريد الإلكتروني 1
      try
      {
        new MailAddress(Email1);
      }
      catch
      {
        résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Email1" }));
      }
      // عرض قائمة الأخطاء
      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] لعرض النموذج:


    // الإجراء 11-GET
    [HttpGet]
    public ViewResult Action11Get()
    {
      return View("Action11Get", new ViewModel11());
}

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


    // الإجراء 11-POST
    [HttpPost]
    public ViewResult Action11Post(ViewModel11 modèle)
    {
      return View("Action11Get", modèle);
}
  • السطر 3: يتم إنشاء النموذج [ViewModel11] ثم تهيئته بالقيم المرسلة. وقد تحدث أخطاء عندئذٍ. ترتبط رسالة خطأ بكل خاصية خاطئة P في النموذج. وهذه الرسالة هي التي تتيح الحصول على طريقة [Html.ValidationMessageFor] للنموذج.

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

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

 

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


    // التحقق من الصحة
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
      List<ValidationResult> résultats = new List<ValidationResult>();
      // التاريخ 1
      if (Date1.Date <= DateTime.Now.Date)
      {
        résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Date1" }));
      }
      // البريد الإلكتروني 1
      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" }));
      }

      // عرض قائمة الأخطاء
      return résultats;
}

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

 

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

تمت جميع عمليات التحقق السابقة من صحة البيانات من جانب الخادم. لذلك، يلزم إجراء تبادل للبيانات بين العميل والخادم حتى يلاحظ المستخدم أخطائه. تستخدم عملية التحقق من صحة البيانات من جانب العميل كود جافا سكريبت لإبلاغ المستخدم بأخطائه في أقرب وقت ممكن، وعلى أي حال قبل تنفيذ 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. تتضمن هذه الأسطر في طريقة العرض نصوص جافا سكريبت اللازمة للتحقق من صحة البيانات من جانب العميل. توجد هذه البرامج النصية في المجلد [Scripts] الخاص بالمشروع:

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

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


    // الإجراء 12-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] لم يحدث. ولكن النقر على الزر [Valider] أدى إلى تشغيل جميع عمليات التحقق من صحة البيانات من جانب العميل وظهرت رسائل خطأ جديدة.

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


        <tr>
          <td><label for="Chaine1">Cha&#238؛لا يقل عن أربعة أحرف</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] للعلامات التي تم إنشاؤها بواسطة جافا سكريبت الذي قمنا بتضمينه في العرض. في حالة عدم وجوده، يتم تجاهل هذه السمات ببساطة ولن يكون هناك تحقق من صحة البيانات من جانب العميل. نعمل كما في المثال السابق. ومن هنا جاء مصطلح [unobtrusive] لهذه التقنية.

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() {
      // يتم استرداد نموذج المستند
      var form = document.forms[0];
      // الإرسال
      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]. عند النقر عليه، يتم تنفيذ وظيفة جافا سكريبت [postForm] (السمة href). يتم تعريف هذه الوظيفة في الأسطر 12-17؛
  • السطر 14: يتم استرداد مرجع للنموذج الأول في المستند، وهو النموذج الموجود في السطر 23؛
  • السطر 16: يتم إرسال هذا النموذج. في النهاية، يحدث كل شيء كما لو تم النقر على زر من النوع [submit]. يتم إرسال النموذج إلى وحدة التحكم والإجراء المحددين في السطر 23؛
  • السطر 30: رابط تنقل. الرمز HTML الذي تم إنشاؤه هو التالي:

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

الطريقة المستخدمة هي ActionLink(نص، إجراء، وحدة تحكم).

يتم إنشاء الصفحة 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].

يُطلب من القارئ تجربة هذا المثال.