2012-01-27 5 views
11

Si è verificato un leggero problema nel tentativo di ottenere un'interfaccia utente jquery e un knockout js in cohoperate. Fondamentalmente voglio creare una fisarmonica con oggetti che vengono aggiunti dal knockout attraverso un foreach (o modello).knockout.js e jQueryUI per creare un menu di fisarmonica

Il codice di base è la seguente:

<div id="accordion"> 
    <div data-bind="foreach: items"> 
     <h3><a href="#" data-bind="text: text"></a></h3> 
     <div><a class="linkField" href="#" data-bind="text: link"></a></div> 
    </div> 
</div> 

Niente impressionante qui ... Il problema è che se faccio qualcosa di simile:

$('#accordion').accordion(); 

verrà creato La fisarmonica ma il div interno sarà il selettore dell'intestazione (primo figlio, come predefinito) in modo che l'effetto non sia quello desiderato.

roba Fissaggio con questo:

$('#accordion').accordion({ header: 'h3' }); 

sembra funzionare meglio, ma in realtà crea 2 fisarmoniche e non uno con 2 sezioni ... strano.

Ho provato a esplorare i modelli ad eliminazione diretta e utilizzando "afterRender" per riequilibrare il div, ma senza successo ... sembra di ri-rendere solo il primo collegamento come fisarmonica e non il secondo. Probabilmente questo è dovuto alla mia conoscenza da principiante di jquery UI comunque.

Avete qualche idea su come far funzionare tutto insieme?

risposta

13

Vorrei andare con associazioni personalizzate per tale funzionalità.

Proprio come RP Niemeyer con un esempio di jQuery Accordion legame knockoutjs http://jsfiddle.net/rniemeyer/MfegM/

+3

Yup - che è un po 'accordioning serio. – PhillipKregg

+0

Questo è interessante e risponde a quello che stavo chiedendo anche se sembra un po 'hacky:/ Probabilmente ho bisogno di esaminare meglio i collegamenti personalizzati un po' meglio. Grazie per il collegamento comunque! – Tallmaris

+0

Secondo me le associazioni personalizzate sono essenziali per la comprensione di knockout e l'uso di – AlfeG

1

C'è qualche motivo per cui non è possibile applicare il widget di fisarmonica per i div interno qui? Per esempio:

<div id="accordion" data-bind="foreach: items"> 
    <h3><a href="#" data-bind="text: text"></a></h3> 
    <div><a class="linkField" href="#" data-bind="text: link"></a></div> 
</div> 
6

avevo cercato di integrare ad eliminazione diretta e la fisarmonica JQuery UI e poi la fisarmonica pieghevole Bootstrap. In entrambi i casi ha funzionato, ma ho scoperto che dovevo implementare alcune soluzioni alternative per far sì che tutto fosse visualizzato correttamente, specialmente quando si aggiungevano elementi dinamicamente tramite knockout. I widget menzionati non sono sempre a conoscenza di ciò che accade per quanto riguarda l'eliminazione diretta e le cose possono essere incasinate (altezze div errate calcolate ecc ...). Soprattutto con la fisarmonica JQuery tende a riscrivere l'html come meglio crede, il che può essere un vero dolore.

Così, ho deciso di creare il mio widget di fisarmonica utilizzando core JQuery e Knockout. Date un'occhiata a questo esempio: http://jsfiddle.net/matt_friedman/KXgPN/

Naturalmente, l'utilizzo di markup e css diversi può essere personalizzato in base alle esigenze.

La cosa bella è che è interamente guidato dai dati e non fa alcuna ipotesi sul layout oltre a qualsiasi css tu decida di utilizzare. Noterai che il markup è semplicemente semplice. Questo è solo un esempio. È pensato per essere personalizzato.

Markup:

<div data-bind="foreach:groups" id="menu"> 
    <div class="header" data-bind="text:name, accordion: openState, click: toggle">&nbsp;</div> 
    <div class="items" data-bind="foreach:items"> 
     <div data-bind="text:name">&nbsp;</div> 
    </div> 
</div> 

Javascript:

