2012-11-03 9 views
9

Come si fa a fare questo?Elenco ordinabile di trascinamento della selezione in Meteor

Gli articoli nell'elenco potrebbero corrispondere ai record di una raccolta e la loro posizione nell'elenco potrebbe corrispondere a un campo su ciascun record ("rank" forse) che dovrebbe essere aggiornato quando si verifica l'evento "stop" .

Meteore può giocare bene con jQueryUI Sortable? Cosa succederebbe se più utenti tentassero di trascinare e ordinare la stessa lista in una sola volta? Meteor avrebbe bisogno di un comportamento di smistamento personalizzato?

+1

Penso che potresti implementarlo abbastanza facilmente. Dovresti creare i tuoi dati di lista attraverso i tipici modelli Meteor e quindi usare il callback 'Template.name.rendered()' per applicare '$(). Sortable()'. È quindi possibile configurare i gestori di eventi nella stessa posizione, che potrebbe aggiornare la raccolta quando viene chiamata. – bento

+0

nel caso in cui le persone non leggano tutte le risposte, a partire da 0.9.0 elenco ordinabile con .sortable funziona bene. hanno un esempio sul loro repo qui https://github.com/meteor/meteor/blob/devel/examples/unfinished/reorderable-list – daxiangCODE

risposta

6

MODIFICA: La risposta di seguito non è aggiornata, la risposta di @ cutemachine o http://blog.differential.com/sortable-lists-in-meteor-using-jquery-ui è molto più vicina allo stato dell'arte.


