Project

General

Profile

Actions

Bug #11230

closed

Unsuspending fails for Wt::WTextEdit inside Wt::WTemplate

Added by Dries Mys almost 2 years ago. Updated almost 2 years ago.

Status:
Closed
Priority:
Normal
Assignee:
Roel Standaert
Target version:
Start date:
01/06/2023
Due date:
% Done:

100%

Estimated time:

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

Actions #1

Updated by Roel Standaert almost 2 years ago

  • Status changed from New to InProgress
  • Assignee set to Roel Standaert
Actions #2

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
Actions #3

Updated by Roel Standaert almost 2 years ago

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).

Actions #4

Updated by Roel Standaert almost 2 years ago

  • Status changed from InProgress to Review
  • Assignee deleted (Roel Standaert)
Actions #5

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.

Actions #6

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).

Actions #7

Updated by Roel Standaert almost 2 years ago

Yeah, we may need to look into that sometime. It's definitely not new.

Actions #8

Updated by Marnik Roosen almost 2 years ago

  • Assignee set to Marnik Roosen
Actions #9

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
Actions #10

Updated by Roel Standaert almost 2 years ago

  • Status changed from Implemented @Emweb to Resolved
Actions #11

Updated by Roel Standaert almost 2 years ago

  • Status changed from Resolved to Closed
Actions

Also available in: Atom PDF