knockout.js и jQueryUI для создания аккордеонного меню - PullRequest
11 голосов
/ 27 января 2012

У меня возникла небольшая проблема, связанная с попыткой использования jquery UI и нокаута js для совместной работы.В основном я хочу создать аккордеон с элементами, добавляемыми из нокаута через foreach (или шаблон).

Основной код выглядит следующим образом:

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

Ничего впечатляющего здесь нет ...проблема в том, что если я сделаю что-то вроде:

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

Аккордеон будет создан, но внутренний div будет селектором заголовка (первый дочерний элемент, по умолчанию), так что эффект не желаемый.

Исправление вещи с этим:

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

Кажется, работает лучше, но на самом деле создает 2 аккордеона, а не один с 2 секциями ... странно.

Я пытался исследовать нокаутШаблоны и использование "afterRender" для повторной аккордеонизации div, но безрезультатно ... кажется, что в качестве аккордеона повторно воспроизводится только первая ссылка, а не вторая.Вероятно, это все-таки связано с тем, что мой начальный пользователь знает интерфейс jquery.

У вас есть идеи, как заставить все работать вместе?

Ответы [ 6 ]

13 голосов
/ 28 января 2012

Я бы использовал пользовательские привязки для такой функциональности.

Так же, как RP Niemeyer с примером привязки jQuery Accordion к knockoutjs http://jsfiddle.net/rniemeyer/MfegM/

6 голосов
/ 30 января 2013

Я пытался интегрировать нокаут и аккордеон JQuery UI, а затем складной аккордеон Bootstrap. В обоих случаях это сработало, но я обнаружил, что мне пришлось реализовать несколько обходных путей, чтобы все отображалось правильно, особенно при динамическом добавлении элементов с помощью нокаута. Упомянутые виджеты не всегда осведомлены о том, что происходит с нокаутом, и что-то может испортиться (неверно рассчитаны высоты div и т. Д.). Особенно с аккордеоном JQuery он имеет тенденцию переписывать html по своему усмотрению, что может быть настоящей болью.

Итак, я решил создать свой собственный виджет аккордеона, используя основные JQuery и Knockout. Взгляните на этот рабочий пример: http://jsfiddle.net/matt_friedman/KXgPN/

Конечно, используя разную разметку и CSS, это можно настроить на все, что вам нужно.

Приятно то, что он полностью управляется данными и не делает никаких предположений о компоновке, вне зависимости от того, какой CSS вы решите использовать. Вы заметите, что разметка очень проста. Это всего лишь пример. Это должно быть настроено.

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());
1 голос
/ 23 июля 2014

Я попытался принять решение, и оно сработало. Просто пришлось внести небольшое изменение, так как я получаю следующую ошибку

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

просто нужно было добавить следующее, и это сработало

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

подробности см. В Перерыв в связке с аккордеоном

1 голос
/ 12 октября 2012

Есть ли причина, по которой вы не можете применить виджет аккордеона к внутреннему div здесь? Например:

<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>
0 голосов
/ 18 июля 2014

Что я сделал, так как мои данные загружались из AJAX, и я показывал счетчик «Загрузка», я прикрепил аккордеон к ajaxStop следующим образом:

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

Работал отлично.

0 голосов
/ 25 сентября 2013

Вы можете попробовать это в качестве шаблона, похожего на это:

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

"Tasks ()" представляет собой массив ko.observableArray, заполненный задачами с атрибутами «TaskId», «TaskName», «Description», «SelectedTask» объявлены как ko.observable ();

myAccordion - это

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