4

My aim is to be able to allow users to sort a list of commands. I am developing my application using Angular2/Typescript.

So I looked around for angular2 based libraries that may provide sortable functionality similar to JQueryUI Sortable, But couldn't find much.

I came across this SO post, which demonstrated how to integrate JQuery with angular2. Using the plunk provided in one of the solutions for this post, I could develop a sortable behavior with angular2. See this plunk. This is working as expected.

@Component({
  selector: 'my-app',
  directives: [SMSortable],
  providers: [],
  template: `
    <div>
      <p>This is a list that can be sorted </p>
      <div sm-sortable>
        <p>Item 1</p>
        <p>Item 2</p>
        <p>Item 3</p>
      </div>
    </div>
  `
})
export class App {
  constructor() {
    this.name = 'Angular2'
  }
}

The idea was to define a directive which would apply the sortable behavior to the native element using JQueryUI sortable() API. And then use the directive in the component's template.

@Directive({
  selector: "[sm-sortable]"
})
export class SMSortable{

    constructor(el: ElementRef) {
        jQuery(el.nativeElement).sortable( {
              start: function(event, ui) {
                  console.log("Old position: " + ui.item.index());
              },
              stop: function(event, ui) {
                  console.log("New position: " + ui.item.index());
              }
        });
    }
}

This works well when the component's template has all native elements. But if my template has custom angular2 components, this stops working.

See this not-working plunk.

@Component ({
  selector: 'sm-cmd',
  template: '<ng-content></ng-content>'
})
export class SMCommand {

}

@Component({
  selector: 'my-app',
  directives: [SMSortable, SMCommand],
  providers: [],
  template: `
    <div>
      <p>This is a list that can be sorted </p>
      <div sm-sortable>
        <sm-cmd><p>Item 1</p></sm-cmd>
        <sm-cmd><p>Item 2</p></sm-cmd>
        <sm-cmd><p>Item 3</p></sm-cmd>
      </div>
    </div>
  `
})
export class App {

}

In this case I am able to drag the item, but not able to drop it. The moved item reverts back to its original location. I added console.log to see the item index at start and stop event during sort. The value remains same.

I am not able to debug further on this. Could somebody provide some input on this?

Community
  • 1
  • 1
tyrion
  • 714
  • 2
  • 7
  • 27

1 Answers1

5

The problem is actually very simple: since you are using custom element sm-cmd browser doesn't know what rendering-model to use (block or inline). By default it applies inline one, which results into a collision in terms of element dimensions, because you have block-level p inside inline sm-cmd. So the browser doesn't calculate block dimensions properly.

So the solution is this simple CSS rule:

sm-cmd {display: block;}

Also make sure you initialize jQuery plugin in ngAfterViewInit hook:

@Directive({
  selector: "[sm-sortable]"
})
export class SMSortable{

  constructor(private el: ElementRef) {}

  ngAfterViewInit() {
      jQuery(this.el.nativeElement).sortable( {
            start: function(event, ui) {
                console.log("Old position: " + ui.item.index());
            },
            stop: function(event, ui) {
                console.log("New position: " + ui.item.index());
            }
      });
  }
}

Demo: http://plnkr.co/edit/QEd9wozXSZlqT07qr51h?p=preview

dfsq
  • 191,768
  • 25
  • 236
  • 258