أهم أقسام المدونة

الصفحات

الخميس، 4 أبريل 2019

دليل استخدام Vazor

شرح استخدام Vazor لإنشاء مشاريع ASP.NET Core
بلغة فيجوال بيزيك دوت نت
صدرت VS.NET 2019 أول أمس، ولا أرى فيها أي جديد، لأن فيجوال بيزيك 16 و سي شارب 8 و دوت نت كور 3 كلها لم تصدر بعد وأمامها شهران أو ثلاثة!
بل والمستفز أكثر أن VS.NET 2019 نزلت بإصدار دوت نت كور 2.1 وليس 2.2، وعلى من يريد هذا الإصدار الأخير فعليه تنزيله وإعداده في خطوة مستقلة!
لكل هذا قررت أن أصنع نسخة من Vazor تعمل بالإصدار 2.2 من نواة دوت نت، حتى يمكنكم تجربتها في دوت نت 2017.. تجدونها هنا:
يمكنكم تنزيل هذا المشروع إلى أجهزتكم بضغط الزر المنسدل Clone or download ، واختيار Download Zip.
وكما ذكرت، فإن هذه النسخة تحتاج إلى دوت نت كور 2.2.. إذا لم يكن لديكم، فيمكنكم تنزيله من هذه الصفحة:
مع ضرورة اختيار رابط التنزيل من العمود الأوسط لأنه يحتوي على النسخ الخاصة بالمبرمجين وليس المستخدمين، مع التأكد من اختيار نسخة Windows Installer x84 للأجهزة التي عليها ويندوز 32 بت، ونسخة Windows Installer x64 لنسخ ويندوز 64 بت.
وبعد تنزيل هذه النسخة برنامج الإعداد، قم بتشغيله وواصل خطوات الإعداد معه.
والآن فكوا ضغط ملف المشروع، واضغطوا الملف Vazor.sln لفتح المشروع في فيجوال ستديو.. هذا المشروع يحتوي على ثلاثة مشاريع فرعية وهي:

1- Vazor:
هذا المشروع مكتبة كود ينتج عنه الملف Vazor.dll في المجلد debug/bin.. هذا الملف هو الذي تستخدمه في مشاريعك (بإضافة مرجع Reference إليه) لجعل Razor يعرض صفحات العرض التي تكتبها بكود vbxml بدلا من cshtml.. لاحظ أن المشروعين الآخرين يضيفان مرجعا للمشروع Vazor نفسه لهذا يستطيعان استخدام الفئات الموجودة فيه.
وقد بسطت المشروع Vazor جدا ليكون استخدامه سهلا، كما سترون في الشرح التالي.
مستقبلا، سأوفر Vazor.dll كحزمة إضافية Nuget بإذن الله، حتى يمكن استخدامه في مشاريعكم بخطوة واحدة، لكن المشكلة الآن أننا لا نملك قالبا لمشاريع ASP.NET بلغة فيجوال بيزيك.. أنشأت أحد هذه القوالب على جهازي (وهو أمر ليس معقدا)، لكني ما زلت أطور في المشروع وسيكون كل شيء عرضة للتغير، لهذا من الأفضل أن يكون إنشاء القالب آخر خطوة.
لهذا يجب عليكم استخدام مشروعي فيجوال بيزيك المرفقين كقالب.
2- WepApp1:
هذا مشروع ASP.NET Core MVC مكتوب بلغة فيجوال بيزيك دوت نت.
3- VazorPages1:
هذا مشروع ASP.NET Core Razor Pages مكتوب بلغة فيجوال بيزيك دوت نت.
لاحظ أن كلا من المشروعين الأخيرين يحتوي على بعض صفحات العرض بصيغة cshtml وبعضها الآخر بصيغة vbxml كمثال على تعايش النوعين معا.. وكلاهما  مجرد مثال بدائي يمكنك تعديله والبناء عليه لإنشاء مشروع خاص بك.
كيف يمكنك إنشاء صفحة عرض من النوع vbxml؟
الأمر بسيط للغاية.. اتبع هذه الخطوات في مشروع من النوع MVC:
1- حدد المجلد Views وأضف إليه مجلدا جديدا اسمه Book.. حدد هذا المجلد وأضف إليه فئة جديدة New Class اسمها Book.vazor.vb.. لاحظ أن وجود .vazor في الاسم مجرد عملية تنظيمية، ليسهل عليك معرفة محتوى كل ملف من اسمه.
2- في ملف الكود، غير اسم الفئة الجديدة من Book إلى BookView ليكون أكثر وضوحا ومنعا للالتباس مع أي فئة اسمها Book في قالب البيانات Model.. واجعل هذه الفئة ترث الفئة VazorView:
Imports Vazor
Public Class BookView
    Inherits VazorView
