Details
-
Improvement
-
Status: Open
-
Minor
-
Resolution: Unresolved
-
Upcoming Branch, 22.01.01
-
None
-
None
Description
Proof of concept for Model–view–viewmodel (MVVM) in form widgets.
Added data-bind attribute to the field, form and grid tag.
Example 1:
Go to https://localhost:8443/humanres/control/NewEmployee and try the following field:
- First name
- Middle name
- Last name
Added Full Name field to demonstrate computed observable/property.
Example 2:
Page at https://localhost:8443/ordermgr/control/ListQuoteItemsDemo?quoteId=CQ0001 duplicated the functionality of Quote Items page, but done using data-binding way.
mbrohl: here is a link to the discussion on the dev mailing list: https://lists.apache.org/thread.html/r1cddbc8041e5a6559bfad979bc8570581a3d1f662a0f8692c7ee9e59%40%3Cdev.ofbiz.apache.org%3E
15th March 2020
Updated the patch with more concrete examples for form and grid.
10th April 2020
Updated the patch with initVmFunc and some refactoring.
3rd May 2020
Updated the patch with a single demo that uses form and grid together.
- The demo can be accessed from https://localhost:8443/ordermgr/control/EditQuoteAndItems.
- Added a view='knockout' attribute so that form renderer will render the form / grid for use by knockout framework.
- Added autocomplete templates
Details
1) A javascript model function will be generated for the form / grid widgets when its data-bind attribute is defined.
As of 3rd April, all datamodel are not autogenerated.
2) First row of the grid is generated by the ofbiz form renderer at the server side. Subsequent rows will be generated by knockout at the browser side.
3) Naming convention of the viewmodel function is <Name of Form or Grid>VmFunc. There is a viewmodel for the Grid widget at row level; naming convention is <Name of Grid>VmRowFunc.
4) Viewmodel function can be extended using javascript prototype. See the *.js.ftl files in the patch.
5) The following javascript files are used:
- big/big.js - for basic mathematics operation
- knockout/knockout-3.5.1.js - knockout library
- knockout/knockout-config.js - extending the library and some code to configure knockout-secure-binding
- knockout/knockout-secure-binding - allow knockout js to be used with a Content Security Policy that disables eval and new Function.
- knockout-jqAutocomplete.js - for autocomplete
6) Example of hyperlink:
Before:
<field name="quoteItemSeqId" title="${uiLabelMap.OrderOrderQuoteItemSeqId}" widget-style="buttontext" use-when="${groovy: 'N'.equals(isPromo)}"> <hyperlink description="${quoteItemSeqId}" target="EditQuoteItem" also-hidden="false"> <parameter param-name="quoteId"/> <parameter param-name="quoteItemSeqId"/> </hyperlink> </field>
After:
<field name="quoteItemSeqId" title="${uiLabelMap.OrderOrderQuoteItemSeqId}" data-bind="text:quoteItemSeqId, attr: { href: editQuoteItemLink, title: 'Quote Item Seq ID'}" widget-style="buttontext"> <hyperlink description="[placeholder]"/> </field>
QuoteItem.prototype = { computed: { "editQuoteItemLink": function(){ return "<@ofbizUrl>EditQuoteItem?</@ofbizUrl>"eItemSeqId=" + this.quoteItemSeqId() + ""eId=" + this.quoteId(); }, ... };
From the value of data-bind attribute, we can see that the href attribute points to the editQuoteItemLink function.
editQuoteItemLink is a prototype member of QuoteItems which is viewmodel function for the Grid widget at row level.
7) Example of decimal places:
extend: {
"quoteUnitPrice": {numeric: 2}
}
The column or field for qouteUnitPrice will be rounded to 2 decimal places.
8) Example of calculating line totals:
<field name="lineTotal" title="Line Total (Demo)" ><display/></field>
QuoteItems.prototype = { computed: { ... "lineTotal": function() { var itemPrice = this.quoteUnitPrice(); var selectedAmount = this.selectedAmount() || 1; var quantity = this.quantity() || 0; return new Big(itemPrice).times(selectedAmount).times(quantity).toFixed(2); } ... };