Title: How do you design and implement an application wide event detection mechanism to capture click events in a large scale Angular application?
Tags: javascript;angular;design-patterns;google-analytics;event-handling
Question: I'm trying to implement a solution to report detailed user activities to Google Analytics. Since the application is a single page application I know that I can capture the page change events from router, but this is only the basic part of the solution which is only for reporting seen pages.
Main point is since this is a large scale application containing 500+ pages/components and I don't want to create a logger service requires to change every page by adding it, I need to figure out a way to determine a centralized or application wide place to catch and detect button click events and maybe the pages they are fired from. Then I hope to be able to report detailed user activities on related pages to analytics like X button clicked from Y page.
For a central place I know there are interceptors for HTTP events, and router for page changes. Besides that I know maybe I can use change detection with hooks, but I'm not sure about should I have to work with hooking into application lifecycle level.
How can I implement a mechanism to catch and detect component elements events with related pages in Angular? Are there any best practices or abstractions that framework provides that may I utilize?
Note: I've learned that Google Tag Manager does the job specifically for the analytics purposes, but my question remain same which how to implement it with Angular.
Comment: If you need to track user's clicks etc. you may make use of Directives which will be placed on target elements and "report" to some service about events. Also, don't forget that you can use regular JS selectors for Directive selector thus targeting tons of native elements (in case you need it of course)

Comment: @TahaYavuzBodur I don't think that you can achieve this somehow without changing the source code (there is yet no such intelligent techs to detect what you want from code). But playing with directive's selector can give you a leverage to do what you want in an easier way since you may use already existing selectors (also you can use as many selectors for one directive as you wish.. I think)

Comment: @TahaYavuzBodur I think it's because of Angular's events handling. They are not synthetic and thus are not handled at global service which decides where to direct this event info. There is an `ng.getComponent` on `window` object (available when using Angular Ivy) where you can get component's instance by it's JS selector. However, I'm not sure if it works in prod (I think not)

Comment: @TahaYavuzBodur by synthetic events I refer to one article I've once read about difference in Angular's and React's event handling. In latter there is a synthetic events where there is one global listener which then delegates events to corresponding listeners. In Angular for each event listener you establish new event listener. So I suppose that in React it's possible to augment this global event handler to make use of it.

Comment: Since you want to catch events from button clicks you would need to implement only one directive with `button` selector which may then report about clicks. What about pages it's clicked on - I may suggest using `InjectionToken`s which would be provided for each page to be used in report (or simply report current URL). Also, you could make a base `PageClass` which would be extended by page components so that `button` may inject this class and report component's name for example.

Comment: I think you may concentrate on some kind of "migration" script which would ease this introduction pain if possible. (or to use replace in path provided by IDE which I use time to time to make simple migrations over a project)

Comment: @TahaYavuzBodur this article has explanation of syntetic event in React in first few paragraphs https://blog.logrocket.com/a-guide-to-react-onclick-event-handlers-d411943b14dd/#:~:text=Event%20handlers%20are%20used%20to,onClick%20in%20a%20React%20app.

Comment: In a more advanced approach, if you need to register all user actions on the website without leaving the task to the client side, this could be achieved using websockets and implementing the logic of the client side application on the backend. Each time the client makes an action like a click, that would be sent through the open websocket and the backend will send back the action that should be done. That way you would prevent making unnecessary HTTP calls each time you want to register an action.

Comment: @Sergey, thank you but actually this approach which seems very reasonable, is the approach I'm trying to avoid. Because in this case I have to change over 100 source files which are templates of the components and this is the original reason to ask this question which I'm in search of a way to this without changing lots of source files but in a central and preferably single place.

Comment: Thanks @Sergey, sorry I didn't meant without changing the source code, but my main concern is change less source files as possible so I was looking for some central places to put somehow dynamic logic. For that I thought that maybe I could use event bubbling or even in the directive case I was looking for a way to get a handle to templates of components maybe by somehow getting names from router object or runtime and then selecting without introducing directives to 100|+ source files.

Comment: In addition I see the value of directive approach which also I have used for dynamic component loading to get a reference to the template. However, this approach is counter-intuitive to me because of this question, why components is getting checked themselves, when you could do from a central place which is the exact opposite idea. I mean it's a bit indirect to me.

Comment: @Sergey, thank you for "I think it's because of Angular's events handling. **They are not synthetic**", I have investigated a bit but what is `synthetic` actually mean in this context or what is the opposite? Is it the opposite of event bubbling? I'm asking this because actually I always thought that what google tag manager doing is catching events, but I'm aware that it's doing it probably from rendered javascript. Do you mean that ordinary events like click or something is not propagated like it to be in vanilla javascript?

Comment: I often emit custom events on window, using (CustomEvent)[https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent] and (dispatchEvent)[https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent]. This way, anything can hook in and listen using `window.addEventListener` (assuming you dispatch it on window).

Here is the accepted answer: Custom event API is one option, but you have to despatch that event in all clicks.
So using HostListner we can achieve this.
Add a listener in the main component ie in app.componnet
@HostListener('window:click', ['$event'])
onClickEvent(event: MouseEvent) {
var target = event.target || event.srcElement;
var id = target['id']; //this will be the id of the target that you clicked
this.clickedValue = id;
And follow a naming pattern while you adding the id of each item. ie add the component name as the prefix of id. After prod build, the component name will be minified to one-letter name, it loses its original name.
For eg: if you have component mychild.component
<div&gt;
<h1 id=&quot;mychild-h1&quot;&gt;Test</h1&gt;
</div&gt;
Here, id is myChild-h1 and you can pick the components name from it. Now you have the id and name of the component that you clicked and create a service to log that info.
Attaching one sample code for check
CheckThis


{'source': 'StackExchangeDataset', 'subset': 'stackoverflow.com', 'metadata': {'AnswerCount': 1, 'CommentCount': 13, 'ContentLicense': 'CC BY-SA 4.0', 'CreationDate': '2020:00:06/17/20', 'FavoriteCount': None, 'PostTypeId': 1, 'Tags': '<javascript><angular><design-patterns><google-analytics><event-handling>', 'Title': 'How do you design and implement an application wide event detection mechanism to capture click events in a large scale Angular application?', 'ViewCount': 353, 'id': 62426800, 'user_AboutMe': "<p>I'm a software engineer with a passion of programming languages, software architecture and internal details.</p>\n", 'user_AccountId': 3870714.0, 'user_DisplayName': 'Taha Yavuz Bodur', 'user_Reputation': 135.0, 'user_Views': 72.0, 'user_id': 3206049.0}}