Commit 264ded5c authored by Romain Courteaud's avatar Romain Courteaud

[erp5_hal_json_style/erp5_web_renderjs_ui] Render the form directly after a...

[erp5_hal_json_style/erp5_web_renderjs_ui] Render the form directly after a successfull call to Base_edit

This decreases the query number from 2 (POST to Base_edit -> redirect + GET to render the form) to 1 (POST to Base_edit -> render the form) and so improve the speed of ERP5JS.

To achieve this, apply so fixed in ERP5JS:
* rerender the panel/header after Base_edit (router is not called in such case, so, it must be manually called)
* fetch the page title from the form calculation (as the document context is not recalculated, only the form)
* force deleting the current field values, used by the relation field listbox search

This commit also force the full page refresh when diplaying a new document, in order to prevent keeping field values from the previous one (so bugs used to occur when using the previous/next document pagination).

Thanks to Tristan Cavelier for his work on this topic.
parent 3f017d74
......@@ -256,4 +256,9 @@ if context.REQUEST.get('is_web_mode', False) and \
not editable_mode:
form_id = 'view'
return context.Base_redirect(keep_items={'portal_status_message': message})
# Directly render the form after a successful edit
# Cleanup formulator's special key in request to ensure field are only calculated from context and not the request anymore
for key in list(context.REQUEST.keys()):
if str(key).startswith('field') or str(key).startswith('subfield'):
context.REQUEST.form.pop(key, None)
return context.Base_renderForm(form_id, message=message)
......@@ -13,6 +13,15 @@
if (/ Module$/.test(erp5_document._links.type.href)) {
return portal_type;
}
if (erp5_document.hasOwnProperty('_embedded') &&
erp5_document._embedded.hasOwnProperty('_view') &&
erp5_document._embedded._view.hasOwnProperty('_links') &&
erp5_document._embedded._view._links.hasOwnProperty('traversed_document')) {
// When refreshing the page (after Base_edit), only the form content is recalculated
// and erp5_document.title may contain the old title value.
// Get the title value from the calculated form if possible
title = erp5_document._embedded._view._links.traversed_document.title;
}
return portal_type + ': ' + title;
});
};
......
......@@ -228,7 +228,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>974.49982.38828.49698</string> </value>
<value> <string>978.12269.8875.56678</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -246,7 +246,7 @@
</tuple>
<state>
<tuple>
<float>1554209210.63</float>
<float>1567694872.64</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -98,6 +98,20 @@
return route(gadget, 'panel', 'render', [gadget.props.panel_argument_list]);
}
function refreshHeaderAndPanel(gadget, refresh) {
var promise;
if (refresh) {
promise = route(gadget, "header", 'render',
[gadget.props.header_argument_list]);
} else {
promise = updateHeader(gadget);
}
return RSVP.all([
promise,
updatePanel(gadget)
]);
}
function callJioGadget(gadget, method, param_list) {
return route(gadget, 'jio_gadget', method, param_list);
}
......@@ -552,6 +566,11 @@
gadget.props.panel_argument_list = param_list[0];
})
.allowPublicAcquisition('refreshHeaderAndPanel',
function acquireRefreshHeaderAndPanel() {
return refreshHeaderAndPanel(this, true);
})
.allowPublicAcquisition('hidePanel', function hidePanel(param_list) {
return hideDesktopPanel(this, param_list[0]);
})
......@@ -732,10 +751,7 @@
content_container.appendChild(main_gadget.element);
element.appendChild(content_container);
return RSVP.all([
updateHeader(gadget),
updatePanel(gadget)
]);
return refreshHeaderAndPanel(gadget);
// XXX Drop notification
// return header_gadget.notifyLoaded();
}
......@@ -747,10 +763,7 @@
return page_gadget.render(gadget.state.options);
})
.push(function () {
return RSVP.all([
updateHeader(gadget),
updatePanel(gadget)
]);
return refreshHeaderAndPanel(gadget);
}));
}
......
......@@ -228,7 +228,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>978.15454.53878.14950</string> </value>
<value> <string>978.15561.54943.25565</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -246,7 +246,7 @@
</tuple>
<state>
<tuple>
<float>1567693410.93</float>
<float>1567699994.46</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -113,6 +113,7 @@ and handling data send&receive.
.declareAcquiredMethod("translate", "translate")
.declareAcquiredMethod("jio_allDocs", "jio_allDocs")
.declareAcquiredMethod("updatePanel", "updatePanel")
.declareAcquiredMethod("refreshHeaderAndPanel", "refreshHeaderAndPanel")
.declareAcquiredMethod("notifyChange", "notifyChange")
.declareAcquiredMethod("notifySubmitting", "notifySubmitting")
.declareAcquiredMethod("notifySubmitted", "notifySubmitted")
......@@ -178,7 +179,8 @@ and handling data send&receive.
erp5_document: undefined,
erp5_form: undefined,
url: undefined,
embedded: asBoolean(options.embedded)
embedded: asBoolean(options.embedded),
is_refresh: options.is_refresh || false
};
// options.editable differs when it comes from the erp5_launcher of FormBox - try to unify it here
......@@ -277,7 +279,7 @@ and handling data send&receive.
erp5_document = JSON.parse(gadget.state.erp5_document),
erp5_form = JSON.parse(gadget.state.erp5_form);
if (modification_dict.hasOwnProperty('url')) {
if ((!gadget.state.is_refresh) || modification_dict.hasOwnProperty('url')) {
queue = gadget.declareGadget(gadget.state.url, {scope: "fg"});
} else {
queue = gadget.getDeclaredGadget("fg");
......@@ -288,6 +290,11 @@ and handling data send&receive.
var sub_options = options.fg || {};
if (gadget.state.is_refresh) {
// Delete the previous form content when refreshing
// to prevent loosing user modification
delete gadget.state.options.form_content;
}
loadFormContent(gadget, erp5_document._embedded._view);
sub_options.erp5_document = erp5_document;
......@@ -300,7 +307,7 @@ and handling data send&receive.
return page_template_gadget.render(sub_options);
})
.push(function () {
if (modification_dict.hasOwnProperty('url')) {
if ((!gadget.state.is_refresh) || modification_dict.hasOwnProperty('url')) {
return page_template_gadget.getElement()
.push(function (fragment) {
var element = gadget.element;
......@@ -409,6 +416,7 @@ and handling data send&receive.
// We modify inplace state.options because render method uses and removes
// erp5_document hidden in its options.
options.erp5_document = erp5_document;
options.is_refresh = true;
return new RSVP.Queue()
.push(function () {
if (response_view._notification === undefined) {
......@@ -431,6 +439,9 @@ and handling data send&receive.
*/
return gadget.render(options);
})
.push(function () {
return gadget.refreshHeaderAndPanel();
})
.push(function () {
// Make sure to return nothing (previous render can return
// something) so the successfull handler does not receive
......@@ -556,7 +567,8 @@ and handling data send&receive.
var erp5_document = JSON.parse(gadget.state.erp5_document);
erp5_document._embedded._view = response;
erp5_document._now = Date.now();
return gadget.changeState({erp5_document: JSON.stringify(erp5_document)});
return gadget.changeState({erp5_document: JSON.stringify(erp5_document),
is_refresh: true});
});
}
})
......
......@@ -228,7 +228,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>978.9875.57005.56541</string> </value>
<value> <string>978.15561.54943.25565</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -246,7 +246,7 @@
</tuple>
<state>
<tuple>
<float>1567358955.05</float>
<float>1567699957.33</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -42,7 +42,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>testDialogLinesField</string> </value>
<value> <string>testDateTimeFieldUpdate</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
......
<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
<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 Dialog Main</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">Test that the content of LinesField textarea doesn't change when sort button in sub document list is clicked</td></tr>
</thead><tbody>
<tal:block metal:use-macro="here/PTZuite_CommonTemplate/macros/init" />
<!-- Shortcut for full renderjs url -->
<tr><td>store</td>
<td>${base_url}/web_site_module/renderjs_runner</td>
<td>renderjs_url</td></tr>
<tr><td>open</td>
<td>${renderjs_url}/#/foo_module/1?editable=1</td><td></td></tr>
<tr><td>waitForElementPresent</td>
<td>//div[@data-gadget-url="${renderjs_url}/gadget_erp5_field_lines.html"]//textarea</td><td></td></tr>
<tr><td>type</td>
<td>field_my_lines_list</td>
<td>foo<br/><!-- tags and comments get stripped away -->
bar</td></tr>
<!-- Change sorting of the listbox -->
<tr><td>waitForElementPresent</td>
<td>//div[@data-gadget-url="${renderjs_url}/gadget_erp5_field_listbox.html"]//table/thead/tr/th/a[text()='Title']</td><td></td></tr>
<tr>
<td>chooseOkOnNextConfirmation</td>
<td></td>
<td></td>
</tr>
<tr><td>click</td>
<td>//div[@data-gadget-url="${renderjs_url}/gadget_erp5_field_listbox.html"]//table/thead/tr/th/a[text()='Title']</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>assertValue</td>
<td>//textarea[@name='field_my_lines_list']</td>
<td>foo<br/>
bar</td></tr>
<!-- Save and force unloading of Form and its content from the memory by going back -->
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/save" />
<tr><td>click</td>
<td>//div[@data-role='header']//h1/a</td><td></td></tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tr><td>waitForElementPresent</td>
<td>//div[@data-gadget-url='${renderjs_url}/gadget_erp5_field_listbox.html']//table/tbody/tr//a</td><td></td></tr>
<tr><td>click</td>
<td>//div[@data-gadget-url='${renderjs_url}/gadget_erp5_field_listbox.html']//table/tbody/tr[1]//a</td><td></td></tr>
<tr><td>waitForElementPresent</td>
<td>//div[@data-gadget-url="${renderjs_url}/gadget_erp5_field_lines.html"]//textarea</td><td></td></tr>
<!-- Assert value still holds and change it -->
<tr><td>assertValue</td>
<td>//textarea[@name='field_my_lines_list']</td>
<td>foo<br/>
bar</td></tr>
<tr><td>assertValue</td>
<td>//textarea[@name='field_my_lines_list']</td>
<td>foo<br/>
bar</td></tr>
<tr><td>type</td>
<td>//textarea[@name='field_my_lines_list']</td>
<td>foo bar</td></tr>
<tr>
<td>chooseOkOnNextConfirmation</td>
<td></td>
<td></td>
</tr>
<tr><td>click</td>
<td>//div[@data-gadget-url="${renderjs_url}/gadget_erp5_field_listbox.html"]//table/thead/tr/th/a[text()='Title']</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>assertValue</td>
<td>//textarea[@name='field_my_lines_list']</td>
<td>foo bar</td></tr>
<!-- Save and force unloading of Form and its content from the memory by going back -->
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/save" />
<tr><td>click</td>
<td>//div[@data-role='header']//h1/a</td><td></td></tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tr><td>waitForElementPresent</td>
<td>//div[@data-gadget-url='${renderjs_url}/gadget_erp5_field_listbox.html']//table/tbody/tr//a</td><td></td></tr>
<tr><td>click</td>
<td>//div[@data-gadget-url='${renderjs_url}/gadget_erp5_field_listbox.html']//table/tbody/tr[1]//a</td><td></td></tr>
<tr><td>waitForElementPresent</td>
<td>//div[@data-gadget-url="${renderjs_url}/gadget_erp5_field_lines.html"]//textarea</td><td></td></tr>
<tr><td>assertValue</td>
<td>//textarea[@name='field_my_lines_list']</td>
<td>foo bar</td></tr>
</body>
</html>
\ No newline at end of file
......@@ -29,8 +29,10 @@
<td>${base_url}/web_site_module/renderjs_runner/#/foo_module</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_app_loaded" />
<tr>
<td>waitForElementPresent</td>
<td>assertElementPresent</td>
<td>//a[@data-i18n='Add']</td>
<td></td>
</tr>
......@@ -40,16 +42,17 @@
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_dialog" />
<tr>
<td>waitForTextPresent</td>
<td>Save</td>
<td></td>
</tr>
<tal:block tal:define="notification_configuration python: {'class': 'success',
'text': 'Object created.'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tr>
<td>assertTextPresent</td>
<td>Save</td>
<td></td>
</tr>
<tr>
<td>assertSelected</td>
......
<?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>