2

How can delegate a click event on my board, what I do is I tell element to find element with the class open-preview And I can not do it like I do. Because the li is created dynamically in ruby loop. I do not know how I can delegate a click event in angular instead of find.

HTML CODE

<ul>
  <li open-preview>
    <a ng-href="{{product.url}}" title="{{product.brand}}">
      <img ng-src="{{product.urlImage}}" alt="{{product.brand}}">
    </a>
    <div class="descrip-product">
      <p>{{product.descriptionExcerpt}}</p>
    </div>
    <span class="open-preview">Quick view</span>
  </li>
</ul>

DIRECTIVE CODE

var app = angular.module('productPreview'),

 app.directive('openPreview', function($compile, $templateCache, $timeout) {

     return {
         restrict: 'A',
         transclude: false,
         templateNamespace: 'html',
         scope: true,
         link: function(scope, element, attrs) {

             element.find('.open-preview').bind('click', function() {
                // function here
             });
         }
     }
 }):

2 Answers2

1

It's better (if not outright required) to use an isolate scope because you are reusing the directive.

In isolate scope you can define how a directive can invoke an external (to the directive) function - this is done with scope: { param: "&" }

app.directive('openPreview', function() {
     return {
         restrict: 'A',
         scope: {
            openPreview: "&"
         },
         link: function(scope, element, attrs) {

             element.find('.open-preview').bind('click', function() {
                // invoke the function
                scope.openPreview({p1: 'foo', p2: 'bar'});
             });
         }
     }
 }):

Then the usage is:

<li open-preview="onPreview(p1, 'xyz', p2)">
  ...
</li>
New Dev
  • 48,427
  • 12
  • 87
  • 129
1

Just in case someone is looking for an Agular 8+ solution, here's my approach using a Directive.

import {
  Directive,
  ElementRef,
  EventEmitter,
  HostListener,
  Input,
  Output,
  Renderer2
} from '@angular/core';

@Directive({ selector: '[delegateClick]' })
export class DelegateClickDirective {
  @Input() delegatedTo: string = '';
  @Output() delegateClick: EventEmitter<InnerClickEvent> = new EventEmitter<InnerClickEvent>();
  @HostListener('click', ['$event']) onClick(event: MouseEvent) {
    const target: HTMLElement = event.target as HTMLElement;
    // do nothing if the clicked item is the decorated element itself or delegated not set
    if (target === this.elementRef.nativeElement || !this.delegatedTo) {
      return;
    }
    event.preventDefault();
    this.handleClick(event.target as HTMLElement);
  }
  constructor(private elementRef: ElementRef) {}

  handleClick(element: HTMLElement) {
    if (element.matches(this.delegatedTo)) {
      this.delegateClick.next({ detail: element.dataset, target: element });
      return;
    }
    // also stop searching if we reach the container itself
    if (element === this.elementRef.nativeElement) {
      return;
    }
    this.handleClick(element.parentElement);
  }
}

export interface InnerClickEvent {
  target: HTMLElement;
  detail: DOMStringMap;
}

Use it in the template like this:

<ul (delegateClick)="delegatedClick($event)" delegatedTo="li">
        <li>1</li>
        <li>2</li>
        <li>3</li>
</ul>
WSD
  • 3,243
  • 26
  • 38