使用jquery mobile和knockout的动态复选框控制组

时间:2014-01-21 07:40:29

标签: jquery-mobile knockout.js

我正在尝试使用knockout绑定动态创建和过滤包含复选框的jquery移动控制组。基本思想是用户选择一个过滤控制组中复选框列表的选项。我在这里看到过类似的问题,但它们似乎都是一次性绑定,曾经被ko限制并且被jqm增强,它们保持不变。我有这种行为,当基础viewModel更改并且ko更新控件组中的复选框列表时,会出现问题。可以在jsfiddle上找到该行为的完整演示:http://jsfiddle.net/hkrauss2/JAvLk/15/

我可以看到问题是由于jqm在增强控件组时创建了一个包装器div。然后Ko在更新DOM时将新元素放在包装div之上。基本上我问是否有人解决了这个问题,如果有人认为我通过集成这两个库来惹麻烦?提前感谢大家。

这是Html:

<div id="home" data-role="page">
<div data-role="header">
    <h2>Knockout Test</h2>
</div>
<div data-role="content">
    <ul id="parent-view" data-role="listview" data-inset="true" data-bind="foreach: parentCategories">
        <li><a href="#list" data-transition="slide" data-bind="text: description, click: $parent.OnClick"></a></li>
    </ul>
    <p>
        To reproduce the issue select Restaurants, come back and select Nightlife or Bars
    </p>
</div>
</div>
<div id="list" data-role="page">
<div data-role="header">
    <h2>Knockout Test</h2>
    <a data-rel="back" data-icon="carat-l" data-iconpos="notext">Back</a>
</div>
<div data-role="content">
    <form>
        <div id="child-view" data-role="controlgroup" data-bind="foreach: childCategories, jqmRefreshControlGroup: childCategories">
            <input type="checkbox" name="checkbox-v-2a" data-bind="attr: {id: 'categoryId' + id}" />
            <label data-bind="text: description, attr: {for: 'categoryId' + id}" />
        </div>
    </form>
</div>
</div>

和基本的javascript。请注意,此处未列出两个外部js文件。一个在mobileinit事件上设置$.mobile.autoInitializePage = false;。另一个以JSON数组的形式引入数据,该数组用于初始化AppViewModel中的Categories属性。

// Custom binding to handle jqm refresh
ko.bindingHandlers.jqmRefreshControlGroup = {
  update: function (element, valueAccessor) {
    ko.utils.unwrapObservable(valueAccessor());
    try {
        $(element).controlgroup("refresh");
    } catch (ex) { }
  }
}
function GetView(name) {
  return $(name).get(0);
}

// Define the AppViewModel
var AppViewModel = function () {
  var self = this;

  self.currentParentId = ko.observable(0);
  self.Categories = ko.observableArray(Categories); // Categories comes from sampledata.js

  self.parentCategories = ko.computed(function () {
    return ko.utils.arrayFilter(self.Categories(), function (item) {
        return item.parentId == 0;
    });
  });

  self.childCategories = ko.computed(function () {
    return ko.utils.arrayFilter(self.Categories(), function (item) {
        return item.parentId == self.currentParentId();
    });
  });

  self.OnClick = function (viewModel, $event) {
    self.currentParentId(viewModel.id);
    return true;
  };
};

// Create the AppViewModel
var viewModel = new AppViewModel();

// Apply bindings and initialize jqm
$(function () {
  ko.applyBindings(viewModel, GetView('#parent-view'));
  ko.applyBindings(viewModel, GetView('#child-view'));
  $.mobile.initializePage();
});

2 个答案:

答案 0 :(得分:1)

更新

我的旧解决方案将每个元素包装在ui-controlgroup-controls div中,这会添加不必要的标记。但是,增强部分是必不可少的。

$(element).enhanceWithin().controlgroup("refresh"); /* line 16 in fiddle */

新的解决方案更加动态,无需额外的包装器即可维护干净的标记:

  • 第一步:创建 controlgroup controlgroupcreate(事件)后,将data-bind添加到其容器< / em> .controlgroup("container")

  • 第二步:添加复选框,由inputlabel组成。同时,为每个元素添加data-bind

  • 第三步:应用约束ko.applyBindings()

controlgroup 的静态结构应该是基本的,它不应该静态包含任何元素。如果静态添加复选框,则每个动态创建的复选框将包含在其他.ui-checkbox div中。

<div id="child-view" data-role="controlgroup">
   <!-- nothing here -->
</div>

JS

$(document).on("controlgroupcreate", "#child-view", function (e) {
    $(this)
        .controlgroup("container")
        .attr("data-bind", "foreach: childCategories, jqmRefreshControlGroup: childCategories")
        .append($('<input type="checkbox" name="checkbox" />')
        .attr("data-bind", "attr: {id: 'categoryId' + id}"))
        .append($('<label />')
        .attr("data-bind", "text: description, attr: {for: 'categoryId' + id}"));
    ko.applyBindings(viewModel, GetView('#child-view'));
});
  

<强> Demo


旧解决方案

从jQuery Mobile 1.4开始,项目应附加到.controlgroup("container"),而不是直接附加到$("[data-role=controlgroup]")

首先,您需要在div中包含 controlgroup 的内部元素,其中类ui-controlgroup-controls充当控制组容器 < / p>

<div id="child-view" data-role="controlgroup" data-bind="foreach: childCategories, jqmRefreshControlGroup: childCategories">
  <div class="ui-controlgroup-controls">
    <input type="checkbox" name="checkbox-v-2a" data-bind="attr: {id: 'categoryId' + id}" />
    <label data-bind="text: description, attr: {for: 'categoryId' + id}" />
  </div>
</div>

第二步,您需要使用.enhanceWithin() 增强插入控制组容器的元素。

$(element).enhanceWithin().controlgroup("refresh"); /* line 16 in fiddle */
  

<强> Demo

答案 1 :(得分:1)

奥马尔上面的答案非常有效。正如他在评论中提到的那样,它确实将每个输入/标签组合包装在它们自己的div中。这似乎不会影响视觉或功能,但还有另一种方法,如下所述。基本上它使用无容器控制流语法来绑定列表。

新Html

<div id="child-view" data-role="controlgroup">
   <!-- ko foreach: childCategories, jqmRefreshControlGroup: childCategories, forElement: '#child-view' -->
   <input type="checkbox" name="checkbox-v-2a" data-bind="attr: {id: 'categoryId' + id}"></input>
   <label data-bind="text: description, attr: {for: 'categoryId' + id}"></label>
   <!-- /ko -->
</div>

使用无容器语法意味着我们在自定义绑定处理程序中丢失了对 controlgroup div的引用。为了帮助实现这一点,我在名为 forElement 的自定义绑定中将id添加为“#child-view”。魔术仍然发生在自定义绑定处理程序中,而Omar的enhanceWithin建议仍然是秘密成分。注意:我需要更改参数列表以包含由ko传递的所有参数。

ko.bindingHandlers.jqmRefreshControlGroup = {
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
    ko.utils.unwrapObservable(valueAccessor());
    try {
        $(allBindings.get('forElement')).enhanceWithin().controlgroup("refresh");
    } catch (ex) { }
  }
}

最后注意事项:要在虚拟元素上使用自定义处理程序,需要通知它是正常的。以下是更新的启动语句:

// Apply bindings and initialize jqm
$(function () {
  ko.virtualElements.allowedBindings.jqmRefreshControlGroup = true; // This line added
  ko.applyBindings(viewModel, GetView('#parent-view'));
  ko.applyBindings(viewModel, GetView('#child-view'));
  $.mobile.initializePage();
});