End Class
بمجرد أن تضغط Enter بعد الاسم كتابة Inherits VazorView سيضيف محرر الكود تعريفا للخاصية Content داخل الفئة، لأني عرفت هذه الخاصية على أنها واجبة الاستبدال MustOverride في الفئة الأم.. في هذه الخاصية اكتب الكود الذي يعيد محتوى صفحة العرض في صورة مصفوفة من الوحدات الثنائية Byte Array، لإرساله إلى Razor كأنه محتويات الصفحة التي عليه عرضها في المتصفح.. اجعل كود هذه الخاصية كالتالي:
Public Overrides ReadOnly Property Content() As Byte()
   Get
       Dim html = GetVbXml(Me).ParseTemplate(Books)
       Return Encoding.GetBytes(html)
   End Get
End Property
في الغالب هذا هو الكود الذي ستكتبه في هذه الخاصية في كل صفحات Vazor التي ستنشئها. (تعديل: لهذا السبب نقلت كود هذه الخاصية إلى الفئة VazorView ولم تعد بحاجة إلى كتابتها بنفسك.. لكن لو احتجت إلى كتابة المزيد من الكود فيها لأي سبب، فيمكنك أن تضيفها مرة أخرى لاستبدال الخاصية الاصلية، حيث يمكنك أن تعدل فيها كما تشاء.
لاحظ أن:
- الوسيلة GetVbXml هي وسيلة سنقوم بتعريفها بأنفسنا بعد قليل، لنضع فيها كود vbxml الخاص بالصفحة.. وهي تعيد كائنا من النوع XElement يحمل كود XML.
- ParseTemplate هي وسيلة إضافية للفئة XElement عرفتها في المشروع Vazor.. ومهمتها معالجة قالب البيانات ForEach = "m" كما شرحت في الموضوع السابق، وهي تعيد نصا يحمل كود HTML.
- Encoding هي خاصية معرفة في الفئة الأم VazorView، وقيمتها الافتراضية هي System.Text.Encoding.UTF8 لاستخدام الترميز UTF8 في تحويل النص إلى مصفوفة ثنائية.. يمكنك استخدام أي ترميز آخر لو أردت، بإرساله إلى حدث إنشاء هذه الفئة كما سأوضح بعد قليل.
- Books هي خاصية تقوم أنت بتعريفها لتحتوي على مجموعة من الكتب التي تريد عرضها في الصفحة.. مثلا:
Public ReadOnly Property Books As List(Of Book)
حيث Book هو اسم فئة في قالب البيانات Mode تحمل بيانات الكتاب.. لهذا قلت لك يجب أن نسمي فئة العرض BookView.
- ولو كنت تحتاج إلى التعامل مع كائن بيانات العرض ViewData في كتابة كود الصفحة، فأنشئ خاصية لوضعه فيها:
Public ReadOnly Property ViewData() As ViewDataDictionary
- والآن يجب أن نكتب حدث الإنشاء New لنستقبل عبره مجموعة الكتب المراد عرضها، وكائن بيانات العرض، ولو أردت استقبال أي شيء آخر فلك مطلق الحرية في تعريف المزيد من المعاملات لهذا الحدث:
Public Sub New(books As List(Of Book), viewData As ViewDataDictionary)
   MyBase.New("Book", "Views\Book", "قائمة الكتب")
   Me.Books = books
   Me.ViewData = viewData
   viewData("Title") = Title
End Sub
لاحظ أن أول سطر في حدث الإنشاء يجب أن يستدعي منشئ الفئة الأم MyBase.New ، ويرسل إليه البيانات التعريفية للصفحة وهي:
1- اسم الصفحة (بدون امتداد)، وهو هنا "Book".. ستوضع هذه القيمة في الخاصية Name في الفئة الأم.
2- مسار المجلد الذي فيه الصفحة، وهو هنا "Views\Book".. ستوضع هذه القيمة في الخاصية Path في الفئة الأم.
3- العنوان الذي ستعرضه الصفحة في الشريط العلوي للمتصفح، وهو هنا "قائمة الكتب".. ستوضع هذه القيمة في الخاصية Title في الفئة الأم.
4- وهناك صيغة ثانية لحدث الإنشاء تستقبل معاملا رابعا يمثل الترميز الذي تريد استخدامه لعرض الصفحة.. ستوضع هذه القيمة في الخاصية Encoding في الفئة الأم.. مثال:

MyBase.New("Book", "Views\Book", "قائمة الكتب", System.Text.Encoding.UTF7)
وهذا هو كل شيء.. هكذا ستبدو الفئة كاملة: 
Imports Microsoft.AspNetCore.Mvc.ViewFeaturesImports Vazor
Public Class BookView
    Inherits VazorView
    Public ReadOnly Property Books As List(Of Book)
    Public ReadOnly Property ViewData() As ViewDataDictionary
    Public Sub New(books As List(Of Book), viewData As ViewDataDictionary)
        MyBase.New("Book", "Views\Book", "قائمة الكتب")
        Me.Books = books
        Me.ViewData = viewData
        viewData("Title") = Title
    End Sub
    Public Overrides ReadOnly Property Content() As Byte()
       Get
           Dim html = GetVbXml(Me).ParseTemplate(Books)
           Return Encoding.GetBytes(html)
       End Get
    End Property
End Class
ملحوظة: لو أنشأت قالبا للمشروع بإذن الله، فسأجعل كل هذه الخطوات تتم آليا، بمجرد أن تختار إضافة عنصر من النوع Vazor View من مربع حوار إضافة عنصر!
والآن لم يبق إلا الخطوة الأخيرة والأهم، وهي إنشاء الدالة GetVbXml التي سنصمم فيها الصفحة.
ولتنظيم الأمور بشكل مريح، سنضيف هذه الدالة في ملف مستقل، في تعريف جزئي Partial للفئة BookView.. وسنجعل اسم هذا الملف في متصفح المشاريع Book.vbxml.vb، ونكتب فيه ما يلي:





Protected Shared Function GetVbXml(view As BookView) As XElement
        Return _
        <vbxml>
            <h3>Book names:</h3>
            <ul>
                <%= (Iterator Function()
                         For Each bk In view.books
                             Yield <li><%= bk.Name %></li>
                         Next
                     End Function)() %>
            </ul>
            <p>More details:</p>
            <ul>
                <li ForEach="bk">
                   Id: <bk.Id/><br/>
                   Name: <bk.Name/><br/>
                   <p>Author: <bk.Author/></p>
                </li>
            </ul>
            <script>
                 var x = 5;
                 document.writeln(
                   "Books count = <%= view.Books.Count() %>");
        </script>
        </vbxml>

    End Function 

End Class
 
هذه الصفحة ستعرض أسماء الكتب وتفاصيلها، وهي تتكون من كود HTML5 عادي، تم تضمين بعض كود فيجوال بيزيك فيه بين العلامتين <%= %>; المستخدمة في صفحات ASP القديمة بدلا من استخدام العلامة @ كما في Razor.
وهكذا نكون قد انتهينا من تصميم الصفحة.. بقي شيء واحد فقط، هو أن نعرضها.
الطبيعي أن يكون هناك متحكم Controller في مشروع MVC مسئول عن عرض الصفحة.. سنفترض أن لدينا متحكم اسمه Details وبداخله وسيلة أداء Action Method اسمها Book يتم استدعاؤها حينما يطلب المستخدم عرض صفحة الكتب.. سنكتب في هذه الوسيلة الكود التالي:
Public Function Index() As IActionResult
   Dim Books = GetBooksFromDB()
   Dim bv = New BookView(Books, ViewData)
   Dim viewName = Vazor.VazorViewMapper.Add(bv)
   Return View(viewName)
End Function
في البداية حصلنا على الكتب من قاعد البيانات ووضعناها في المتغير Book.. لمحاكاة هذا يمكنك أن تعرف قائمة فيها بعض الكتب وتجعل الوسيلة GetBooksFromDB تعيدها على أنها نتيجة البحث.
بعد هذا سنرسل قائمة الكتب إلى حدث إنشاء الفئة BookView هي وبيانات العرض ViewData.
الآن نحتاج إلى تسجيل هذه النسخة من الفئة BookView في خريطة الصفحات باستخدام الوسيلة VazorViewMapper.Add .. ستأخذ الصفحة اسما متفردا في الخريطة، وستعيده إلينا هذه الوسيلة.. الآن كل ما سنفعله هو أن نرسل اسم هذه الصفحة الذي حصلنا عليه إلى الوسيلة View الخاصة بالمتصفح ونطلب منها عرضها.. بهذا سيذهب Razor إلى مزود الملفات التخيلي Virtual File Provider الخاص بـ Vazor ليطلب محتويات هذه الصفحة، وسيبحث عنها المزود في الخريطة فيجدها، فيستدعي الخاصية Content التي كتبناها في الفئة BookView للحصول على محتوى الصفحة، وتسليمها إلى Razor الذي سيعرضها في المتصفح بنجاح!
لاحظ أنك لا تحتاج إلى تغيير صفحات العرض المشتركة SharedView (مثل صفحة إطار العرض Layout)، فهي لا تحتوي على كود سي شارب.. يمكنك أن تتركها بصيغة cshtml وستعمل بشكل صحيح.. لكن لو أردت إنشائها بضيغة vbxml فعليك تسجيلها في الخريطة في الوسيلة Startup.Configure باستدعاء الوسيلة AddStatic، لأن هذه الصفحات تترجم مرة واحدة فقط ويظل محتواها ثابتا عند عرضها مع كل صفحات الموقع:
Vazor.VazorViewMapper.AddStatic(New LayoutView())
وهذا هو كل شيء!
هذه هي ببساطة طريقة استخدام Vazor وكود vbxml.
ويمكنك تطبيق نفس الخطوات في مشاريع Razor Pages لكن مع مراعاة الاختلافات التي شرحتها في الموضوع السابق.
واضح أن الأمر في منتهى البساطة، لكن هناك عيب أساسي فيه، هو أن محرر الكود في ففيجوال بيزيك لا يساعدك في إكمال أسماء التيجان والسمات الخاصة ب HTML5 ولا كود جافا سكربت وأنت تكتبها في كود vbxml، وهذا سيجعل الأمر صعبا، أو ستصطر إلى كتابة هذه الأكواد في محرر HTML5 أولا (أي صفحة htm تفتحها في فيجوال ستديو) ثم تنقلها إلى صفحة vbxml لتضبط كود فيجوال بيزيك المضمن فيها، وهي طريقة متعبة بعض الشيء!
سأحل هذه المشكلة بإذن الله وسأجعل محرر الكود يراعي هذا، لكنه أمر ليس سهلا يحتاج لكتابة مكون إضافي لفيجوال ستديو وما زلت أقرأ فيه (لحل عملي سريع لهذه المشكلة، اقرأ هذا المنشور).. سيكون الأمر أسهل وأسرع طبعا لو وجدت متحمسين يساعدون في إكمال المشروع، فحتى الآن ما زلت أعمل بمفردي.. أرجو أن تدخلوا على المشروع في GitHub وتحاولوا المشاركة.

ليست هناك تعليقات:

إرسال تعليق

ملحوظة: يمكن لأعضاء المدونة فقط إرسال تعليق.