Events are fun right? Well, only in part to be honest.ย For example, what do you do if you want to catch the same event but at different places?
This is where JavaScript’s addEventListener() comes into play. In short it allows you to add as many event-handlers to the same event as your heart desires. But raw unadulterated JavaScript is a bit of a mess, so I decided to wrap this up in clear cut objects. Oh, and I added a “fixed” event for when you want to have objects for standard events as well.
So now whenever you want to hook an event without ruining your published event-handlers (for instance in TW3CustomControl) you can just use one of these ๐
Enjoy!
unit eventobjs; interface uses w3c.dom, SmartCL.Components, SmartCL.System; type TEventObjTriggeredEvent = procedure (sender:TObject;EventObj:JEvent); TEventObj = class(TObject) private FOwner: TW3TagObj; FAttached: Boolean; FEventName: String; protected procedure HandleEvent(eobj:variant);virtual; public Property Attached:Boolean read FAttached; procedure Attach(EventName:String); procedure Detach; constructor Create(AOwner:TW3TagObj);virtual; destructor Destroy;Override; public Property EventName:String read FEventName; Property Owner:TW3TagObj read FOwner; Property OnEvent: TEventObjTriggeredEvent; end; TFixedEventObj = class(TObject) protected FAttached: Boolean; FOwner: TW3TagObj; procedure HandleEvent(eobj:variant);virtual; protected function DoGetEventName:String;virtual;abstract; public Property Attached:Boolean read FAttached; procedure Attach; procedure Detach; constructor Create(AOwner:TW3TagObj);virtual; destructor Destroy;override; public Property Owner:TW3TagObj read FOwner; Property OnEvent: TEventObjTriggeredEvent; end; TElementRemovedEvent = class(TFixedEventObj) protected function DoGetEventName:String;override; end; TElementAddedEvent = class(TFixedEventObj) protected function DoGetEventName:String;override; end; implementation //############################################################################# // TElementAddedEvent //############################################################################# function TElementAddedEvent.DoGetEventName:String; begin result := "DOMNodeInserted"; end; //############################################################################# // TElementRemovedEvent //############################################################################# function TElementRemovedEvent.DoGetEventName:String; begin result := "DOMNodeRemoved"; end; //############################################################################# // TFixedEventObj //############################################################################# constructor TFixedEventObj.Create(AOwner:TW3TagObj); begin inherited Create; FOwner:=AOwner; Attach; end; destructor TFixedEventObj.Destroy; begin Detach; inherited; end; procedure TFixedEventObj.Attach; begin if FAttached then Detach; FOwner.Handle.addEventListener(DoGetEventName,@HandleEvent,true); FAttached := true; end; procedure TFixedEventObj.Detach; begin if FAttached then begin FOwner.Handle.removeEventListener(DoGetEventName,@HandleEvent,true); FAttached := false; end; end; procedure TFixedEventObj.HandleEvent(eObj:variant); begin if assigned(OnEvent) then OnEvent(self, JEvent(eObj)); end; //############################################################################# // TEventObj //############################################################################# constructor TEventObj.Create(AOwner:TW3TagObj); begin inherited Create; FOwner := AOwner; end; destructor TEventObj.Destroy; begin if FAttached then Detach; inherited; end; procedure TEventObj.HandleEvent(eobj:variant); begin if assigned(OnEvent) then OnEvent(self,JEvent(eObj)); end; procedure TEventObj.Attach(EventName:String); begin if FAttached then Detach; FEventName := EventName; try FOwner.handle.addEventListener(FEventName,@HandleEvent,true); except FEventname:= ''; FAttached:=false; exit; end; FAttached:=true; end; procedure TEventObj.Detach; begin if FAttached then begin try FOwner.handle.removeEventListener(FEventName,@HandleEvent,true); finally FEventName := ''; FAttached := false; end; end; end; end.
I read this three times and I am still clueless as to how they are used. Sorry! Can we get a demo of each of the Objects?
Ha ha ๐ Yeah i did write that rather quickly.
Now, if you look in the RTL, you notice that the events we expose, like OnClick etc. are mapped directly. So whenever you assign an event-handler, that event is taken right?
FMyComponent.OnClick := procedure (sender:TObject)
begin
end;
When the event is taken then you cant add more event-handlers to it. Which sucks if you want to catch that event on many different places. By tradition, Object Pascal allows only one event-handler per event.
Javascript however, allows several event-handlers per event. So the code above allows you to do that.
So now you can do:
FFirst := TEventObj.Create(w3Panel1);
FFirst.attach(‘onclick’);
FFirst.OnEvent := procedure (sender:TObject;EventObj:JEvent)
begin
end;
FSecond:= TEventObj.Create(w3Panel1);
FSecond.attach(‘onclick’);
FSecond.OnEvent := procedure (sender:TObject;EventObj:JEvent)
begin
end;
Voila! Now we have two event handlers for the same event! Both will kick in whenever you click on the panel ๐
NOTE: The classes above attach to the JavaScript side of things, so event-names are case sensitive. Also, you get access to the JavaScript event object directly.
About “TFixedEventObj”.
This is just a class which does the same thing — but allows you to wrap events in pre-defined objects. You dont need this class if you are happy with the Attach(Eventname). You see that i have implemented two “fixed” classes, which map the OnElementInserted and OnElementRemoved, which fires on the javascript side whenever you add or remove a component from .. well, a component.
So the first will fire when you do this:
FChild := TW3CustomControl.Create(FParent);
Above, the TElementAddedEvent will fire.
FChild.free; // removes child from FParent
And here the TElementRemovedEvent will fire.
I just wrapped these two events for ease of use, nothing more. There is nothing magical about these
Thanks, that helped tremendously. I did not know this was possible in any event-driven language.
Its standard in C#, C++, Java and JavaScript ๐
Also, the main use for those are when you write custom controls.
You dont want to kidnap the OnClick etc. events, because then the user of your component wont have any.
With the above you can write components that act on events, and still expose the standard events without ruining anything.
Ex:
TMySuperPanel = class(Tw3CustomControl)
published
property OnClick;
property OnMouseDown;
end;
In the above we expose the OnClick event and OnMouseDown which are defined in TW3CustomControl. So users can now attach their code to them. Thet remained “un-used” even though we might listen to the same events in our code (using the classes in this article)