Angular 2 Dragula Model updating incorrectly

2019-03-09 22:24发布

Working with ng2-dragula. I'm looking to update the orderNumber for every item in a database using the new dropped order.

dragulaService.dropModel.subscribe((value) => {
  var drake = dragulaService.find('bag-orders').drake
  var models = drake.models
  console.log(models)
})

The new model order that it returns does not reflect the order within the bag.

TL;DR: has anyone implemented reordering within a database onDrop with ng2-dragula?

4条回答
太酷不给撩
2楼-- · 2019-03-09 22:50

If you want to be able to drag items (without them disappearing) AND fire the dropModel event:

  • Put the [dragula] and [dragulaModel] directives in the parent element. (For example, contrary to the current doc where it says to put them in the <li>, you have to put them in the <ul>

  • Inject the dragularService, and, in the constructor of your component:

  • Call the dragulaService.setOptions for the bag (you can pass an empty dictionary). If you don't call it, dropModelcallback is not fired

  • Subscribe to dropModel

The result:

<!--thecomponent.html-->
<h2>Playlists</h2>
<ul [dragula]='"first-bag"' [dragulaModel]='playlists'>
  <li *ngFor="let playlist of playlists">

//Inside the component.ts  
playlists: Playlist[];

constructor(private youtubeService: YoutubeService, private dragulaService: DragulaService) {

    dragulaService.setOptions('first-bag', {})
    dragulaService.dropModel.subscribe((value) => {
      this.onDropModel(value);
    });
}

private onDropModel(args) {
    //Here, this.playlists contains the elements reordered
}
查看更多
放我归山
3楼-- · 2019-03-09 22:54

I found Marçal's answer incredibly helpful, and have even expanded on it a little to post an update to my DB to update the sequence value of each item (contacts within an organization, in my case) in the list:

HTML (container is the contact's organization. a contact, in my case, can't be moved between organizations):

<div class="container" [dragula]="'org-' + org.id" [dragulaModel]="org.contacts">
  <nested-contact *ngFor="let contact of org.contacts" [user]="contact" class="contact" [attr.data-id]="contact.id"></nested-contact>
</div>

JS (in my Contact Service, a PUT function to update the stored sequence values associated with each of my contacts, so that their orders persist):

contactsIdPut (id, body) {
  let url = '/api/v3/contacts/' + id + '?access_token=' + localStorage.getItem('access_token');
  let headers = new Headers({ 'Content-Type': 'application/json' });
  let options = new RequestOptions({ headers: headers });

  return this.http.put(url, body, options)
    .map((response: Response) => {
      return response.json();
    });

}

JS (in my organization view component, to outline the actions to be taken upon drag and drop, respectively):

export class nestedOrganizationComponent {
  orgs = [];
  thisOrg: any;
  thisContact: any;

  constructor (private _contactService: ContactService, private dragulaService: DragulaService) {
    this._contactService = _contactService;

    let dragIndex: any;
    let dropIndex: any;
    let elementNode: any;

    dragulaService.drag.subscribe((value) => {
      let id = Number(value[1].dataset.id);
      let orgId: Number = value[0].split('-')[1];
      elementNode = value[2].querySelectorAll('.contact:not(.ignore)').item(dragIndex);

      if (!!id) {
        // this renderedOrgs is just an array to hold the org options that render in this particular view
        this._organizationService.renderedOrgs.push(this.org);
        this.thisOrg = this._organizationService.renderedOrgs.filter(org => { return org.id == orgId; })[0];
        this.thisContact = this.thisOrg.contacts.filter(contact => { return contact.id == id; })[0];

        let arr = this.thisOrg.contacts.map(x => { return x.id; });
        dragIndex = arr.indexOf(id);
      }
    });

    dragulaService.drop.subscribe((value: any[]) => {
      if (elementNode) {
          let id = Number(elementNode.dataset.id);
          if (!!id) {
            let arr = this.thisOrg.contacts.map(x => { return x.id; });
            dropIndex = arr.indexOf(id);
          }
      }

      if (value[2] === value[3]) { // target container === source container
          if (dragIndex >= 0 && dropIndex >= 0 && dragIndex !== dropIndex) {
            this.thisOrg.contacts.forEach((contact, index) => {
              contact.sequence = index;
              this.updateSequence(contact.id, index);
            });
          }
      }
    });
  }

  updateSequence (id: Number, index: Number) {
    let contactBody = {
      avatar: {
        sequence: index,
      }
    };

    return this._contactService.contactsIdPut(id, contactBody)
      .subscribe(
        (data: any) => {
          // nothing is needed, the same view can apply because the contact has already been moved.
        },
        (error: any) => {
          console.error(error);
        }
      );
  }
}

Hopefully this provides a bit more clarity on the matter to someone else in a spot similar to where I found myself today.

查看更多
叼着烟拽天下
4楼-- · 2019-03-09 22:57

I was using the ng2-dragula and It's quite strange on something that I've noticed. The issue I had was the same. Server call is not updating the data object according to the dragged order.

I've just used if clause inside the ngDoCheck lifecycle hook to solve the issue.

It's sorted in the printed object in the console. After digging deeper a bit could find out in the network that the object that is sent with the update server call was the not updated one.

So, it's because the server call is made before the data object is updated.

All I did was adding a global variable which can keep track of drag has updated the data object.

private dropModelUpdated = false;

ngAfterViewInit() {
    // this method only changes a variable to execute a method in ngDoCheck
    this.dragulaService.drop.subscribe((value) => {
      this.dropModelUpdated = true;
    });
}

Then, in the ngDoCheck lifecycle hook,

ngDoCheck() {    
    if (this.dropModelUpdated) { // this excutes if this.dropModelUpdated is true only
        const drake = this.dragulaService.find('any_bag_name').drake;
        const models = drake.models;
        this.modelContent.groups = models[0];
        // ...Here... Make the server or DB call to update the model with the changed/sorted order
        this.dropModelUpdated = false; // make sure this is there for the next smooth execution
    }
}
查看更多
看我几分像从前
5楼-- · 2019-03-09 23:03

I finally found a solution. I have a horizontal list. The first two elements are the ones a I want to ignore and all have the ignore CSS class. And all elements have the item class. So markup:

<ul [dragula]='"foo-list"' class="list">
    <li class="list ignore">a</li>
    <li class="list ignore">b</li>
    <li class="list" *ngFor="let item of getList()" [attr.data-id]="item.id">{{ item.name }}</li>
</ul>

Then the TypeScript:

constructor(private dragulaService: DragulaService) {
    let dragIndex: number, dropIndex: number;

    dragulaService.setOptions('foo-list', {
        moves: (el, source, handle, sibling) => !el.classList.contains('ignore'),
        accepts: (el, target, source, sibling) => sibling === null || !sibling.classList.contains('ignore'),
        direction: 'horizontal'
    });

    dragulaService.drag.subscribe((value) => {
        // HACK
        let id = Number(value[1].dataset.id);
        if (!!id) {
            dragIndex = _.findIndex(this.getList(), {id: id});
        }
    });

    dragulaService.drop.subscribe((value:any[]) => {
        // HACK
        let elementNode = value[2].querySelectorAll('.item:not(.ignore)').item(dragIndex);
        if (elementNode) {
            let id = Number(elementNode.dataset.id);
            if (!!id) {
                dropIndex = _.findIndex(this.getList(), {id: id});
            }
        }

        if (value[2] === value[3]) { // target === source
            if (dragIndex >= 0 && dropIndex >= 0 && dragIndex !== dropIndex) {
                let temp: any = this.list[dragIndex];

                this.list[dragIndex] = this.list[dropIndex];
                this.list[dropIndex] = temp;
            }
        }

        // Custom code here
    });
}

getList(): any[] {
    return this.list;
}
查看更多
登录 后发表回答