Angular component with varying appearance and click event

Angular component with varying appearance and click event

It seemed to be very simple: I wanted to create a component which displays a link and provides the ability to disable the link. Namely, disable the click event on the link and make it look disabled. So, how to handle the click event correctly in Angular?

Contents

In order to make the link disabled, I decided to convert it to a span. A few minutes later the component was created:

Shell

I thought it would be the easiest to display a span when the component is disabled and a link when it is enabled. Thus I filled in the component with code:

HTML
my-button.component.html
TypeScript
my-button.component.ts

Then I created a sample application to test the new component:

HTML
app.component.html

Piece of cake… Except it didn’t work:

Despite the conditions being alternatives, only the link worked – the span was hidden. I supposed there was a problem with disabled, so I started creating numerous variations on it, until I finally noticed the problem: the span element was displayed under correct conditions, but without any contents.

Why? Because you can’t use the same <ng-content> twice in a component. Only the first one will be filled with content, even if it is hidden. One of the solutions could look like this:

HTML
my-button.component.html

I put the <ng-content> only once, in a template, which I referenced twice from both span and link. This is one of the workarounds for referencing an ng-content in several places in a component.

That behavior is regrettably by design, and it is explained i.a. in this bug report. Please note that this trick cannot be used to display that ng-content more than one time in the component. I wrote about it in the consecutive post.

As you can see, now the contents is displayed correctly:

Click event

The next step was adding support for the click event. I added it in the component and handled in the application:

Here is the component:

TypeScript
my-button.component.ts

And sample application:

HTML
app.component.html
TypeScript
app.component.ts

Unfortunately, when testing the application, I discovered two new problems:

Button #1 was clicked
Button #1 was clicked
Button #2 was clicked
Button #2 was clicked
Button #3 was clicked
  1. although the span did not have the click event attached, that event (from the component) was fired when a user clicked the disabled link
  2. clicking the enabled links raised two events

At first, I ignored double events and I thought that the solution found in Google Material’s source code (button.ts) will help for the first issue. I added _haltDisabledEvent() to the span:

HTML
my-button.component.html
TypeScript
my-button.component.ts

It turned out that it helped for a real user using a web browser to not be able to fire the event, but a clever user could still click in the space around the label, still within the component. And it did not help for a unit test which checked if the component is clickable.

Another attempt also failed, even worse, as it did not prevent from clicking in any case:

TypeScript
my-button.component.ts

Taking into consideration that even enabled component worked incorrectly (two events were fired), it led me to think that the initial approach was wrong. Probably the component should implement the ControlValueAccessor interface…

To be continued in future.

Lessons learned

Do not use two <ng-content>

Don’t implicitly declare all click events – some are handled automatically

Preventing clicking in a component is really tricky

Leave a Reply

avatar
  Subscribe  
Notify of