2

I'm trying to create a simple directive in Angular 2 so I can use jQuery UI Sortables on my page.

Currently I'm stuck at the point where I want to let the sortable directive know what function it should call on the page Component. I want this because you can register functions on events in the sortable plugin. I want the directive to call that function on the page's Component.

I currently have this:

@Directive({
    selector: '[sortable]'
})
export class SortableDirective {

    stopSortingEvent: any;

    constructor(el: ElementRef) {
        this.stopSortingEvent = el.nativeElement.getAttribute('stopSortingEvent');

        if(this.stopSortingEvent) {
          options['stop'] = this.stopSortingEvent; // This should be a reference to a function in the page Component.
        }

        $(el.nativeElement).sortable(options).disableSelection();
    }
}

So the idea is that I create a HTML element with the sorting attribute on it. Including an attribute with stop-sorting-event="functionOnComponent()".

That should be a function on the current page's component. But obviously this it doesn't work like this. My directive doesn't know it has to try and call it on another component.

So how can I do this? Is there a way to get a reference to my page component?


Update

I think the suggestion of pixelbits is great, using the events with the @Output annotations. But, it seems tricky to get it work in combination with jQueryUI. It seems like jQueryUI can't read the property of my directive.

Here is a demo to get a better idea of what I mean: http://plnkr.co/edit/Hfc5vxuMLW6yQwr4qW2W?p=preview

2 Answers 2

3

If you want to handle an event in the parent component, you can setup an Output event that the parent component can handle:

@Directive({
    selector: '[sortable]'
})
export class SortableDirective {
    @Output() stopSort:EventEmitter<any> = new EventEmitter();

    constructor(el: ElementRef) {
        var options = { 
           stop: () => {
              this.stopSort.emit();
           }
        };

        $(el.nativeElement).sortable(options).disableSelection();
    }
}

Then in your parent component:

<div (stopSort)="stopSortingEvent()" sortable>(...)</div>
Sign up to request clarification or add additional context in comments.

2 Comments

I would really love this solution, but can't get it to work. It seems like jQuery can't handle @Directive properties. I've made a small demo, maybe you have an idea how to fix this problem: it:plnkr.co/edit/Hfc5vxuMLW6yQwr4qW2W?p=preview
this is being redefined in a function. Use the arrow function instead. I've updated my answer. Here is the fixed version: plnkr.co/edit/Uo9saAamR7TuAcypnMsw?p=preview
0

In fact, you can inject the component the directive is attached on from the constructor and pass the name of the function as parameter:

@Directive({
  selector: '[sortable]'
})
export class SortableDirective {
  @Input('sortable')
  stopSortingEvent:string;

  constructor(el: ElementRef, page: PageComponent) {
    if(this.stopSortingEvent) {
      options['stop'] = page[stopSortingEvent].bind(page);
    }

    $(el.nativeElement).sortable(options).disableSelection();
  }

You could then pass the method name like this:

<div sortable="stopSortingEvent">(...)</div>

3 Comments

Is there anything special I have to do to make this work? Right now I get a lot of errors in console: Cannot resolve all parameters and it says that PageComponent (which is how my component is named) is undefined. Tried to decorate my PageComponent with @Injectable and added this to the directives construct param: @Inject(PageComponent) page: PageComponent, but that didn't work either.
In fact, you need to be careful of cyclic dependencies (Component -> Directive and Directive -> Component). Perhaps defining both the same module could fix your problems...
and forwardRef (see angular.io/docs/ts/latest/api/core/forwardRef-function.html) since hoisting isn't supported for classes. This applies if you want to use / reference a type defined later in your module file...

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.