La risposta breve è: questo non è facile se si desidera che sia reattivo. Per creare una versione non reattiva, è sufficiente avvolgere il modello in un tag {{#constant}} e collegare il jquery-ui ordinabile in render come suggerito da @ bento.

Per creare una versione reattiva, il tuo widget ordinabile dovrà avere a che fare con le cose che cambiano sotto di esso (pensa di essere a metà trascinamento quando i dati si ri-ordinano). Ecco alcuni pensieri su come si dovrebbe andare a questo proposito:

  1. Purtroppo, non sta andando essere facile da renderlo animare, che sta per portare a scarsa UX. Lasciamo da parte per ora.

  2. rendering delle voci con qualcosa di simile: {{#each items}} {{> item}} {{/item}}

    Questo si ri-ordine stesso quando i dati scende dal server (senza animazione).

  3. Impostare ogni elemento da trascinare quando esegue il rendering. Potresti o

    i. Usa qualcosa come jquery-ui trascinabile e collegalo in render sul modello item. Potresti avere problemi nel fare ciò poiché l'elemento sottostante potrebbe scomparire durante un trascinamento se l'ordine cambia dall'upstream.

    ii. implementare il proprio codice di trascinamento, magari utilizzando una libreria di livello inferiore.

  4. Quando un oggetto viene trascinato in posizione, riordina immediatamente l'elenco localmente (in questo modo l'utente dovrebbe vedere la cosa giusta, sperando che il server rispetti la modifica .. ma non entriamo nemmeno in quello).

Penso che ci sia un grande bisogno di un tale widget, fatto in modo meteorico. È sul mio radar personale (ma lo sono anche molte cose, incluso un bel modo di riordinare con l'animazione).

+0

Sembra che il wrapping di un {{#each}} con un {{# constante} } non ha alcun effetto e, consentendo a jquery di modificare la dom, la meteora elimina i nodi o entra in un loop infinito. –

+0

Anche se costante ha funzionato, avrei bisogno di un modo per aggiungere/rimuovere elementi nella lista, che suppongo che avrei dovuto implementare manualmente? –

+0

Sì, esattamente. Questo è il motivo per cui non sono un grande fan dell'approccio '{{#constant}}'. Tu puoi farlo con un 'observ' comunque. –

0

Sono riuscito a implementare un trascinamento, elenco ordinabile e modificabile utilizzando jQuery UI sortable e Meteor Collection Hooks e contentEditable rispettivamente. Per un esempio parzialmente funzionante, consulta this demo.

mia implementazione è la seguente (purtroppo non sarà un semplice plug-in e andare esempio, ma spero di ottenere una demo installato e funzionante per questo esempio specifico presto ):

client JS per trascinare, rilasciare e risparmia:

Template.templateName.rendered = -> 
    Deps.autorun -> 
    $('#list').sortable 
     handle: '.handle' 
     stop: (event, ui) -> 
     _.each $(event.target).children('div'), (element, index, list) -> 
      Elements.update { _id: element.getAttribute('data-element-id') }, 
      $set: position: index + 1 

Poche cose da notare qui, sto usando un 'manico' a trascinare l'elemento intorno, come all'interno di ogni div vi sono altri pulsanti e contenuti modificabili. Una volta che l'utente ha trascinato un elemento e lo ha rilasciato, l'evento "stop" si avvia e aggiorno l'elemento ogni nell'elenco con il nuovo posizionamento.

Ho anche la possibilità di aggiungere elementi alla pagina, che saranno posizionati in fondo alla lista. In caso contrario, è probabile che tu possa utilizzare i pacchetti Meteor Collection Behaviours e/o Mongo Counter. Tuttavia, ho utilizzato Meteor Collection Hooks#.before.insert come segue:

Collection prima gancio

@Elements.before.insert (userId, doc) -> 
    highestElement = Elements.findOne({}, 
        sort: { position: -1 } 
        limit: 1 
        ) 
    position = if highestElement? then highestElement.position else 0 
    doc.position = position + 1 

Qui stiamo semplicemente ottenere il documento più alto, l'ordinamento sul attributo position. Se non esiste (ad es. Il primo elemento da creare) inizializziamo le posizioni per iniziare a 1.

PS: se non si capisce CoffeeScript, copypasta il codice a questo incredible tool (Js2coffee).

Edit: si prega di vedere una versione standalone qui: demo (molto lento sui server di Meteor) e il nuovo motore di rendering source code

1

** Aggiornamento per 1,0

Partenza MeteorPad: http://meteorpad.com/pad/TzQTngcy7PivnCCjk/sortable%20demo

Ecco la versione ho implementato - molto simile a @ tomsabin di, ma senza comportamenti Collection necessario. Ha funzionato bene per me con più utenti ed è reattivo.

HTML (Probabilmente non è una buona idea per rendere il div id lo stesso di _id, sono sicuro che troverete una soluzione migliore.)

<template name="myList"> 
    <div class="step_list"> 
    {{#each card}} 
     {{> card_template}}   
    {{/each}} 
    </div> 
</template> 

<template name="card_template"> 
    <div class="card" id="{{_id}}">    
     <h3>{{name}}</h3> 
    </div> 
</template> 

JS

Template.myList.helpers ({ 
    card : function() { 
     return Cards.find({}, {sort: {pos: 1}} 
    )} 
}) 

Template.myList.rendered = function(){ 

    $(".step_list").sortable({ 
     items: ".card", 
     delay: 100, 
     refreshPositions: true, 
     revert: true, 
     helper: "clone", 
     scroll: true, 
     scrollSensitivity: 50, 
     scrollSpeed: 35, 
     start: function(event, ui) { 
     $(ui.helper).addClass("dragging"); 
     }, // end of start 
     stop: function(event, ui) { 
     $(ui.item).removeClass("dragging"); 
     }, // end of stop 
     update: function(event, ui) { 
     var index = 0;     
     _.each($(".card"), function(item) { 
      Cards.update({_id: item.id}, { 
      $set:{ 
       pos: index++, 
      } 
      }); 
     }); 
     } 
    }).disableSelection(); 

} 
+0

Puoi confermare che questo funziona dopo [modifica API] (http://differential.com/blog/sortable-lists-in-meteor-using-jquery-ui)? Forse fare un Meteorpad per questo? –

+0

Funziona ancora, in pratica. Non hai più rimosso l'aiutante. Ecco un Meteorpad: http://meteorpad.com/pad/TzQTngcy7PivnCCjk/sortable%20demo Anche se mi piace Blaze.getData si avvicina meglio. Non c'è bisogno di piantare il _id di ogni record nel dom. –