Closed GoogleCodeExporter closed 9 years ago
Test suite attached.
Original comment by google...@pcforum.hu
on 19 Jun 2009 at 5:16
Attachments:
OK I just got a simpler idead to fix the issue. That is: there should be a
client-
side registry these JavaScript-supported controls, and the JS-objects
themselves
should be modified so they register themselves and the events they want to
listen to
in that registry. Now if there's a registration for the same control, the
registry
should first dispose of the old object for that control - if there's any
already -
and register the new object as the handler for the control and for the future.
It is
important tho that when deregistering, the object should release all event
handlers
and such too it hooked when it was created. It is also important that all
controls
preserve their state information in the DOM (instead of in the JavaScript
object) or
that when a replacement in the registry occours, all state fields of the
previous
object get copied to the new one (they should have a method for this in that
case),
so that the control's state information doesn't get lost.
I'm also thinking about whether this issue with multiple instances does affect
other
controls too, which render "Event.observe()" snippets in their OnPreRender()
methods
using registerEndScript(). That might lead to multiple events (event handlers)
fired
for a single event on a single control too, after one or more callbacks have
been
executed on the page, but I will still have to check that.
Anyway, it would be good to hear something from the Prado core developers about
this
issue, as it is a really serious design flaw I think, which could affect
practically
all Prado applications and lead to very weird and seemingly untraceable bugs,
therefore should be fixed ASAP.
Original comment by google...@pcforum.hu
on 20 Jun 2009 at 1:50
Thanks for your detailled report.
I'm not really a javascript expert, so, I'm not sure how I can fix this issue.
We
already have a client side registry since 3.1.5, so, we may implement your idea
in it.
It would be great if you have some code to provide to help us to fix this issue.
Anyway, I move this issue to 3.2. If we can find a good solution, we may
backport it
to 3.1.
Original comment by Christophe.Boulain@gmail.com
on 19 Jul 2009 at 4:33
[deleted comment]
Events are now removed and added for every postback/callback
Original comment by uaca...@gmail.com
on 10 Mar 2011 at 8:58
[deleted comment]
This patch causes regression in 3rd party components that
1. are derived from TActiveControl
2. use "Event.Observe(...)" calls to install event handler(s)
3. the handler codes are emitted/registered either with THtmlWriter::write() or
TClientScripManager::registerEndScript() calls
4. the latter are done so prior to calling the TControl's render() method (like
for ex. in OnPreRender(), or in the control's render() method, but before the
call to the parent::render), or if overridden, prior to the
TActiveControlAdapter::registerCallbackClientScript() or
TClientScriptManager::registerCallbackControl() call.
In these controls the extra event handlers won't be actually ever called, and
thus some or all functionality of the controls will be lost.
This is because the new Prado.WebUI.PostBackControl.initialize() method unbinds
ALL existing event handlers, therefore disabling all existing bindings for the
control it has been created for. So if the 3rd party component has registered
it's handlers prior to the construction of the client side object, those event
handlers will be all discarded. This didn't happen prior to r2888, but does now.
The solution is to postpone event handler registration after the construction
of the client-side object (ie. emit the event handler bind code after the above
mentioned calls, preferably to the end of the control's ::render() method), or
to subclass the client-side object itself, and move all event registration code
into the newly created control class. (The latter is, however, tricky as with
JavaScript/prorotype there's no straighforward way to override the
initialize/onInit() method and still call the parent implementation).
Original comment by google...@pcforum.hu
on 12 Jul 2011 at 9:18
you mean the users controls that are derived from TActiveControl? If so those
controls mus register the events again or the events will fire as many times as
the control is created.
Do you have a example of this behavior?
Original comment by uaca...@gmail.com
on 13 Jul 2011 at 2:32
No, I mean the regression occours when all 4 above mentioned conditions are
met. It holds also true for any application-level code that meet criteria 2-4.
and tries to install event handlers on controls that meet criteria 1.
I've attached a simple test case. Try to run that with and without r2888
applied to the framework!
A framework-level - and much cleaner (in my opinion) even though less simpler -
solution would be to leave it up to the client-side objects deregister their
own handlers (instead of purging all handlers unconditionally), as that would
preserve all external handlers installed by either other controls or by
application-level code.
Original comment by google...@pcforum.hu
on 13 Jul 2011 at 10:15
Attachments:
There's another issue with patch r2888, and that is that it only takes care of
event handlers installed inside the prototype/Prado framework but does not
consider hooks and callbacks outside of those. However, there might be very
well other kinds of hooks and callbacks active and installed, either for ex. in
third part libraries or even just as simple things as outstanding periodic
timers (windows.setTimeout()).
See attached test case how patch r2888 does't solve duplicate registration
issues with controls that have also timers installed.
-
As already described above, the solution to all these would be to
1. define a "deinitialize" method in the client-side base control class
2. the client-side control registry should call that method on the previous
instance of the client-side wrapper when a control object with the same ID is
being constructed/registered
3. each client-side control should override said "deinitialize" method and use
it to deinstall all their event handlers (meaning explicitly and only those
installed by them), stop all timers, and release all other resources they've
allocated
This would solve both the regression and the issue with the timers and other
possible callbacks.
Original comment by google...@pcforum.hu
on 13 Jul 2011 at 10:32
Attachments:
Sorry, I just realized that even the above described method wouldn't solve all
problems, as for ex. when the timer control would be a dinamically created one,
and would be removed (or not re-instantiated) in a refresh cycle, that would
still leave a dandling client-side object for it on the page. That would happen
because in this scenario there would be no attempt to re-register a control
with the same ID, so de-registration of the already existing client-side object
instance wouldn't happen either - where in fact the latter should happen, since
now the host control have been removed from the control tree on the server side
and doesn't exists anymore. So, in fact, since the client-side object for the
timer would be still there, it would still try to issue the scheduled callback
- but would fail at that and would raise a server-side exception (probably
periodically), as Prado wouldn't be able to find the host control for the
callback.
So, I think the only really perfect solution would be the one described in my
original comment, ie. keeping track of all object disposals and creations, and
manage client-side wrapper disposal from the server-side. That, however, would
be fairly complex and consume probably a lot of resources. So, I'd say
realistically we should settle for the non-perfect solution described in
comment #10, which would still take care of 99% of the cases.
There's also another solution to the problem, which however is more like a
workaround. The solution could be that 1. all controls should emit their markup
with at least one html element having a distinctive and unique ID (this now
happens only if the ClientID property of the control has been referenced prior
to rendering), 2. even controls that basically don't need any HTML markup (like
the TTimeTriggeredCallback) would need emit some "dummy" code (like a hidden
div). Then, after each callback all registered active controls should be asked
by the framework/client-side registry (by the means of a call to a method
defined for that purpose in the base client-side wrapper) to check for the
existence of their markup counterpart in the DOM (or for that dummy div in the
case where basically no markup is needed), by looking for that specific unique
ID. If they can't find their markup in the DOM that means that their host
controls have been eliminated from the control tree during the last
callback/refresh cycle - and in this case the objects should just "commit
suicide", and disable themselves. That would solve the situation with the
lingering client-side control objects described above - but without the heavy
burden to have to keep track of each and all control's lifecycle on the server
side, and communicate that to the client-side control manager.
Original comment by google...@pcforum.hu
on 13 Jul 2011 at 10:56
As of late i have been getting some issues where i need control over a control
created in the client side, simple stuff like update the value of a slider or
call a event on the server without recreate the whole repeater from database to
just to give the user a warning about delete a item. Giving it some thought i
think the problem is that PRADO keeps track of "static" controls in the
template but does not keep track of dynamic controls. If we do have someway of
keep tracking of all the server controls i guess it would be necessary to
reflect the update and destroy of those controls on the client side and bamm,
problem goes away. This also gives a few other benefits, for example, today it
not possible to have a TActiveMultiView simple because the necessary javascript
for a control is created ONLY at postback and event if a control is created at
callback it wont work on the client side. As you described above this is a
consume a lot of resources and would not be backwards compatible.
Original comment by uaca...@gmail.com
on 13 Jul 2011 at 1:03
I have actually created a TActiveTabPanel which only renders a single tab at
all times (but of course depending on which tab you click, it will be a
different one). So that's not a problem. Also controls created inside callbacks
render fine and have an appropriate client-side wrapper created, too. (The
trick is that you have to keep track of those dynamically added controls in the
pagestate, and recreate them prior to letting Prado handle the callback, or it
won't find the callback target.)
So, the problem isn't with dynamically created controls (even though it's PITA
to keep track of them in the pagestate and recreate them in every callback, but
maybe there could be some help from the framework to do that). The problem is
with updating and deleting already existing controls.
Original comment by google...@pcforum.hu
on 13 Jul 2011 at 1:47
Well i did a TActiveTabPanel myself, try to add a validator to it, it will only
work if you have a validator outside of the TActiveTabPanel or in the default
view. Every time the page renders validator manager try to register the
validator with the form, it there is none validator wont work anymore even you
you create it in the callback, that is true for a few others controls. Problem
is that validator dont register the javascript source if there is not at least
one visible validator.
What i think that should be done is every time a control is created, updated or
made invisible on the server it should let the client side know and let it
handle the event registration.
Original comment by uaca...@gmail.com
on 13 Jul 2011 at 2:18
That sounds like a different issue. The validation framework has nothing to do
with the client-side control registry per se. It's using a different registry,
with obviously having with its own issues.
Another issue I'm aware of is however, that the registration of new client-side
scripts won't be taken care of in callbacks either. That means that if you try
to instantiate a control in a callback that uses and registers a .js file
otherwise properly (with TClientScriptManager::registerScriptFile() or
~::registerPradoScript()), that isn't included/referenced on the original page
already, Prado won't load the additional .js file(s) when processing the
callback response either, thus preventing the control from functioning
properly.
This issue can be worked around by including a hidden instance of the control
class(es) in question on the original, always-visible porition of the page, in
which case the controls instantiated in the callbacks will function properly.
This however is is an ugly hack, and also might waste bandwidth and cause
loading delays in cases where you have a lot of controls which can be
instantiated in callbacks, but most of the time most of them won't actually
ever get to appear on the page during the whole life cycle of the latter.
Original comment by google...@pcforum.hu
on 13 Jul 2011 at 2:53
Created a new ticket (issue 347) for the validation problem which is distinct
from this one, and has to do rather with missing registration than with
duplicate/multiple registrations. (And it's an issue with the validation
registry, not with the control registry.)
Original comment by google...@pcforum.hu
on 13 Jul 2011 at 3:38
Fixed your validation problem. See issue 347. Validation now seems to be
working flawlessly in my TActiveTabPanel control, even on initially non-visible
tab pages. If you still have problems in yours then please supply a test case
in issue 347.
Original comment by google...@pcforum.hu
on 13 Jul 2011 at 4:33
I believe there is a problem in revision r2888 of the inlineeditor.js file.
On line 33 I realized that the code was added
/ / Issue 181
$ (this.options.ExternalControl). stopObserving ('click', this.onclickListener);
But it took the old line 33 out of the IF.
In other words, the lines 33 to 35 should not be?
if (this.options.ExternalControl)
{
/ / Issue 181
$ (this.options.ExternalControl). stopObserving ('click', this.onclickListener);
Event.observe ($ (this.options.ExternalControl), 'click', this.onclickListener);
}
Original comment by cezarpirajant@gmail.com
on 13 Sep 2011 at 2:52
applied the patch to inlineeditor.js in r3038, thank you
Original comment by ctrlal...@gmail.com
on 13 Sep 2011 at 3:37
[deleted comment]
This issue is still marked as fixed even though the testcase attached to my
comment #10 is still successfully exposing the very basic flaw of patch r2888,
even wit the latest trunk sources. Also the problem with killing all event
handlers attached to the control wrapper, but not related directly to the
creation of the latter, is still present, as described in comment #7.
As already explained above the only proper solution that would solve all such
issues once and for all would be to add some kind of standard deinitialization
procedure to the Prado client-side control wrappers, which should be trusted to
remove the event handlers installed by them (and only those) whenever the
controls the wrappers are representing get replaced during a callback.
This would 1. prevent having multiple client-side wrappers lingering around
attached and/or operating on the same control(s), 2. eliminate all problems
arising from automatic and unconditional detachment of all (also external)
event handlers whenever recreating a control wrappers (which is what r2888 is
doing).
Original comment by google...@pcforum.hu
on 2 Mar 2012 at 3:42
[deleted comment]
[deleted comment]
[deleted comment]
[deleted comment]
[deleted comment]
I've created a patch to fix both issues - and leave room for even more flexible
client-side control management.
The key changes are:
1. there's now a new Prado.WebUI.Control class which is supposed to be the base
of all client-side control wrappers. This class automatically detects whether a
wrapper is being instantiated "over" a previous wrapper with the same ID, and
if that's the case, it makes sure that the previous wrapper gets properly
disposed of, before firing up the new wrapper. Wrappers are now supposed to
properly clean up after themselves, including - but not limited to - disposing
of all their event handlers. This makes sure that there will be no lingering
event handlers left when recreating a wrapper, but also makes sure that any
event handlers installed not directly by the wrapper stay intact (which was not
the case since r2888).
2. instead of the original Event.observe() all wrappers use now
Prado.WebUI.Control's observe() method to install their event handlers. The
latter method keeps track of all the registered event handlers, and disposes of
them when the wrapper gets destroyed (because of a new wrapper replacing it -
see above). This simple extension takes out the pain of deinstalling event
handlers when destroying the wrapper, while requiring only minimal
modifications to any existing wrappers (ie. you only have to replace
"Event.observe()" calls with "this.observer()" class). Event observing can be
stopped with a call to the this.stopObserving() method.
3. similar changes implemented for timers (both periodic and one-time): instead
of window.setTimeout() and window.setInterval the controls are calling
this.setTimeout() and this.setInternal(). When the timeout is over, a wrapper
function checks whether the control wrapper is still alive, and only fires the
timer event if it is; for periodic timer events (ie. setInterval), it
automatically cancels the timer upon deinitialization of the object. That is,
if it hasn't been already cancelled with a call to this.clearInterval().
4. I've also incorporated changes to take advantage of the new class
inheritance syntax introduced in Prototype 1.6, and to make all class
initializations look uniform
I didn't touch some client wrapper classes (like datepicker.js, ratings.js)
which will be therefore still prone to the two existing bugs described above. I
left them untouched because I'm not using them and didn't want to build
projects to test them. However, I see no problem with applying the same changes
to them (ie. make them descendants of the new base control class, remove their
custom initialization code, and make them use automatically detached observers).
I've tested most of the other controls, however, peer review wouldn't hurt them
either - even though their core behavioral codebase hasn't been touched at all.
Original comment by google...@pcforum.hu
on 11 Mar 2012 at 11:18
Attachments:
patch committed as r3117
Original comment by ctrlal...@gmail.com
on 24 Mar 2012 at 9:30
This patch needs a correction. In TTimerTriggerCallback::setInterval() the line
$client->callClientFunction('Prado.WebUI.TTimeTriggeredCallback.setInterval', array($this, $interval));
should be changed to
$client->callClientFunction('Prado.WebUI.TTimeTriggeredCallback.setTimerInterval', array($this, $interval));
to reflect changes in the client-side class method naming of r3117. Otherwise
the timer interval will not be updateable from callbacks and every timed
callback will raise an exception in the browser upon return, due to the
setInterval method references in the PHP code not being existent in the client
wrapper JS class. However, this exception will only be obvious if running the
browser in the debug mode or with the JavaScript console enabled, as otherwise
the exception will be supressed ("eaten") in Prado.CallbackRequest::__run().
Original comment by google...@pcforum.hu
on 11 May 2012 at 3:23
patched in r3139, thank you
Original comment by ctrlal...@gmail.com
on 11 May 2012 at 3:52
Original issue reported on code.google.com by
google...@pcforum.hu
on 19 Jun 2009 at 4:35