Nicolas Garin
Nicolas Garin

Reputation: 233

Angular 2 click fired multiple times inside ngFor

I have a very weird problem (never saw before) with my component. I'm trying to remove a line when clicking on a button inside a ngFor list. When I have only one line it works but when It's more than one line the event is fired twice, once for the good line and once for the first line (after deleted the other line) :

<label>
    <div class="tag" *ngFor="let o of selectedOptions;">
        <span class="tag-text">{{ o.textContent }}</span>
        <button (click)="removeTag(o)" type="button" class="fa fa-times"></button>
    </div>
</label>

And here is my method witch is called twice (only if there is more that one "option") :

public removeTag (option: NxtSelectOptionComponent) {
    this.selectedOptions = [
        ...this.selectedOptions.filter(o => o !== option),
    ]
}

I've tried to use splice, I've tried to add stopPropagation... I don't understand I've done it tons of time and this is the first time I see that.

EDIT : the removeTag method is called when I click on .tag element this is why when I click on the button it is called twice, but I can't figure this out why... the (click) attribute is only on the button

Problem resolved : I've found the problem... FYI label tag will click on the button so if you have any (click) attribute it'll fired twice.

Upvotes: 11

Views: 10143

Answers (4)

rootOfSound
rootOfSound

Reputation: 139

Worth mentioning my situation in case it is tripping anyone else up. The component which housed the buttons was subscribed to an observable and never unsubscribed so this was keeping old components alive and firing their click methods. Make sure to .unsubscribe() any subscriptions you create in the button's parent component.

Upvotes: 3

mad_fox
mad_fox

Reputation: 3188

Full solution:

<label>
    <div class="tag" *ngFor="let o of selectedOptions;">
        <span class="tag-text">{{ o.textContent }}</span>
        <button (click)="removeTag(o, $event)" type="button" class="fa fa-times"></button>
    </div>
</label>

Then the method that takes the event:

public removeTag (option: NxtSelectOptionComponent, event) {
    event.preventDefault(); // This is needed to prevent the click event from firing twice on a label
    this.selectedOptions = [
        ...this.selectedOptions.filter(o => o !== option),
    ]
}

Upvotes: 2

Ashitosh birajdar
Ashitosh birajdar

Reputation: 470

Actually the second click will be called by the parent element. The browsers default behavior is, to trigger a click on the input, once the parent got clicked.

Use

event.preventDefault();

to stop second trigger.

Upvotes: 11

dev_in_progress
dev_in_progress

Reputation: 2494

Try this

<div *ngFor="let user of users; let i=index" >
    {{user.name}}
    <div>
        <a class="btn btn-danger" (click)="removeUser(i)">-</a>
    </div>
</div>

removeUser(i): void {
    this.users.splice(i, 1);
}

Upvotes: 0

Related Questions