Commit 6dece901 authored by Tristan Cavelier's avatar Tristan Cavelier Committed by Romain Courteaud

[erp5_web_renderjs_ui] improve page form gadget update

- Allow to continue to edit a form while saving is on going
- Reset fields on form view update (like by clicking on the active tab from the panel)
- Fix rendering an input like gadget with the same value as rendered before causes the
  gadget to be not updated even if the field has been manually edited.

/bug #20190204-39AF19
https://nexedijs.erp5.net/#/bug_module/20190204-39AF19

/reviewed-on nexedi/erp5!861
parent af5d8f0e
......@@ -92,6 +92,35 @@ and handling data send&receive.
}
}
function ensureComparable(value, field_type) {
// For LinesField, getContent returns string, but we may give an array of strings to the render method.
if (field_type === "LinesField") { return value.join ? value.join("\n") : value; }
if (typeof value === "object") { return JSON.stringify(value); }
return value;
}
function updateErp5DocumentForAfterSave(new_erp5_document, submitted_content, current_content) {
var view = new_erp5_document._embedded._view,
content_key_list = Object.keys(current_content),
document_reference_dict = {},
document_key_list = Object.keys(view),
i = 0,
field = null,
key = "";
for (i = 0; i < document_key_list.length; i += 1) {
field = view[document_key_list[i]];
document_reference_dict[field.key] = field;
}
for (i = 0; i < content_key_list.length; i += 1) {
key = content_key_list[i];
if (document_reference_dict[key] &&
ensureComparable(submitted_content[key], document_reference_dict[key].type) ===
ensureComparable(document_reference_dict[key].default, document_reference_dict[key].type)) {
document_reference_dict[key].default = current_content[key];
}
}
}
function warmupGadgetList(gadget, url_list) {
var i;
for (i = 0; i < url_list.length; i += 1) {
......@@ -163,6 +192,10 @@ and handling data send&receive.
.allowPublicAcquisition('notifySubmit', function notifySubmit() {
return this.triggerSubmit();
})
.allowPublicAcquisition("notifyChange", function notifyChange(argument_list) {
this.state.changing = true;
return this.notifyChange.apply(this, argument_list);
})
/**
* Render obtain ERP5 Document and assigned Form Definition.
*
......@@ -286,7 +319,8 @@ and handling data send&receive.
.push(function (result) {
page_template_gadget = result;
var sub_options = options.fg || {};
var sub_options = options.fg || {},
sub_queue = null;
loadFormContent(gadget, erp5_document._embedded._view);
......@@ -297,7 +331,23 @@ and handling data send&receive.
sub_options.jio_key = options.jio_key; // jIO identifier of currently rendered ERP5 document
sub_options.editable = options.editable; // form decides on editability of its fields
return page_template_gadget.render(sub_options);
if (gadget.state.changing && gadget.state.last_submitted_content) {
sub_queue = page_template_gadget.getContent()
.push(function (content) {
updateErp5DocumentForAfterSave(
sub_options.erp5_document,
JSON.parse(gadget.state.last_submitted_content),
content
);
gadget.state.last_submitted_content = null; // free some memory
gadget.state.changing = false;
});
}
return (sub_queue || new RSVP.Queue())
.push(function () {
return page_template_gadget.render(sub_options);
});
})
.push(function () {
if (modification_dict.hasOwnProperty('url')) {
......@@ -358,6 +408,13 @@ and handling data send&receive.
return gadget.notifySubmitting()
.push(function () {
return gadget.getDeclaredGadget("fg");
})
.push(function (sub_gadget) {
return sub_gadget.getContent();
})
.push(function (content) {
gadget.state.last_submitted_content = JSON.stringify(content);
return gadget.jio_putAttachment(jio_key, target_url, content_dict);
})
.push(function (attachment) {
......
......@@ -228,7 +228,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>975.56533.48127.29525</string> </value>
<value> <string>975.55180.61738.11520</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -246,7 +246,7 @@
</tuple>
<state>
<tuple>
<float>1558448080.86</float>
<float>1558366871.01</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -207,6 +207,12 @@
.declareAcquiredMethod("notifyChange", "notifyChange")
.onEvent('change', function change() {
var input = this.element.querySelector("input");
if (input) {
// force the state to have the current edited value
this.state.checked = input.checked;
this.state.value = input.value;
}
return RSVP.all([
this.checkValidity(),
this.notifyChange("change")
......
......@@ -228,7 +228,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>967.40700.16743.2833</string> </value>
<value> <string>975.45053.32644.40618</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -246,7 +246,7 @@
</tuple>
<state>
<tuple>
<float>1526653024.9</float>
<float>1557839422.8</float>
<string>UTC</string>
</tuple>
</state>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>expand</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>testDateTimeFieldUpdate</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
<value> <string>utf-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<html xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test RenderJS UI</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">Test RenderJS UI</td></tr>
</thead><tbody>
<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/init" />
<!-- Clean Up -->
<tr>
<td>open</td>
<td>${base_url}/bar_module/ListBoxZuite_reset</td>
<td></td>
</tr>
<tr>
<td>assertTextPresent</td>
<td>Reset Successfully.</td>
<td></td>
</tr>
<!-- Initialize -->
<tr>
<td>open</td>
<td>${base_url}/bar_module/BarModule_createObjects?num:int=1</td>
<td></td>
</tr>
<tr>
<td>assertTextPresent</td>
<td>Created Successfully.</td>
<td></td>
</tr>
<tr>
<td>open</td>
<td>${base_url}/web_site_module/renderjs_runner/#/bar_module/0?editable=true</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/go_to_bar_datetime_field_view" />
<!-- Actual test -->
<tr>
<td>waitForElementPresent</td>
<td>//input[@name='field_my_start_date']</td>
<td></td>
</tr>
<tr>
<td>storeValue</td>
<td>//input[@name='field_my_start_date']</td>
<td>start_date_value</td>
</tr>
<tr>
<td>type</td>
<td>//input[@name='field_my_start_date']</td>
<td>2019-04-23T01:01:00</td>
</tr>
<tr>
<td>chooseOkOnNextConfirmation</td>
<td></td>
<td></td>
</tr>
<tr>
<td>click</td> <!-- using go_to_bar_datetime_field_view, chooseOkOnNextConfirmation does not work -->
<td>//a[@class="active" and text()='DateTimeField']</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_confirmation" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tr>
<td>pause</td> <!-- necessary to wait for field to be updated -->
<td>1000</td>
<td></td>
</tr>
<tr>
<td>verifyValue</td>
<td>//input[@name='field_my_start_date']</td>
<td>${start_date_value}</td>
</tr>
</tbody></table>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>expand</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>testFormViewEditableNextWithEditedField</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
<value> <string>utf-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<html xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test Form View Next With Edited Field</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">Test Form View Next With Edited Field, two Foos have empty short title, one is edited but not saved, next short title field should be reset.</td></tr>
</thead><tbody>
<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/init" />
<!-- Clean Up -->
<tr>
<td>open</td>
<td>&#36;{base_url}/foo_module/ListBoxZuite_reset</td>
<td></td>
</tr>
<tr>
<td>assertTextPresent</td>
<td>Reset Successfully.</td>
<td></td>
</tr>
<!-- Initialize -->
<tr>
<td>open</td>
<td>&#36;{base_url}/foo_module/FooModule_createObjects?num:int=2</td>
<td></td>
</tr>
<tr>
<td>waitForTextPresent</td>
<td>Created Successfully.</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/wait_for_activities" />
<tr>
<td>open</td>
<td>${base_url}/web_site_module/renderjs_runner/#/foo_module?editable=true</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_app_loaded" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_listbox_loaded" />
<tr>
<td>click</td>
<td>//a[text()='Title 0']</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tr>
<td>type</td>
<td>field_my_short_title</td>
<td>Hello</td>
</tr>
<tr>
<td>chooseOkOnNextConfirmation</td>
<td></td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//div[@data-gadget-scope='header']//a[text()='Next' and contains(@href, '#!selection_next')]</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_confirmation" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tr>
<td>pause</td> <!-- necessary to wait for field to be updated -->
<td>1000</td>
<td></td>
</tr>
<tr>
<td>verifyValue</td>
<td>field_my_short_title</td>
<td></td>
</tr>
</tbody></table>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>expand</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>testFormViewEditableSaveActionContinuingEditingInputField</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
<value> <string>utf-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<html xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test Form View Editable Save Action Continuing Editing Input Field</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">Test Form View Editable Save Action Continuing Editing Input Field, one Foo have empty short title which is edited, saved and edited during the saving process. New typed values should be kept.</td></tr>
</thead><tbody>
<tal:block metal:use-macro="here/PTZuite_CommonTemplate/macros/init" />
<tr>
<td>open</td>
<td>${base_url}/web_site_module/renderjs_runner/#/foo_module/1?editable=true</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_app_loaded" />
<tr>
<td>type</td>
<td>field_my_short_title</td>
<td>Hello</td>
</tr>
<tr>
<td>click</td>
<td>//div[@data-gadget-scope='header']//button[text()='Save' and @type='submit']</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>field_my_short_title</td>
<td>World</td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<!-- saving should not prevent to continue to edit the document -->
<tr>
<td>pause</td> <!-- necessary to wait for field to be updated -->
<td>1000</td>
<td></td>
</tr>
<tr>
<td>verifyValue</td>
<td>field_my_short_title</td>
<td>World</td>
</tr>
<tr>
<td>open</td>
<td>${base_url}/foo_module/1/getShortTitle</td>
<td></td>
</tr>
<tr>
<td>assertTextPresent</td>
<td>Hello</td>
<td></td>
</tr>
</tbody></table>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>expand</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>testFormViewEditableSaveActionContinuingEditingLinesField</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
<value> <string>utf-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<html xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test Form View Editable Save Action Continuing Editing Lines Field</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">Test Form View Editable Save Action Continuing Editing Lines Field, one Foo have empty line list which is edited, saved and edited during the saving process. New typed values should be kept.</td></tr>
</thead><tbody>
<tal:block metal:use-macro="here/PTZuite_CommonTemplate/macros/init" />
<tr>
<td>open</td>
<td>${base_url}/web_site_module/renderjs_runner/#/foo_module/1?editable=true</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_app_loaded" />
<tr>
<td>type</td>
<td>field_my_lines_list</td>
<td>Hello</td>
</tr>
<tr>
<td>click</td>
<td>//div[@data-gadget-scope='header']//button[text()='Save' and @type='submit']</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>field_my_lines_list</td>
<td>World</td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<!-- saving should not prevent to continue to edit the document -->
<tr>
<td>pause</td> <!-- necessary to wait for field to be updated -->
<td>1000</td>
<td></td>
</tr>
<tr>
<td>verifyValue</td>
<td>field_my_lines_list</td>
<td>World</td>
</tr>
<tr>
<td>open</td>
<td>${base_url}/foo_module/1/getProperty?key=lines_list</td>
<td></td>
</tr>
<tr>
<td>assertTextPresent</td>
<td>('Hello',)</td>
<td></td>
</tr>
</tbody></table>
</body>
</html>
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment