【问题标题】:knockout.js and jQueryUI to create an accordion menuknockout.js 和 jQueryUI 创建手风琴菜单
【发布时间】:2012-02-20 14:55:57
【问题描述】:

尝试让 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 UI 的初学者了解。

你知道如何让所有东西协同工作吗?

【问题讨论】:

    标签: jquery-ui knockout.js jquery-ui-accordion


    【解决方案1】:

    我会为此类功能使用自定义绑定。

    就像RP Niemeyer 与jQuery Accordion 绑定到knockoutjs http://jsfiddle.net/rniemeyer/MfegM/ 的示例一样

    【讨论】:

    • 是的 - 这是一些严肃的手风琴。
    • 这很有趣,它回答了我的问题,尽管它看起来有点笨拙:/ 我可能需要更好地研究自定义绑定才能完全理解。无论如何感谢您的链接!
    • 在我看来,自定义绑定对于理解和使用淘汰赛至关重要
    • 使用淘汰赛模板更新了 AlfeG jsfiddle 和淘汰赛 2.1 jsfiddle.net/coffeedannylai/neYHw/9
    • 当我将 foreach 绑定和手风琴绑定放在同一个元素上说 Multiple bindings (foreach and accordion) are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element. 时,我收到了来自 knockout.js 的错误
    【解决方案2】:

    这里有什么原因不能将手风琴小部件应用到内部 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>
    

    【讨论】:

      【解决方案3】:

      我曾尝试将淘汰赛和 JQuery UI 手风琴以及后来的 Bootstrap 可折叠手风琴集成。在这两种情况下它都有效,但我发现我必须实施一些变通方法才能正确显示所有内容,尤其是在通过剔除动态添加元素时。提到的小部件并不总是知道在淘汰赛方面发生了什么,事情可能会变得一团糟(div 高度计算错误等......)。尤其是使用 JQuery 手风琴时,它往往会根据需要重写 html,这可能会很痛苦。

      因此,我决定使用核心 JQuery 和 Knockout 制作自己的手风琴小部件。看看这个工作示例:http://jsfiddle.net/matt_friedman/KXgPN/

      当然,使用不同的标记和 css 这可以根据您的需要进行定制。

      好消息是它完全是数据驱动的,除了您决定使用的任何 css 之外,不会对布局做出任何假设。您会注意到标记非常简单。这只是一个例子。它是用来定制的。

      标记:

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

      【讨论】:

      • 真的很好。谢谢你。我稍作修改,为我的用户提供有用的键盘导航。 (至少比大多数基于 jQuery 的菜单实现提供的键盘导航更有用!)
      • 谢谢你。我最终也使用了它。如果有办法让它更通用一点,那就太棒了。也许我稍后会玩弄它。
      【解决方案4】:

      你可以试试这个模板,类似于:

      <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,其中填充了 task-s,具有属性 "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);
             });
            ...
      }
      

      【讨论】:

        【解决方案5】:

        我所做的是,由于我的数据是从 AJAX 加载的,并且我正在显示“正在加载”微调器,因此我将手风琴附加到 ajaxStop,如下所示:

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

        完美运行。

        【讨论】:

          【解决方案6】:

          我尝试了接受的解决方案,它奏效了。由于出现以下错误,因此只需要进行一些更改

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

          详情请见Knockout accordion bindings break

          【讨论】:

            猜你喜欢
            • 2018-09-22
            • 1970-01-01
            • 2012-11-20
            • 1970-01-01
            • 1970-01-01
            • 2019-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-05-01
            相关资源
            最近更新 更多