#Quick question about $event objects

2 messages · Page 1 of 1 (latest)

frail mauve
#

Take this code for example…

component.html

<mat-toolbar (click)=“potentialNav($event)”>
    <mat-toolbar-row>
        <button mat-icon-button matRipple>
            <mat-icon fontIcon=“chevron_left”></mat-icon>
        </button>
        <span>Current View Title</span>
        <span style=“flex: 1;”></span>
        <button mat-icon-button matRipple>
            <mat-icon fontIcon=“close”></mat-icon>
        </button>
    </mat-toolbar-row>
</mat-toolbar>
<router-outlet></router-outlet>

Renders a simple primary app header bar that more or less looks like this…

———————————————————————————————————————
< Current View Title X
———————————————————————————————————————

Now in the component logic, we have something like this…

component.ts

public potentialNav(event: Event): void {
    // Determine button used, and act accordingly
    const button: string = event….something? // between event and target, if button clicked, read the `fontIcon` value off button’s <mat-icon>
    switch (button) {
        case “chevron_left”:
            this.navigateBack();
            break;
        case “close”:
            this.cancel();
            break;
        default:
            …
            break;
    }
}

private navigateBack(): void {
    this.router.navigate([“…”]);
}

private cancel(): void {
    this.router.navigate([“…”]);
}

Now the issue is this, that this primary app header bar is extremely dynamic, differing between different screens/views

As it is now, it exists in AppComponent and in app.component.html outside of the <router-outlet>

So, there are going to be a variety of different buttons rendered (or not), at any given time, from point to point

You may be thinking to yourself, “why don’t you just do this?”…

        <button mat-icon-button matRipple (click)=“navigateBack()”>
            <mat-icon fontIcon=“chevron_left”></mat-icon>
        </button>
public navigateBack(): void {
    this.router.navigate([“…”]);
}

But I feel that one event listener and delegation here is probably the simplest/easiest way of handling such dynamic complexities, because the primary AppComponent is not always going to have access to all of the relevant data and/or services it needs at a given time, to actually in fact be capable of handling the action itself. For example, what if it’s a form upload/submit button that lives in a child component? Makes a lot more sense for that child component to have it’s own specific onSubmit() and upload() methods, then individually import any relevant and necessary services required to. Are these methods function properly within itself only, rather than bog down the AppComponent

What such a child component WOULD need to know though, is whether a given user action within the app main header, should trigger it’s own internal logic, such as if it’s own private this.upload() or this.cancel() needs to run

To accomplish this, I’m thinking of creating a service that AppComponent simply writes to, and then any components interested in specific navigation events or application commands from the main app header would subscribe() to

In this case I’d modify AppComponent to remove any complex if or switch statements, simply sending off any buttons used like this

public potentialNav(event: Event): void {
    const button = … // again, still need to read `fontIcon` attribute somehow
    if (button) this.service.respondTo(button);
}

and then move any of that more complex if/switch logic into the new service, which might look like this…

public requested$ = new Subject<string>();

public respondTo(action: string): void {
    // complex if/switch logic moved here

    // once processed/verified
    this.requested$.next(action);
}
#

while then any such potential screen/components/services or whatever could then just import this service, and then specific methods like .navigateBack() or .cancel() can not only all be moved directly into the actual components that need them to exist, but can even be different/unique in logic between one component to the next, only requiring that any such components import this new service and then subscribe to requests with something like here…

import { TBDService as Service } from “@services/tbd”

@Component(…)
class SomeComponent {
    
    constructor(
        private service: Service
    ) {
        this.service.requested$.subscribe(
            (action: string) => if (action === “close”) this.cancel()
        );
    }

    private cancel(): void {
        // abandon process, and navigate elsewhere
    }
}

Now in plain old vanilla JavaScript, I’m more than familiar enough with how to accomplish this by adding event listeners to the document, <body>, or simply a specific container like <mat-toolbar> here in this instance, retrieving which child specifically was interacted with via event.target, proceeding to check of whether or not it was one of the buttons I should care about, further digging into it’s children if so, and then finally reading attribute value for fontIcon off the child <mat-icon>

But such as in this example

<… (click)=“method($event)” …>

$event is an angular Angular specific object/class/implementation, and varies WILDLY outside of the traditional HMTL DOM Element Event objects we’ve gotten to know and become familiar with, is it not?

So the question is, in what ways and how is it different? Is the actual original and simple Event object instance we all know and understand accessible in there some where, just in case it comes down to vanilla warfare? and either way, is there rather an official “Angular way” to go about such a particular design pattern as the event delegation described here?

Help me to under the $event object

Thanks