【问题标题】:Knockout binding not working淘汰赛绑定不起作用
【发布时间】:2013-08-19 08:41:59
【问题描述】:

我用 MVC4、jQuery 和 Twitter Boostrap 开发了一个网站。现在我决定在一些页面上添加敲除,以使工作流程更加流畅。在这个特定页面上,目的是在左侧有一个项目列表,可以使用 jQuery UI 可排序项目重新排序。单击时,每个项目的详细信息将出现在右侧的区域中以进行编辑。根据类型,编辑会非常不同,所以我决定根据类型设置单独的 DIV 标签。

以前的工作版本通过模型加载页面项目。我的想法是在淘汰视图模型中添加一个可观察数组,然后在页面加载时加载它。该模型是在服务器上定义的,所以我的意图是不再在客户端视图模型上定义所有属性。进一步的操作和编辑将只使用相同的集合并通过 AJAX 调用将新的/更改的/删除的值上传到服务器。

我已经阅读了淘汰演示并在 stackoverflow 中搜索了类似的用例。我想我遵循了这些建议,但显然缺少一些东西。模型加载正常。项目出现并根据类型以不同的颜色显示。它们是可排序的,并且更改会被发送回服务器并保持不变。

缺少的部分是当前选中项的显示和编辑。我在视图模型中添加了另一个名为 currentItem 的属性。在函数 $("body").on("click", ".sortableItem" 中,我可以验证 currentItem 是否已加载并且 Heading 属性是否正确。

但 DIV divEditType1 的可见性和内容不受影响。我误解了绑定的概念吗?我是否误解了从服务器 JSON 数组加载?非常感谢任何帮助。

这是服务器页面模型:

public class PresentationItemsModel
{
    public Guid PresentationId { get; set; }
    public string DisplayName { get; set; }
    public List<PresentationItemModel> PresentationItems { get; set; }
}

这是重复项模型:

public class PresentationItemModel
{
    public Guid PresentationItemId { get; set; }
    public string PresentationItemType { get; set; }
    public int OrderNumber { get; set; }
    public string Heading { get; set; }
    public string Content { get; set; }
    ....
}

这是客户页面(不相关部分已删除):

@model MyCustomWeb.Models.PresentationItemsModel

<header>
    <h2>@Model.DisplayName</h2>
</header>

<div style="float: left;">
    <fieldset>
        <legend>Current list</legend>
        <ul id="sortable" data-bind="foreach: presentationItems">
            <li class="sortableItem btn btn-block" data-bind="css: 'sortable' + PresentationItemType + 'Item'">
                <div style="font-weight: 500;">
                    <span data-bind="text: Heading"></span>
                    <span class="ui-icon ui-icon-arrowthick-2-n-s" style="display: inline-block; color: gray; float: right; opacity: 0.5; height: 14px; margin: 2px 4px 0 0"></span>
                </div>
            </li>
        </ul>
    </fieldset>
</div>

<div id="divEditType1" data-bind="visible: currentItem.PresentationItemType == 'Type1'" style="float: left; margin-left: 50px;">
    <fieldset>
        <legend>Edit Type 1</legend>
        <div class="control-group">
            <input type="text" data-bind="value: currentItem.Heading" placeholder = "Enter Heading" />
        </div>
        <div class="control-group">
            <button id="btnSave" type="submit" class="btn btn-primary">Save</button>
            <button id="btnDelete" type="submit" class="btn btn-danger">Delete</button>
        </div>
    </fieldset>
</div>

<div id="divEditType2" data-bind="visible: currentItem.PresentationItemType = 'Type2'" style="float: left; margin-left: 50px;">
</div>

<div id="divEditType3" data-bind="visible: currentItem.PresentationItemType = 'Type3'" style="float: left; margin-left: 50px;">
</div>


@section Scripts {
    <script>
        $(document).ready(function () {

            // Enable item sorting
            $("#sortable").sortable(
                {
                    cursor: "move",
                    placeholder: "sortableSeparator",
                    update: function () {
                        // Create array of all changed items
                        var changeItems = [];
                        var currentOrderNumber = 1;
                        $(".sortableItem").each(function () {
                            var id = $(this).attr('data-id');
                            var previousOrderNumber = $(this).attr('data-orderNumber');
                            if (currentOrderNumber != previousOrderNumber)
                                changeItems.push({ "id": id, "orderNumber": currentOrderNumber });
                            currentOrderNumber += 1;
                        });
                        // Send ajax action
                        $.ajax({
                            type: "PUT",
                            url: "/api/PresentationApi/ChangeOrder/" + "@Model.PresentationId",
                            contentType: 'application/json; charset=utf-8',
                            data: JSON.stringify(changeItems)
                        });
                    },
                }
            );

            // Enable change depending on item click
            // Note: Must be done this way beacuse of the dynamic binding!
            $("body").on("click", ".sortableItem", function () {
                viewModel.currentItem = ko.dataFor(this);
                alert(viewModel.currentItem.Heading);  // Works fine!
                //alert(JSON.stringify(viewModel.currentItem));  // Also looks good!
            });



            // Knockout view model
            function ViewModel() {
                var self = this;
                self.presentationItems = ko.observableArray([]);
                self.currentItem = ko.observable();
            }

            var viewModel = new ViewModel();
            viewModel.presentationItems(@Html.Raw(Json.Encode(Model.PresentationItems)));
            ko.applyBindings(viewModel);
        });
    </script>
}

编辑: 可以在这里找到创建 Fiddle 演示的尝试: http://jsfiddle.net/XXNz9/

【问题讨论】:

  • 你能发布 @Html.Raw(Json.Encode(Model.PresentationItems)) 的结果 json 吗?你能制作一个重现探针的小提琴吗?
  • 我不习惯摆弄,但我试图在一个例子中重现这个案例。同样的示例在我的 VS2012 上下文中执行时会创建一个列表,但在小提琴中我什至没有得到那个...jsfiddle.net/XXNz9也许你能找到一些明显的错误?
  • jsFiddle 似乎是一个有用的工具,值得学习。但我很困惑。有一个下拉框,我可以在其中选择将 Knockout 或 jQuery 添加到示例中。这是否意味着我不能两者兼得?

标签: jquery asp.net-mvc-4 binding knockout.js


【解决方案1】:

我建议你用另一个 div 包装你的“divEditType1”。内部 div 的上下文将更改为“currentItem”,因此您无需每次都编写它

<div data-bind="with: currentItem">
    <div id="divEditType1" data-bind="visible: PresentationItemType = 'Type1'" style="float: left; margin-left: 50px;">
        <fieldset>
            <legend>Edit Type 1</legend>
            <div class="control-group">
                <input type="text" data-bind="value: Heading" placeholder = "Enter Heading" />
            </div>
            <div class="control-group">
                <button id="btnSave" type="submit" class="btn btn-primary">Save</button>
                <button id="btnDelete" type="submit" class="btn btn-danger">Delete</button>
            </div>
        </fieldset>

    </div>
</div>

这也可以解决您的问题,因为如果 currentItem == null 应用时您的绑定可能会失败

【讨论】:

  • 谢谢。很好的做法,但在我的示例中没有任何区别。
  • @JakobLithner 您正在以错误的方式设置 selectedItem。您需要设置一个可观察值viewModel.currentItem(ko.dataFor(this)); 而不是通常的赋值
  • 谢谢!!!就是这么简单……愚弄我的是我对实际正确的设定值的警报检查。我想我破坏了可观察的变体并通过错误的方式将其设置为静态?您对如何调试淘汰代码有什么建议吗?
  • 是的,您已将另一个对象分配给该属性。如果您分配另一个可观察对象,它也不会起作用。有一个用于敲除调试的 Chrome 插件goo.gl/WXEyyz。但我几乎没有使用它,只是使用控制台。并且还使用打字稿 - 它有助于避免这样简单的错误。
  • 谢谢,我试试 Chrome 淘汰插件!
猜你喜欢
  • 2014-01-10
  • 2015-12-27
  • 1970-01-01
  • 1970-01-01
  • 2012-11-14
  • 2013-03-01
  • 2013-12-27
  • 2012-11-08
  • 2015-11-06
相关资源
最近更新 更多