Ответ 1
Я бы пошел с пользовательскими привязками для такой функциональности.
Как и RP Niemeyer с примером привязки jQuery Accordion к knockoutjs http://jsfiddle.net/rniemeyer/MfegM/
Возникла небольшая проблема, пытаясь объединить jQuery UI и knockout 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 аккордеона, а не один с двумя разделами... странно.
Я попытался изучить шаблоны нокаутов и использовать "afterRender" для повторного аккордеона div, но безрезультатно... он, похоже, повторно отображает только первую ссылку как аккордеон, а не вторую. Вероятно, это связано с моим новичком в jQuery UI в любом случае.
Есть ли у вас идея, как все работать вместе?
Я бы пошел с пользовательскими привязками для такой функциональности.
Как и RP Niemeyer с примером привязки jQuery Accordion к knockoutjs http://jsfiddle.net/rniemeyer/MfegM/
Я попытался интегрировать нокаут и аккордеон UQuery UI, а позже - разборный аккордеон Bootstrap. В обоих случаях это сработало, но я обнаружил, что мне пришлось реализовать несколько обходных решений, чтобы все отображалось правильно, особенно при динамическом добавлении элементов через нокаут. Описанные виджеты не всегда знают о том, что происходит с нокаутом, и все может быть испорчено (недопустимые расчеты высот div и т.д.). Особенно с аккордеоне JQuery он имеет тенденцию переписывать html по своему усмотрению, что может быть настоящей болью.
Итак, я решил создать свой собственный аккордеонный виджет, используя основные JQuery и Knockout. Взгляните на этот рабочий пример: http://jsfiddle.net/matt_friedman/KXgPN/
Конечно, используя разную разметку и css, это может быть настроено на все, что вам нужно.
Хорошо, что он полностью управляется данными и не делает никаких предположений о макете вне того, что вы решили использовать. Вы заметите, что разметка просты. Это просто пример. Это должно быть настроено.
Разметка:
<div data-bind="foreach:groups" id="menu">
<div class="header" data-bind="text:name, accordion: openState, click: toggle"> </div>
<div class="items" data-bind="foreach:items">
<div data-bind="text:name"> </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());
Есть ли причина, по которой вы не можете применить виджет аккордеона к внутреннему 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>
Я попытался принять принятое решение, и оно сработало. Просто пришлось немного изменить, так как я получал следующую ошибку
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);
}
Подробнее см. Нарушения привязки к нокауту.
Вы можете попробовать это, чтобы создать шаблон, похожий на это:
<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>
"Задачи()" - это 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);
});
...
}
Что я сделал, так как мои данные загружались из AJAX, и я показывал счетчик "Загрузка", я привязал аккордеон к ajaxStop так:
$(document).ajaxStart(function(){$("#cargando").dialog("open");}).ajaxStop(function(){$("#cargando").dialog("close");$("#acordion").accordion({heightStyle: "content"});});
Работал отлично.