power-media / prado3

Automatically exported from code.google.com/p/prado3
Other
0 stars 0 forks source link

TClientScript doesn't render cirrectly itself if set visible in callback #401

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
What steps will reproduce the problem?

1. Put a not-visible TClientScript on a page (eg. inside a <TActivePanel 
Visible="false" />)
2. make TClientScript visible inside a callback
3. TClientScript will render a <script> tag, it will be evaluated by the 
browser but any defined function won't be available to the page

What is the expected output? What do you see instead?

The tipical workaround to this issue is to eval() the javascript code from the 
page or to use the DOM to create a new <script> element and add it to the 
document as a child.
It's quite simple to create a patch for TClientScript.php to get this working, 
but to avoid side problems this probably needs to be addressed clientside using 
a registry.
A registry could avoid problems like double-registrations of the same js code 
on multiple callbacks, missing unloading of the js when the TClientScript is no 
more visible, etc.

Original issue reported on code.google.com by ctrlal...@gmail.com on 14 May 2012 at 3:11

GoogleCodeExporter commented 8 years ago
No need for any extra registry, as since r3010 we already have a resource 
registry on the client side. However, you won't even need to use that directly 
- just call $this->getPage()->getClientScript()->registerScriptFile() to 
register/render the script file reference when in a callback, and r3010 will 
take care of the rest. It will load the script after committing any changes to 
the DOM, but prior to executing any inline script blocks returned by the 
callback.

Alternatively, the Prado.Element client-side class could be patched so it looks 
for any <script></script> blocks (using regexps) in its replace/append/insert 
methods, extracts those from them prior to executing said DOM-manipulation 
operations, and then when done executes the contents of / loads the extracted 
script blocks/files. This would be a more general solution, but it would be 
also a really ugly one, with potential security and/or compatibility 
implications.

As for unloading: once loaded, you can't really unload JavaScript files - 
because even if you'd remove the <script> element from the DOM, there would be 
most likely tons of references lingering around in both the window namespace 
and also probably to instantiated objects, which would prevent any actual 
unloading of the code in the file. That, however, shouldn't be an issue, unless 
you're loading gigabytes of ever-changing script files from rapidly fired 
callbacks. But if you do that, then your application probably shouldn't be 
written using Prado anyway.

Original comment by google...@pcforum.hu on 17 May 2012 at 1:07

GoogleCodeExporter commented 8 years ago
Committed the registerScriptFile() patch in r3142 as suggested.
The problem still exists in inline script blocks containing function 
declarations; i've attached a testcase for this.
I see three possible solutions:

1) dirty and quick: render the inline script block as an external asset file 
and register it using the r3010 method.
2) better, longer, maybe overkill: subclass the resource registry to support 
inline script blocks, and handle them the same way as javascript files;
3) document the limitation: TClientScript is able to render script files and 
inline script blocks in callbacks, but any functions declared in an inline 
script blocks won't be usable.

I agree with you about js unload being almost unnecessary.

Original comment by ctrlal...@gmail.com on 18 May 2012 at 4:11

Attachments:

GoogleCodeExporter commented 8 years ago
Committed in r3143 a small version of solution #2.

Original comment by ctrlal...@gmail.com on 18 May 2012 at 6:11

GoogleCodeExporter commented 8 years ago
The problem with this solution is that it will yield to a different behaviour 
in normal/postback and callback requests. For ex. try putting a  
"document.write()" call into the script block, and observe the behaviour. 

Even though the latter is a very special case, other calls might be also 
problematic. If such inline scripts blocks are rendered as part of a 
normal/postback response, they will execute instantly where they are in the 
page and in a synchronous fashion. However, if they are added to the document 
as part of a callback response (using createElement), then they will be 
executed out-of-band and practically all blocks at once, only then when all DOM 
modifications are complete and any dynamic script file includes are loaded - 
regardless of where they actually occour in the original template/markup code. 
All this will lead to inline script blocks behaving differently in normal, 
postback and callback scenarios, which will ultimately hurt compatibility and 
graceful fallback.

I think rendering of inline script blocks should simply not be allowed from 
callbacks, as consistent behaviour can not be produced this way. If someone 
needs to have some code dynamically executed from a callback, they should be 
forced to call TClientScriptManager::registerEndScript() explicitly. Trying to 
render inline script blocks using the TClientScript component in a callback 
should either raise an exception (preferred way) or the script block should be 
silently dropped (not recommended).

Original comment by google...@pcforum.hu on 18 May 2012 at 11:55

GoogleCodeExporter commented 8 years ago
I agree with your point that callback-generated script blocks will never act 
exactly like normal ones, and the possible problematic scenarios you described.
Mine was just an attempt to workaround a basic problem, and surely is not a 
solution for the whole issue.

The patch should not be behaviour-pejorative since even before r3143, when 
TClientScript was rendering inline <script> blocks in callback requests, they 
would have been evaluated at the time when Prado.Element.replace was called 
upon the boundary containing the actual rendered script block.

I don't like too much the idea of raising an exception when TClientScript is 
rendered in a callback, since this would cut off a functionality that was 
"working" for basic javascript in previous prado versions (i quoted "working" 
to remark that there are problems with it).
I'd prefer to inform developers about the issue let them the ability to decide 
if TClientScript, with its limitations, is the right tool to use.

Original comment by ctrlal...@gmail.com on 19 May 2012 at 9:55

GoogleCodeExporter commented 8 years ago
added a warning in r3144; i've mostly quoted the points of your previous comment

Original comment by ctrlal...@gmail.com on 19 May 2012 at 10:07

GoogleCodeExporter commented 8 years ago
Th is was changed again in r3150, since i saw a few examples of TClientScript 
not acting in a backward-compatible way after the change. Thus, i preferred to 
keep TClientScript the old way and to add a TActiveClientScript control 
instead. This leads to a better separation of ajax-enabled features and permits 
further specialization of the active control.

Original comment by ctrlal...@gmail.com on 29 May 2012 at 9:19