الجزء الاولالجزء الثالث5 عرض بيانات بداية البذور
أول ما
تعرضه صفحة
/WEB-INF/templates/seedstartermng.html هي قائمة تحتوي على بيانات البدء الأولية المحفوظة حاليًا. للقيام بذلك ، نحتاج إلى بعض الرسائل الخارجية ، وكذلك بعض أعمال التعبير عن سمات النموذج. مثل هذا:
<div class="seedstarterlist" th:unless="${#lists.isEmpty(allSeedStarters)}"> <h2 th:text="#{title.list}">List of Seed Starters</h2> <table> <thead> <tr> <th th:text="#{seedstarter.datePlanted}">Date Planted</th> <th th:text="#{seedstarter.covered}">Covered</th> <th th:text="#{seedstarter.type}">Type</th> <th th:text="#{seedstarter.features}">Features</th> <th th:text="#{seedstarter.rows}">Rows</th> </tr> </thead> <tbody> <tr th:each="sb : ${allSeedStarters}"> <td th:text="${{sb.datePlanted}}">13/01/2011</td> <td th:text="#{|bool.${sb.covered}|}">yes</td> <td th:text="#{|seedstarter.type.${sb.type}|}">Wireframe</td> <td th:text="${#strings.arrayJoin( #messages.arrayMsg( #strings.arrayPrepend(sb.features,'seedstarter.feature.')), ', ')}">Electric Heating, Turf</td> <td> <table> <tbody> <tr th:each="row,rowStat : ${sb.rows}"> <td th:text="${rowStat.count}">1</td> <td th:text="${row.variety.name}">Thymus Thymi</td> <td th:text="${row.seedsPerCell}">12</td> </tr> </tbody> </table> </td> </tr> </tbody> </table> </div>
هناك الكثير لنرى. دعونا نلقي نظرة على كل جزء على حدة.
بادئ ذي بدء ، سيتم عرض هذا القسم فقط إذا كان هناك مبتدئين أوليين. نحقق هذا من خلال الدالة
th: أبدا السمة
والدالة # lists.isEmpty (...) .
<div class="seedstarterlist" th:unless="${#lists.isEmpty(allSeedStarters)}">
لاحظ أن جميع كائنات الأداة المساعدة ، مثل
#lists ، متوفرة في تعبيرات EL EL بنفس طريقة تعبيرات OGNL باللهجة القياسية.
الشيء التالي الذي يجب رؤيته هو الكثير من النصوص الدولية (الخارجية) ، مثل:
<h2 th: text = "# {title.list}"> قائمة مشغلي البذور
<table> <thead> <tr> <th th:text="#{seedstarter.datePlanted}">Date Planted</th> <th th:text="#{seedstarter.covered}">Covered</th> <th th:text="#{seedstarter.type}">Type</th> <th th:text="#{seedstarter.features}">Features</th> <th th:text="#{seedstarter.rows}">Rows</th> ...
هذا هو تطبيق Spring MVC ، لقد حددنا بالفعل
MessageSource bean في تكوين Spring لدينا (كائنات
MessageSource هي الطريقة القياسية للتحكم في النصوص الخارجية في Spring MVC):
@Bean public ResourceBundleMessageSource messageSource() { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); messageSource.setBasename("Messages"); return messageSource; }
... تشير خاصية
basename هذه إلى أنه سيكون لدينا ملفات في classpath ، مثل
Messages_es.properties أو
Messages_en.properties . لنلقِ نظرة على النسخة الإسبانية:
title.list=Lista de semilleros date.format=dd/MM/yyyy bool.true=sí bool.false=no seedstarter.datePlanted=Fecha de plantación seedstarter.covered=Cubierto seedstarter.type=Tipo seedstarter.features=Características seedstarter.rows=Filas seedstarter.type.WOOD=Madera seedstarter.type.PLASTIC=Plástico seedstarter.feature.SEEDSTARTER_SPECIFIC_SUBSTRATE=Sustrato específico para semilleros seedstarter.feature.FERTILIZER=Fertilizante seedstarter.feature.PH_CORRECTOR=Corrector de PH
في العمود الأول من الجدول ، نعرض التاريخ الذي تم فيه إعداد المبتدئين. ولكن
سنبين أنه تم تنسيقه كما
حددناه في
DateFormatter . للقيام بذلك ، سوف نستخدم بناء الجملة ذي الأقواس المزدوجة (
$ {{...}} ) ، والذي سيتم تطبيقه تلقائيًا على خدمة تحويل الربيع ، بما في ذلك DateFormatter ، والتي سجلناها عند الإعداد.
<td th:text="${{sb.datePlanted}}">13/01/2011</td>
يوضح الجدول التالي ما إذا كانت حاوية البذور بداية البذور مغطاة أم لا عن طريق تحويل قيمة خاصية الصندوق المغطى المنطقي إلى نعم دولي أو لا مع تعبير بحث حرفي:
<td th:text="#{|bool.${sb.covered}|}">yes</td>
الآن نحن بحاجة إلى إظهار نوع حاوية بداية البذور الأولية. النوع عبارة عن تعداد java ذي قيمتين (
WOOD and
PLASTIC ) ، وبالتالي قمنا بتحديد خاصيتين في ملف
الرسائل الخاصين بنا مع أسماء
seedstarter.type.WOO D و
seedstarter.type.PLASTIC .
ولكن من أجل الحصول على أسماء
الأنواع الدولية ، نحتاج إلى إضافة
seedstarter.type. بادئة لقيمة التعداد باستخدام تعبير ، ونتيجة لذلك سنستخدم بعد ذلك كمفتاح للرسالة:
<td th:text="#{|seedstarter.type.${sb.type}|}">Wireframe</td>
الجزء الأصعب من هذه القائمة هو عمود
الميزة . في ذلك ، نريد عرض جميع وظائف الحاوية الخاصة بنا ، والتي يتم تقديمها كصفيف من تعدادات
الميزات ، مفصولة بفواصل. مثل "
التدفئة الكهربائية ، العشب ".
لاحظ أن هذا أمر صعب للغاية لأنه يجب أيضًا استنتاج قيم التعداد هذه ، كما فعلنا مع الأنواع. تيار الإخراج على النحو التالي:
- استبدل البادئة المناسبة بكل عناصر صفيف الميزات .
- تلقي الرسائل الخارجية المطابقة لجميع المفاتيح في الخطوة 1.
- قم بإرفاق جميع الرسائل المستلمة في الخطوة 2 ، باستخدام فاصلة كفاصل.
للقيام بذلك ، نقوم بإنشاء التعليمة البرمجية التالية:
<td th:text="${#strings.arrayJoin( #messages.arrayMsg( #strings.arrayPrepend(sb.features,'seedstarter.feature.')), ', ')}">Electric Heating, Turf</td>
سيكون العمود الأخير في قائمتنا بسيطًا جدًا. حتى إذا كان يحتوي على جدول متداخل لعرض محتويات كل صف في الحاوية:
<td> <table> <tbody> <tr th:each="row,rowStat : ${sb.rows}"> <td th:text="${rowStat.count}">1</td> <td th:text="${row.variety.name}">Thymus Thymi</td> <td th:text="${row.seedsPerCell}">12</td> </tr> </tbody> </table> </td>
6 إنشاء نماذج
6.1 معالجة كائن الأوامر
كائن الأمر هو الاسم الذي يوفره Spring MVC فاصوليا دعم النموذج ، أي الكائنات التي تقوم بنموذج حقول النموذج وتوفر أساليب get وضبط التي ستستخدمها المنصة لتعيين واسترداد القيم التي أدخلها المستخدم في المستعرض.
يتطلب Thymeleaf أن تحدد كائن أمر باستخدام سمة
th: object في
العلامة <form> :
<form action="#" th:action="@{/seedstartermng}" th:object="${seedStarter}" method="post"> ... </form>
يتماشى ذلك مع استخدام آخر لـ
th: object ، ولكن في الواقع يضيف هذا السيناريو المحدد بعض القيود للتكامل المناسب مع إطار Spring MVC:
- يجب أن تكون قيم سمة الكائن th: الموجودة في علامات النموذج عبارة عن تعبيرات متغيرة ( $ {...} ) ، مع تحديد اسم سمة النموذج فقط ، دون التنقل خلال الخصائص. هذا يعني أن تعبير مثل $ {seedStarter} صالح ، لكن $ {seedStarter.data} لن يكون صحيحًا.
- داخل العلامة <form> ، لا يمكن تحديد سمة أخرى : كائن . هذا يتفق مع حقيقة أن أشكال HTML لا يمكن أن تتداخل.
6.2 المدخلات
دعونا الآن نرى كيفية إضافة مدخلات إلى نموذجنا:
<input type="text" th:field="*{datePlanted}" />
كما ترون ، نحن نقدم سمة جديدة:
th: field . هذه ميزة مهمة للغاية لتكامل Spring MVC ، حيث إنها تقوم بكل العمل الشاق لربط
المدخلات الخاصة بك في خاصية في مكون دعم النموذج. يمكنك أن ترى أنه مكافئ لسمة المسار في علامة من مكتبة علامات Spring MVC JSP.
تتصرف سمة الحقل th: بشكل مختلف اعتمادًا على ما إذا كانت مرفقة بعلامة <input> أو <select> أو <textarea> (وأيضًا بناءً على نوع محدد من العلامة <input>). في هذه الحالة (الإدخال [type = text]) يشبه سطر الشفرة أعلاه:
<input type="text" id="datePlanted" name="datePlanted" th:value="*{datePlanted}" />
... لكنها في الواقع أكثر من ذلك بقليل ، لأن
th: field ستستخدم أيضًا خدمة تحويل Spring المسجلة ، بما في ذلك
DateFormatter ، التي رأيناها سابقًا (حتى لو لم يتم تضمين التعبير الميداني بين قوسين معقوفين). بسبب هذا ، سيتم عرض التاريخ منسق بشكل صحيح.
يجب أن تكون قيم سمات
th: field عبارة عن تعبيرات محددة (
* {...} ) ، وهذا أمر منطقي نظرًا لحقيقة أنه سيتم تقييمها على مكون يدعم النموذج ، وليس على متغيرات السياق (أو سمات النموذج في لغة Spring MVC). )
بخلاف التعبيرات في
th: object ، يمكن أن تتضمن هذه التعبيرات تنقل خاصية (في الواقع ، يُسمح بأي تعبير مسموح به لسمة المسار لعلامة <form: input> JSP هنا).
لاحظ أن th: field يتفهم أيضًا الأنواع الجديدة من عنصر <input> المقدمة في HTML5 ، مثل <input type = "datetime" ... /> ، <input type = "color" ... /> ، إلخ ، بشكل فعال مضيفا دعم HTML5 الكامل لربيع MVC.
6.3 حقول خانة الاختيار
th: يتيح لك
الحقل أيضًا تحديد إدخال مربع الاختيار للأعلام. دعونا نرى مثالا من صفحة HTML لدينا:
<div> <label th:for="${#ids.next('covered')}" th:text="#{seedstarter.covered}">Covered</label> <input type="checkbox" th:field="*{covered}" /> </div>
لاحظ أن هناك شيئًا آخر غير العلامة نفسها ، على سبيل المثال ، تسمية خارجية ، وكذلك استخدام
# ids.next ('Closed') للحصول على القيمة التي سيتم تطبيقها على سمة
المعرف للإدخال في العلامة.
لماذا نحتاج إلى إنشاء سمة
معرف بشكل ديناميكي لهذا الحقل؟ نظرًا لأن العلامات يمكن أن تكون متعددة القيم ، وبالتالي ، ستتم دائمًا إضافة لاحقة رقم التسلسل إلى قيم
معرفها ( داخليًا باستخدام
دالة # ids.seq (...) ) لضمان أن لكل علامة من علامات الإدخال الخاصة بنفس الخاصية قيمة معرف مختلفة .
سيكون من الأسهل علينا أن نرى هذا إذا نظرنا إلى مربع الاختيار المتعدد القيم:
<ul> <li th:each="feat : ${allFeatures}"> <input type="checkbox" th:field="*{features}" th:value="${feat}" /> <label th:for="${#ids.prev('features')}" th:text="#{${'seedstarter.feature.' + feat}}">Heating</label> </li> </ul>
يرجى ملاحظة أننا قمنا في هذه المرة بإضافة السمة
th: value ، لأن حقل الوظيفة ليس منطقيًا ، كما هو موضح أعلاه ، ولكنه عبارة عن مجموعة من القيم.
دعونا نرى إخراج HTML الناتجة عن هذا الرمز:
<ul> <li> <input id="features1" name="features" type="checkbox" value="SEEDSTARTER_SPECIFIC_SUBSTRATE" /> <input name="_features" type="hidden" value="on" /> <label for="features1">Seed starter-specific substrate</label> </li> <li> <input id="features2" name="features" type="checkbox" value="FERTILIZER" /> <input name="_features" type="hidden" value="on" /> <label for="features2">Fertilizer used</label> </li> <li> <input id="features3" name="features" type="checkbox" value="PH_CORRECTOR" /> <input name="_features" type="hidden" value="on" /> <label for="features3">PH Corrector used</label> </li> </ul>
سنرى هنا كيفية إضافة لاحقة التسلسل إلى كل سمة إدخال
معرف وكيف تسمح لنا الدالة
# ids.prev (...) باستخراج قيمة التسلسل الأخيرة التي تم إنشاؤها لمعرف إدخال محدد.
لا تقلق بشأن هذه المدخلات المخفية
بالاسم = "_ ميزات" : تتم إضافتها تلقائيًا لتجنب مشاكل المتصفحات التي لا ترسل قيم إشارة غير محددة إلى الخادم عند إرسال النموذج.
لاحظ أيضًا أنه إذا احتوت خاصية
الميزات الخاصة بنا على بعض القيم المحددة في وحدة دعم النسخ لدينا ، فعندئذٍ
سيحسب الحقل th: هذا ويضيف السمة
المحددة = "المحددة" إلى علامات الإدخال المقابلة.
6.4 حقول زر الراديو
يتم تعيين حقول المحولات بشكل مشابه للإشارات غير المنطقية (متعددة القيم) ، باستثناء ، بالطبع ، أنها ليست متعددة القيم:
<ul> <li th:each="ty : ${allTypes}"> <input type="radio" th:field="*{type}" th:value="${ty}" /> <label th:for="${#ids.prev('type')}" th:text="#{${'seedstarter.type.' + ty}}">Wireframe</label> </li> </ul>
6.5 محددات القائمة المنسدلة
تتكون حقول التحديد من جزأين: العلامة <تحديد> وعلامات <option> المتداخلة الخاصة بها. عند إنشاء حقل من هذا النوع ، يجب أن تتضمن العلامة <select> فقط السمة
th: field ، لكن سمات
th: value في العلامات <option> المتداخلة ستكون مهمة جدًا ، حيث إنها ستوفر فرصة لمعرفة الخيار المحدد حاليًا (على غرار العلامات غير المنطقية وأزرار الاختيار )
لنقم بإعادة إنشاء حقل نوع القائمة المنسدلة:
<select th:field="*{type}"> <option th:each="type : ${allTypes}" th:value="${type}" th:text="#{${'seedstarter.type.' + type}}">Wireframe</option> </select>
في هذه المرحلة ، يكون فهم هذه الشفرة أمرًا سهلاً للغاية. لاحظ فقط كيف تتيح لنا أولوية السمة تعيين
th: كل سمة في العلامة <option> نفسها.
6.6 الحقول الديناميكية
بفضل الإمكانيات المتقدمة لحقول النماذج الملزمة في Spring MVC ، يمكننا استخدام تعبيرات
EL EL المعقدة لربط حقول النماذج الديناميكية بفول دعم النسخ. سيتيح لنا ذلك إنشاء كائنات
صف جديدة في مكون
SeedStarter الخاص بنا وإضافة حقول هذه الأسطر إلى نموذجنا بناءً على طلب المستخدم.
للقيام بذلك ، نحتاج إلى عدد من الأساليب المعينة الجديدة في وحدة التحكم لدينا والتي تضيف أو تزيل خطًا من
SeedStarter لدينا وفقًا لتوفر بعض معلمات الطلب:
@RequestMapping(value="/seedstartermng", params={"addRow"}) public String addRow(final SeedStarter seedStarter, final BindingResult bindingResult) { seedStarter.getRows().add(new Row()); return "seedstartermng"; } @RequestMapping(value="/seedstartermng", params={"removeRow"}) public String removeRow( final SeedStarter seedStarter, final BindingResult bindingResult, final HttpServletRequest req) { final Integer rowId = Integer.valueOf(req.getParameter("removeRow")); seedStarter.getRows().remove(rowId.intValue()); return "seedstartermng"; }
والآن يمكننا إضافة جدول ديناميكي إلى نموذجنا:
<table> <thead> <tr> <th th:text="#{seedstarter.rows.head.rownum}">Row</th> <th th:text="#{seedstarter.rows.head.variety}">Variety</th> <th th:text="#{seedstarter.rows.head.seedsPerCell}">Seeds per cell</th> <th> <button type="submit" name="addRow" th:text="#{seedstarter.row.add}">Add row</button> </th> </tr> </thead> <tbody> <tr th:each="row,rowStat : *{rows}"> <td th:text="${rowStat.count}">1</td> <td> <select th:field="*{rows[__${rowStat.index}__].variety}"> <option th:each="var : ${allVarieties}" th:value="${var.id}" th:text="${var.name}">Thymus Thymi</option> </select> </td> <td> <input type="text" th:field="*{rows[__${rowStat.index}__].seedsPerCell}" /> </td> <td> <button type="submit" name="removeRow" th:value="${rowStat.index}" th:text="#{seedstarter.row.remove}">Remove row</button> </td> </tr> </tbody> </table>
هناك ما يكفي من الأشياء هنا ، لكن ليس الكثير منها حتى لا تفهم ... باستثناء شيء
غريب واحد:
<select th:field="*{rows[__${rowStat.index}__].variety}"> ... </select>
إذا كنت تتذكر من البرنامج التعليمي ، "
استخدام Thymeleaf "
، فإن بناء الجملة
__ $ {...} __ هو تعبير قبل المعالجة ، وهو تعبير داخلي يتم تقييمه قبل التقييم الفعلي للتعبير بأكمله. ولكن لماذا هذه الطريقة لتحديد مؤشر الصف؟ لن يكون ذلك كافيا مع:
<select th:field="*{rows[rowStat.index].variety}"> ... </select>
في الواقع لا. المشكلة هي أن Spring EL لا يقوم بتقييم المتغيرات في الأقواس من فهرس الصفيف ، لذلك عند تنفيذ التعبير أعلاه ، سنحصل على خطأ يخبرنا أن
الصفوف [rowStat.index] (بدلاً من
الصفوف [0] ،
الصفوف [1] ، إلخ. ) موقف غير صالح في مجموعة الصف. هذا هو سبب الحاجة إلى المعالجة المسبقة هنا.
دعونا نلقي نظرة على مقتطف من شفرة HTML الناتجة بعد النقر فوق "إضافة صف" عدة مرات:
<tbody> <tr> <td>1</td> <td> <select id="rows0.variety" name="rows[0].variety"> <option selected="selected" value="1">Thymus vulgaris</option> <option value="2">Thymus x citriodorus</option> <option value="3">Thymus herba-barona</option> <option value="4">Thymus pseudolaginosus</option> <option value="5">Thymus serpyllum</option> </select> </td> <td> <input id="rows0.seedsPerCell" name="rows[0].seedsPerCell" type="text" value="" /> </td> <td> <button name="removeRow" type="submit" value="0">Remove row</button> </td> </tr> <tr> <td>2</td> <td> <select id="rows1.variety" name="rows[1].variety"> <option selected="selected" value="1">Thymus vulgaris</option> <option value="2">Thymus x citriodorus</option> <option value="3">Thymus herba-barona</option> <option value="4">Thymus pseudolaginosus</option> <option value="5">Thymus serpyllum</option> </select> </td> <td> <input id="rows1.seedsPerCell" name="rows[1].seedsPerCell" type="text" value="" /> </td> <td> <button name="removeRow" type="submit" value="1">Remove row</button> </td> </tr> </tbody>