ko.bindingHandlers.accordion = { 

    init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { 
     $(element).next().hide(); 
    }, 
    update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { 

     var slideUpTime = 300; 
     var slideDownTime = 400; 

     var openState = ko.utils.unwrapObservable(valueAccessor()); 
     var focussed = openState.focussed; 
     var shouldOpen = openState.shouldOpen; 

     /* 
     * This following says that if this group is the one that has 
     * been clicked upon (gains focus) find the other groups and 
     * set them to unfocussed and close them. 
     */ 
     if (focussed) { 

      var clickedGroup = viewModel; 

      $.each(bindingContext.$root.groups(), function (idx, group) { 
       if (clickedGroup != group) { 
        group.openState({focussed: false, shouldOpen: false}); 
       } 
      }); 
     } 

     var dropDown = $(element).next(); 

     if (focussed && shouldOpen) { 
      dropDown.slideDown(slideDownTime); 
     } else if (focussed && !shouldOpen) { 
      dropDown.slideUp(slideUpTime); 
     } else if (!focussed && !shouldOpen) { 
      dropDown.slideUp(slideUpTime); 
     } 
    } 
}; 

function ViewModel() { 

    var self = this; 
    self.groups = ko.observableArray([]); 

    function Group(id, name) { 

     var self = this; 
     self.id = id; 
     self.name = name; 

     self.openState = ko.observable({focussed: false, shouldOpen: false}); 

     self.items = ko.observableArray([]); 

     self.toggle = function (group, event) { 
      var shouldOpen = group.openState().shouldOpen; 
      self.openState({focussed: true, shouldOpen: !shouldOpen}); 
     } 
    } 

    function Item(id, name) { 
     var self = this; 
     self.id = id; 
     self.name = name; 
    } 

    var g1 = new Group(1, "Group 1"); 
    var g2 = new Group(2, "Group 2"); 
    var g3 = new Group(3, "Group 3"); 

    g1.items.push(new Item(1, "Item 1")); 
    g1.items.push(new Item(2, "Item 2")); 

    g2.items.push(new Item(3, "Item 3")); 
    g2.items.push(new Item(4, "Item 4")); 
    g2.items.push(new Item(5, "Item 5")); 

    g3.items.push(new Item(6, "Item 6")); 

    self.groups.push(g1); 
    self.groups.push(g2); 
    self.groups.push(g3); 
} 

ko.applyBindings(new ViewModel()); 
+0

Davvero bello. Grazie per questo. L'ho leggermente modificato per fornire una navigazione di tastiera utile per i miei utenti. (Almeno una navigazione di tastiera più utile della maggior parte delle implementazioni di menu basate su jQuery fornisce!) –

+0

Grazie per questo. Ho finito per usarlo pure. Sarebbe fantastico se ci fosse un modo per renderlo un po 'più generico. Forse ci giocherò più tardi. – Quickhorn

0

Si potrebbe provare questo al modello che, simile a questo:

<div id="accordion" data-bind="myAccordion: { },template: { name: 'task-template', foreach: ¨Tasks, afterAdd: function(elem){$(elem).trigger('valueChanged');} }"></div> 

<script type="text/html" id="task-template"> 
    <div data-bind="attr: {'id': 'Task' + TaskId}, click: $root.SelectedTask" class="group"> 
     <h3><b><span data-bind="text: TaskId"></span>: <input name="TaskName" data-bind="value: TaskName"/></b></h3> 
     <p> 
      <label for="Description" >Description:</label><textarea name="Description" data-bind="value: Description"></textarea> 
      </p> 
    </div> 
</script> 

"Attività()" è un ko. ObservableArray con popolato di task, con attributi "TaskId", "TaskName", "Descrizione", "SelectedTask" decla rosso come ko.observable();

"myAccordion" è una

ko.bindingHandlers.myAccordion = { 
    init: function (element, valueAccessor) { 
     var options = valueAccessor(); 
     $(element).accordion(options); 
     $(element).bind("valueChanged", function() { 
      ko.bindingHandlers.myAccordion.update(element, valueAccessor); 
     }); 
     ... 
} 
0

Quello che ho fatto è stato, dal momento che i miei i dati vengono caricati da AJAX e mi stava mostrando un "Loading" filatrice, ho attaccato la fisarmonica a ajaxStop in questo modo:

$(document).ajaxStart(function(){$("#cargando").dialog("open");}).ajaxStop(function(){$("#cargando").dialog("close");$("#acordion").accordion({heightStyle: "content"});}); 

Ha funzionato perfettamente.

1

Ho tentato la soluzione accettata e ha funzionato. Abbiamo solo dovuto fare un piccolo cambiamento in quanto mi è stato sempre seguente errore

Uncaught Error: cannot call methods on accordion prior to initialization; attempted to call method 'destroy' 

solo dovuto aggiungere in seguito e ha funzionato

if(typeof $(element).data("ui-accordion") != "undefined"){ 
$(element).accordion("destroy").accordion(options); 
} 

per i dettagli si veda Knockout accordion bindings break