หลังจากที่ซื่อบื้อเพราะอ่าน cookbook ไม่ค่อยรู้เรื่อง หลังจากที่ได้ไล่ code ไปบ้างแล้ว กลับไปอ่าน cookbook อีกที เลยรู้สึกว่าตัวเองซื่อบื้อที่ไม่ได้ตั้งใจอ่านให้ดี
cookbook นี้กล่าวถึงการนำ widget มาใช้ในการ create screen
การเช็ค permission ของ FOO_ACTION และ render widget เมื่อผ่าน และเมื่อ fail
CODE
<decorator-section name=”foo”>
<section>
<condition><if-has-permission permission=”FOO” action=”_ACTION”/></condition>
<widgets><!– condition success widgets go here –></widgets>
<fail-widgets>
<!– condition failure widgets go here –>
<label style=”head3″>${uiLabelMap.FooActionPermissionError}</label>
</fail-widgets>
</section>
</decorator-section>
การ test ค่า ใน context ว่าควรจะ render widget หรือไม่
CODE
<section>
<condition><if-compare field-name=”objectInContext” operator=”equals” value=”true” type=”Boolean”/></condition>
<widgets> These will render if condition passes </widgets>
<fail-widgets> Otherwise, these widgets will render </fail-widgets>
</section>
1. <section> block ใส่ที่ไหนก็ได้ภายใต้ <widgets> หรือ <fail-widgets> เพื่อทำให้เกิด nested condition
CODE
<widgets> ….
<section>
<condition>…</condition>
<widgets> …
<section>
<condition>….</condition>
<widgets/>
</section>
</widgets>
<fail-widgets><section>…</section></fail-widgets>
</section>
</widgets>
2. จับกลุ่ม condition โดยการ wrapping โดยใช้ <and>, <or>, <xor> และ <not>:
CODE
<condition><not><and><if-compare one/><if-compare two/></and></not></condition>
3. เงื่อนไข <if-empty field=”fieldName”> ใช้ในการ test เมื่อค่าเป็น null หรือยังไม่ได้ถูกำหนด (not defined)
การ ใช้ uiLabelMap ภายใน screens
โดยปกติ main-decorator จะกำหนด uiLabelMap แต่ไม่สามารถถูกเข้าถึงได้จาก screen ใน level ที่สูงกว่าจนกระทั่ง <action> จะถูก execute หลีกเลี่ยงการใช้ uiLabelMap ยกเว้นเฉพาะใน <widget> เท่านั้น ให้กำหนดชื่อของ map ที่จะเรียก เช่น CommonLogin ใน parameter แล้วเรียก uiLabelMap นี้จากใน template file
CODE
<actions>
<set field=”someField” value=”CommonLogin”/> <!– แล้วใช้ใน template นี้ด้วย ${uiLabelMap.get(someField)} –>
<set field=”anotherField” from-field=”uiLabelMap.CommonLogin”/> <!– แบบนี้ยังทำงานไม่ได้เพราะ uiLabelMap ยังไม่ได้ถูก define –>
</actions>
<widgets>
<label style=”head3″>${uiLabelMap.CommonLogin}</label> <!– ส่วนนี้เป็นข้อยกเว้น: you can use the context variables inside <widgets> –>
</widgets>
อัน นี้ไว้สำหรับ apply page titles ที่คุณต้องการกำหนดใน screen นั่นเป็นเหตุผลว่าทำไมจึงเห็น screen section กำหนด pageTitleLabel = “CommonLogin” แทนที่จะอ้างถึง uiLabelMap โดยตรง
การ get ค่า single entity ใน action แล้วเก็บลงใน value
CODE
<set field=”entityPrimaryKeyId” from-field=”parameters.entityPrimaryKeyId”/>
<entity-one entity-name=”EntityName” value-name=”entityValue”/>
การส ร้าง screen 2 columns (This comes from a real main-decorator)
CODE
<!– box ครอบ main content ของ application ภายใต้ section tabbbar–>
<container style=”centerarea”>
<!– subheader includes a place to put the title and login/logout buttons –>
<platform-specific><html><html-template location=”component://crmsfa/webapp/crmsfa/includes/subheader.ftl”/>
</html></platform-specific>
<!– a div of class “contentarea” –>
<container style=”contentarea”>
<!– a div of id “column-container” –>
<container id=”column-container”>
TODO: insert code to show explicit columns
<!– will render shortcuts only if shortcutsScreenName value not empty –>
<include-screen name=”${shortcutsScreenName}”/>
<!– The main column where most of the content goes. –>
<container>
<!– Draw any messages, such as errors. TODO: use our own prettier version –>
<platform-specific><html><html-template location=”component://common/webcommon/includes/messages.ftl”/></html></platform-specific>
<!– Finally, include the section screen –>
<decorator-section-include name=”section-decorator”/>
</container>
</container>
</container>
</container>
การ รับ partyId parameter จากลิงค์และมี partyId เป็น input parameter ภายใน form ด้วย โดยไม่เกิดการ conflict
หากตต้องการมี form ที่รับ partyId และยังมีจาก URL parameter อาจจะทำให้เกิด error เพราะจะอ่านค่า partyId เป็น array 2 ค่า เพราะว่า screen widget ไม่ได้เก่งขนาดที่จะควบคุม request parameter และ form ได้ มันจะ combine URL parameter partyId และ form parameter partyId ไว้ใน array key เป็น parameters.get(“partyId”) เพื่อให้สามารถทำงานได้ ให้ใส่ code ด้านลางนี้บนหัว beanshell script ก่อนที่ from จะถูก render
CODE
partyId = request.getParameter(“partyId”); // check actual URL parameters first
if (partyId == null) parameters.get(“partyId”); // if none, it’s safe that this is a form parameter and not an array
Setting properties file values
You can set values from .properties files for use in the screens by doing this:
CODE
<property-field field=”defaultCurrencyUomId” resource=”general” property=”currency.uom.id.default” default=”USD”/>
where resource denotes which properties file (in this case, “general.properties”) and the property is the property to use. Note that this does not have a global=”true” attribute, so it can only be set for the screen that is using it, not globally in a decorator.
Paginating your forms
You need to set the VIEW_INDEX and VIEW_SIZE in your form-widget so that your forms will paginate properly. They can be set in the <actions> section of your screen like this:
CODE
<set field=”viewIndex” from-field=”parameters.VIEW_INDEX” type=”Integer” default-value=”0″/>
<set field=”viewSize” from-field=”parameters.VIEW_SIZE” type=”Integer” default-value=”20″/>
In opentaps 0.9 and earlier versions, make sure you set those default-values. Otherwise, your first page might be a negative value, or your screen might show 100 records at a time.
If you are using an older version of ofbiz and need to pass additional parameters with the Next and Previous links, put a parameter “queryString” in the <actions> section as follows,
CODE
<set field=”queryString” value=”partyId=${parameters.partyID}&orderId=${parameters.orderId}”/>
Order of Execution of <actions>
TODO: describe the problem with the order of execution of <actions>, the need to set some global variables, and how to work around it. For instance, we can’t do this,
CODE
<actions>
<set field=”label” value=”${uiLabelMap.SomeLabel}”/>
</actions>
<widgets>
<!– include the GlobalDecorator’s main-decorator here, which
has an <actions> that sets the uiLabelMap. –>
</widgets>
That is because the first actions to be executed are the ones in the screen that is invoked. This is an event-based parsing system and is quite logical.
How to set a conditional header item
In the financials application, the same payment screen could belong in either the Receivables or Payables tab, so we had to set it conditionally like this:
in the editPayment.bsh, inside an if block:
CODE
parameters.put(“headerItem”,”receivables”);
in the PaymentScreens.xml AFTER the .bsh is executed:
CODE
<set field=”headerItem” from-field=”parameters.headerItem”/>
How to access page attributes in screens
The screen widget sets a Map (actually a javolution FastMap) called “page” which can be used in the Freemarker and Beanshell templates to access the parameters set in the screen. Here is an example of it from the ecommerce application:
CODE
page = [leftbarScreenName=leftbar, rightbarScreenName=rightbar, MainColumnStyle=nocolumns, titleProperty=PageTitleCategoryPage, layoutSettings=[extraHead=<link rel="stylesheet" href="/content/images/contentForum.css" type="text/css"/>]]
and here is an example of how to use it in the FTL, from header.ftl of the ecommerce application:
CODE
<#if page.title?has_content>${page.title}<#elseif page.titleProperty?has_content>${uiLabelMap.get
(page.titleProperty)}</#if>
การเช็ค permission ของ FOO_ACTION และ render widget เมื่อผ่าน และเมื่อ fail
CODE
<decorator-section name=”foo”>
<section>
<condition><if-has-permission permission=”FOO” action=”_ACTION”/></condition>
<widgets><!– condition success widgets go here –></widgets>
<fail-widgets>
<!– condition failure widgets go here –>
<label style=”head3″>${uiLabelMap.FooActionPermissionError}</label>
</fail-widgets>
</section>
</decorator-section>
การ test ค่า ใน context ว่าควรจะ render widget หรือไม่
CODE
<section>
<condition><if-compare field-name=”objectInContext” operator=”equals” value=”true” type=”Boolean”/></condition>
<widgets> These will render if condition passes </widgets>
<fail-widgets> Otherwise, these widgets will render </fail-widgets>
</section>
1. <section> block ใส่ที่ไหนก็ได้ภายใต้ <widgets> หรือ <fail-widgets> เพื่อทำให้เกิด nested condition
CODE
<widgets> ….
<section>
<condition>…</condition>
<widgets> …
<section>
<condition>….</condition>
<widgets/>
</section>
</widgets>
<fail-widgets><section>…</section></fail-widgets>
</section>
</widgets>
2. จับกลุ่ม condition โดยการ wrapping โดยใช้ <and>, <or>, <xor> และ <not>:
CODE
<condition><not><and><if-compare one/><if-compare two/></and></not></condition>
3. เงื่อนไข <if-empty field=”fieldName”> ใช้ในการ test เมื่อค่าเป็น null หรือยังไม่ได้ถูกำหนด (not defined)
การ ใช้ uiLabelMap ภายใน screens
โดยปกติ main-decorator จะกำหนด uiLabelMap แต่ไม่สามารถถูกเข้าถึงได้จาก screen ใน level ที่สูงกว่าจนกระทั่ง <action> จะถูก execute หลีกเลี่ยงการใช้ uiLabelMap ยกเว้นเฉพาะใน <widget> เท่านั้น ให้กำหนดชื่อของ map ที่จะเรียก เช่น CommonLogin ใน parameter แล้วเรียก uiLabelMap นี้จากใน template file
CODE
<actions>
<set field=”someField” value=”CommonLogin”/> <!– แล้วใช้ใน template นี้ด้วย ${uiLabelMap.get(someField)} –>
<set field=”anotherField” from-field=”uiLabelMap.CommonLogin”/> <!– แบบนี้ยังทำงานไม่ได้เพราะ uiLabelMap ยังไม่ได้ถูก define –>
</actions>
<widgets>
<label style=”head3″>${uiLabelMap.CommonLogin}</label> <!– ส่วนนี้เป็นข้อยกเว้น: you can use the context variables inside <widgets> –>
</widgets>
อัน นี้ไว้สำหรับ apply page titles ที่คุณต้องการกำหนดใน screen นั่นเป็นเหตุผลว่าทำไมจึงเห็น screen section กำหนด pageTitleLabel = “CommonLogin” แทนที่จะอ้างถึง uiLabelMap โดยตรง
การ get ค่า single entity ใน action แล้วเก็บลงใน value
CODE
<set field=”entityPrimaryKeyId” from-field=”parameters.entityPrimaryKeyId”/>
<entity-one entity-name=”EntityName” value-name=”entityValue”/>
การส ร้าง screen 2 columns (This comes from a real main-decorator)
CODE
<!– box ครอบ main content ของ application ภายใต้ section tabbbar–>
<container style=”centerarea”>
<!– subheader includes a place to put the title and login/logout buttons –>
<platform-specific><html><html-template location=”component://crmsfa/webapp/crmsfa/includes/subheader.ftl”/>
</html></platform-specific>
<!– a div of class “contentarea” –>
<container style=”contentarea”>
<!– a div of id “column-container” –>
<container id=”column-container”>
TODO: insert code to show explicit columns
<!– will render shortcuts only if shortcutsScreenName value not empty –>
<include-screen name=”${shortcutsScreenName}”/>
<!– The main column where most of the content goes. –>
<container>
<!– Draw any messages, such as errors. TODO: use our own prettier version –>
<platform-specific><html><html-template location=”component://common/webcommon/includes/messages.ftl”/></html></platform-specific>
<!– Finally, include the section screen –>
<decorator-section-include name=”section-decorator”/>
</container>
</container>
</container>
</container>
การ รับ partyId parameter จากลิงค์และมี partyId เป็น input parameter ภายใน form ด้วย โดยไม่เกิดการ conflict
หากตต้องการมี form ที่รับ partyId และยังมีจาก URL parameter อาจจะทำให้เกิด error เพราะจะอ่านค่า partyId เป็น array 2 ค่า เพราะว่า screen widget ไม่ได้เก่งขนาดที่จะควบคุม request parameter และ form ได้ มันจะ combine URL parameter partyId และ form parameter partyId ไว้ใน array key เป็น parameters.get(“partyId”) เพื่อให้สามารถทำงานได้ ให้ใส่ code ด้านลางนี้บนหัว beanshell script ก่อนที่ from จะถูก render
CODE
partyId = request.getParameter(“partyId”); // check actual URL parameters first
if (partyId == null) parameters.get(“partyId”); // if none, it’s safe that this is a form parameter and not an array
Setting properties file values
You can set values from .properties files for use in the screens by doing this:
CODE
<property-field field=”defaultCurrencyUomId” resource=”general” property=”currency.uom.id.default” default=”USD”/>
where resource denotes which properties file (in this case, “general.properties”) and the property is the property to use. Note that this does not have a global=”true” attribute, so it can only be set for the screen that is using it, not globally in a decorator.
Paginating your forms
You need to set the VIEW_INDEX and VIEW_SIZE in your form-widget so that your forms will paginate properly. They can be set in the <actions> section of your screen like this:
CODE
<set field=”viewIndex” from-field=”parameters.VIEW_INDEX” type=”Integer” default-value=”0″/>
<set field=”viewSize” from-field=”parameters.VIEW_SIZE” type=”Integer” default-value=”20″/>
In opentaps 0.9 and earlier versions, make sure you set those default-values. Otherwise, your first page might be a negative value, or your screen might show 100 records at a time.
If you are using an older version of ofbiz and need to pass additional parameters with the Next and Previous links, put a parameter “queryString” in the <actions> section as follows,
CODE
<set field=”queryString” value=”partyId=${parameters.partyID}&orderId=${parameters.orderId}”/>
Order of Execution of <actions>
TODO: describe the problem with the order of execution of <actions>, the need to set some global variables, and how to work around it. For instance, we can’t do this,
CODE
<actions>
<set field=”label” value=”${uiLabelMap.SomeLabel}”/>
</actions>
<widgets>
<!– include the GlobalDecorator’s main-decorator here, which
has an <actions> that sets the uiLabelMap. –>
</widgets>
That is because the first actions to be executed are the ones in the screen that is invoked. This is an event-based parsing system and is quite logical.
How to set a conditional header item
In the financials application, the same payment screen could belong in either the Receivables or Payables tab, so we had to set it conditionally like this:
in the editPayment.bsh, inside an if block:
CODE
parameters.put(“headerItem”,”receivables”);
in the PaymentScreens.xml AFTER the .bsh is executed:
CODE
<set field=”headerItem” from-field=”parameters.headerItem”/>
How to access page attributes in screens
The screen widget sets a Map (actually a javolution FastMap) called “page” which can be used in the Freemarker and Beanshell templates to access the parameters set in the screen. Here is an example of it from the ecommerce application:
CODE
page = [leftbarScreenName=leftbar, rightbarScreenName=rightbar, MainColumnStyle=nocolumns, titleProperty=PageTitleCategoryPage, layoutSettings=[extraHead=<link rel="stylesheet" href="/content/images/contentForum.css" type="text/css"/>]]
and here is an example of how to use it in the FTL, from header.ftl of the ecommerce application:
CODE
<#if page.title?has_content>${page.title}<#elseif page.titleProperty?has_content>${uiLabelMap.get
(page.titleProperty)}</#if>