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 almost 2 years ago
- Status changed from New to InProgress
- Assignee set to Roel Standaert
Updated by Roel Standaert almost 2 years ago
- Status changed from InProgress to Confirmed
- Assignee deleted (
Roel Standaert) - Target version set to 4.9.1
Updated by Roel Standaert almost 2 years 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).
Updated by Roel Standaert almost 2 years ago
- Status changed from InProgress to Review
- Assignee deleted (
Roel Standaert)
Updated by Dries Mys almost 2 years ago
Thanks for the clarification and fast solution. Proposed fix sounds very reasonable to me (and is much better than my work-around). I can confirm the proposed fix works in my use case.
Updated by Dries Mys almost 2 years ago
Minor remark: error message about the not exposed signal is still present (but it doesn't affect the app functionality).
Updated by Roel Standaert almost 2 years ago
Yeah, we may need to look into that sometime. It's definitely not new.
Updated by Roel Standaert almost 2 years ago
- Status changed from Review to Implemented @Emweb
- Assignee changed from Marnik Roosen to Roel Standaert
- % Done changed from 0 to 100
Updated by Roel Standaert almost 2 years ago
- Status changed from Implemented @Emweb to Resolved
Updated by Roel Standaert almost 2 years ago
- Status changed from Resolved to Closed