Bug #11230
closedUnsuspending fails for Wt::WTextEdit inside Wt::WTemplate
100%
Description
When a session is unsuspended with a Wt::WTextEdit
inside a Wt::WTemplate
the following js error is thrown:
Wt4_8_2.$(...).ed is undefined
Note that this js function is invoked by WTextEdit::renderRemoveJs
which is called by WTemplate::updateDom
due to WTextEdit::domCanBeSaved
returning true.
Minimal example
root()->addNew<Wt::WTemplate>("${field}")->bindNew<Wt::WTextEdit>("field");
// root()->addNew<Wt::WTextEdit>(); // example works if no `Wt::WTemplate` is used.
root()->addNew<Wt::WPushButton>("Suspend & Resume")->clicked().connect([](){
wApp->suspend(std::chrono::minutes(15));
wApp->root()->addNew<Wt::WText>("<p>Suspending ...</p>");
wApp->redirect(wApp->url("/"));
});
wApp->unsuspended().connect([](){
wApp->root()->addNew<Wt::WText>("<p>Welcome back after suspending</p>");
});
When clicking on the "Suspend & Resume" button, "Suspending ..." is correctly shown, but the unsuspended
signal will never be called due to the js error mentioned above. When the Wt::WTemplate
or the Wt::WTextEdit
is removed, the session is correctly unsuspended, and "Welcome back after suspending" is shown.
Work-around / solution
Altering WTextEdit::domCanBeSaved
to return false solves the issue, but will probably (?) introduce some other regression. Note that WTextEdit::domCanBeSaved
was introduced in commit aa198760, but without a clear explanation.
Note that the console output still mentions the following error (but the application seems to be fully functional): "Wt: decodeSignal(): signal 'o18xyg7n.render' not exposed" (with o18xyg7n being the textarea).
A better solution probably is to fix the isRendered
check inside WTextEdit::renderRemoveJs
.
Note that WFileDropWidget::renderRemoveJs
generates a similar bug (i.e. Wt4_8_2.$(...).destructor is undefined) if a WFileDropWidget
and a TextEdit
are added to the same WWidget
(f.ex. WContainerWidget
) that is binded to a WTemplate
as WFileDropWidget::renderRemoveJs
is being called too in that case due to WTextEdit::domCanBeSaved
returning true.
Files
Updated by Roel Standaert 11 months ago
- File 0001-WT-11240-fix-rendered-state-of-widgets-when-returnin.patch 0001-WT-11240-fix-rendered-state-of-widgets-when-returnin.patch added
- Status changed from Confirmed to InProgress
- Assignee set to Roel Standaert
I suspected that the issue lied with the fact that the WTextEdit
is not aware that it isn't rendered at the moment the page is regenerated from scratch.
I see that it does work properly if I use:
<server>
<application-settings location="*">
<session-management>
<reload-is-new-session>false</reload-is-new-session>
</session-management>
</application-settings>
</server>
Putting a breakpoint in the !rendered
branch of WWebWidget::setRendered(...)
reveals that WebSession
calls app_->domRoot()->setRendered(false)
when the response is a page and either JS was not available or reloadIsNewSession()
is true.
Changing this condition to include a suspended()
check makes it work properly for suspend/unsuspend (see patch). I'll merge it into master
after our internal review.
Note on domCanBeSaved
¶
WTemplate
attempts to optimize updates when it rerenders (e.g. to update template text, change bound widgets or text, or condition changes) by saving the DOM elements corresponding to already rendered widgets, and putting them back into place when the template is re-rendered. If domCanBeSaved()
returns false
, that means that that particular widget must be rendered from scratch (TinyMCE would not behave correctly when this optimization is applied. Maybe it does work properly with later versions of TinyMCE released after that function was introduced, I'm not sure).