web-dev-qa-db-fra.com

Angular 2 Dragula Model mise à jour incorrectement

Travailler avec ng2-dragula. Je cherche à mettre à jour le numéro de commande pour chaque article d'une base de données à l'aide de la nouvelle commande supprimée.

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

Le nouveau modèle de commande qu'il retourne ne reflète pas la commande dans le sac.

TL; DR: quelqu'un a-t-il implémenté une réorganisation dans une base de données onDrop avec ng2-dragula?

15
Dan Feinstein

Si vous voulez pouvoir faire glisser des éléments (sans les faire disparaître) ET déclencher l'événement dropModel:

  • Mettez les directives [dragula] et [dragulaModel] dans élément parent. (Par exemple, contrairement au document actuel où il est dit de les mettre dans le <li>, vous devez les mettre dans le <ul>

  • Injectez le dragularService et, dans le constructeur de votre composant:

  • Appelez le dragulaService.setOptions pour le sac (vous pouvez passer un dictionnaire vide). Si vous ne l'appelez pas, dropModelcallback n'est pas déclenché

  • Abonnez-vous à dropModel

Le résultat:

<!--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
}
9
darksider

J'utilisais le ng2-dragula et c'est assez étrange sur quelque chose que j'ai remarqué. Le problème que j'avais était le même. L'appel du serveur ne met pas à jour l'objet de données selon l'ordre glissé.

Je viens d'utiliser la clause if à l'intérieur du hook de cycle de vie ngDoCheck pour résoudre le problème.

Il est trié dans l'objet imprimé dans la console. Après avoir creusé plus profondément, un peu pourrait découvrir dans le réseau que l'objet envoyé avec l'appel du serveur de mise à jour n'était pas celui mis à jour.

Donc, c'est parce que l'appel au serveur est effectué avant la mise à jour de l'objet de données.

Tout ce que j'ai fait était d'ajouter une variable globale qui peut garder une trace de la traînée a mis à jour l'objet de données.

private dropModelUpdated = false;

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

Ensuite, dans le hook de cycle de vie ngDoCheck,

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
    }
}
7
Saiyaff Farouk

J'ai finalement trouvé une solution. J'ai une liste horizontale. Les deux premiers éléments sont ceux que je veux ignorer et ont tous la classe CSS ignore. Et tous les éléments ont la classe item. Alors balisage:

<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>

Ensuite, le 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;
}
4
Marçal Juan

J'ai trouvé la réponse de Marçal incroyablement utile, et je l'ai même développée un peu pour publier une mise à jour dans ma base de données pour mettre à jour la valeur de séquence de chaque élément (contacts au sein d'une organisation, dans mon cas) dans la liste:

HTML (le conteneur est l'organisation du contact. Un contact, dans mon cas, ne peut pas être déplacé entre les organisations):

<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 (dans mon Service Contact, une fonction PUT pour mettre à jour les valeurs de séquence stockées associées à chacun de mes contacts, afin que leurs commandes persistent):

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 (dans le composant de vue de mon organisation, pour décrire les actions à entreprendre lors du glisser-déposer, respectivement):

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);
        }
      );
  }
}

J'espère que cela apporte un peu plus de clarté sur la question à quelqu'un d'autre dans un endroit similaire à celui où je me suis retrouvé aujourd'hui.

2
Kate Sowles