0

我第一次使用 knockoutjs 是为了获得一个任务列表,然后能够单击一个以在弹出表单中显示它。我遇到的问题是 data-bind="click: $parent.edit",它调用 ToDoListViewModel/edit。

当我第一次开始编写列表代码时,它会将当前行的 todo 对象作为第一个参数传递给编辑函数。在为我的添加/编辑弹出表单添加第二个视图模型后,它不再以这种方式运行。调用 $parent.edit 仍然调用编辑函数,但是现在它传入一个空对象 { }。

似乎我遇到了某种冲突的绑定问题,有什么想法吗?

这是待办事项列表html:

<table class="table table-striped table-hover" data-bind="visible: isVisible">
                <thead>
                    <tr>
                        <th>To-Do</th>
                        <th>Estimate</th>
                        <th>Deadline</th>
                        @if (Model == null) { <th>Property</th> }
                        <th>Tenant</th>
                        <th style="display: none;">Assigned To</th>
                        <th></th>
                    </tr>
                </thead>
                <tbody data-bind="foreach: todos">
                    <tr data-bind="visible: isVisible()">
                        <td><a class="pointer" title="Edit To-Do" data-bind="click: $parent.edit, text: Task"></a></td>
                        <td data-bind="text: FormattedAmount"></td>
                        <td data-bind="text: FormattedDueDate"></td>
                        <td data-bind="visible: $parent.showPropertyColumn, text: PropertyName"></td>
                        <td data-bind="text: TenantName"></td>
                        <td>
                            <div class="btn-group pull-right">
                                <a class="btn btn-primary pointer default-click-action" data-bind="click: $parent.markComplete">Mark Complete</a>
                                <button class="btn btn-primary dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></button>
                                <ul class="dropdown-menu">
                                    <li><a class="pointer" data-bind="click: $parent.edit">Edit To-Do</a></li>
                                    <li><a class="pointer" data-bind="click: $parent.remove">Delete To-Do</a></li>
                                </ul>
                            </div>
                        </td>
                    </tr>
                </tbody>
            </table>

这是弹出的html:

@model Housters.Schemas.Models.Property.Listing

<div id="popup" class="modal fade" style="display: none;" data-bind="with: form">
    @using (Html.BeginForm("SaveTodo", "Properties", FormMethod.Post, new { Id = "form", @class = "form-horizontal" }))
    {
    <div class="modal-header">
        <a class="close" data-dismiss="modal">×</a>
        <h3>To-Do Information</h3>
    </div>
    <div class="modal-body">

        <input id="id" name="id" type="hidden" data-bind="value: todo().Id" />
        <input id="originalListingId" type="hidden" value="@(Model != null ? Model.IdString : "")" />

        <div class="control-group @(Model != null ? " hide" : "")">
            <label class="control-label" for="listingId">Property</label>
            <div class="controls">
                @Html.DropDownList("listingId", ViewBag.Listings as SelectList, "None",
                    new Dictionary<string, Object> { { "data-bind", "value: todo().ListingId" } })
            </div>
        </div>

        <div class="control-group">
            <label class="control-label" for="tenantId">Tenant</label>
            <div class="controls">
               <select id="tenantId" name="tenantId" class="span11" data-bind="value: todo().TenantId">
                    <option value="">None</option>
                </select>
                <p class="help-block">Is this task related to a certain tenant?</p>
            </div>
        </div>

        <div class="control-group">
            <label class="control-label" for="amount">Estimate to Complete</label>
            <div class="controls">
                <div class="input-prepend"><span class="add-on">$</span><input type="text" id="amount" name="todo.Amount" maxlength="10" 
                    class="span11 number" data-bind="value: todo().Amount" /></div>
            </div>
        </div>

        <div class="control-group">
            <label class="control-label" for="dueDate">Due Date</label>
            <div class="controls">
                <input type="text" id="dueDate" name="todo.DueDate" maxlength="10" 
                    class="span11 date" data-bind="value: todo().DueDate" />
            </div>
        </div>

        <div class="control-group">
            <label class="control-label" for="task">Task<span class="required-asterisk">*</span></label>
            <div class="controls">
                <textarea id="task" name="todo.Task" rows="4" class="span11 required" data-bind="value: todo().Task"></textarea>
            </div>
        </div>

    </div>
    <div class="modal-footer">
        <a class="btn pointer" data-dismiss="modal">Close</a>
        <a id="mark-complete-popup" class="btn btn-primary" data-dismiss="modal" data-bind="visible: (todo().Id && !todo().IsComplete), click: markComplete">Mark Complete</a>
        <button type="button" class="btn btn-primary" data-bind="click: save">Save</button>
    </div>
    }
</div>

最后,这里是定义视图模型的 javascript:

function ToDoList () {

    if(!(this instanceof arguments.callee)) 
        return new arguments.callee();

    var parent = this;
    this.showCompleted = ko.observable(false);
    this.tenantFilter = new PropertyTenantFilter();
    this.viewModel = {
        list: new ToDoListViewModel(),
        form: new ToDoFormViewModel()
    };

    this.init = function () {
        //get all tenants.
        utils.block($("#grid-content"), "Loading");
        this.tenantFilter.init(function () {
            //initialize view model.
            ko.applyBindings(this.viewModel);
            //setup controls & events.
            $("#dueDate").datepicker();
            $("#listingId").change(this.tenantFilter.getByListing.bind(this.tenantFilter)).change();
        } .bind(this));
    };

    function ToDoListViewModel() {
        //init.
        var self = this;
        self.todos = ko.observableArray([]);

        //computed.
        self.showPropertyColumn = ko.computed(function () {
            return $("#originalListingId").val().length == 0;
        });
        self.isVisible = ko.computed(function () {
            return _.find(self.todos(), function (todo) { return todo.isVisible(); }) != null;
        });

        //operations.
        self.add = function () {
            //set form field values.
            parent.viewModel.form.fill(new schemas.ToDo({}, parent));
            //show popup.
            $("#popup").modal("show");
        };
        self.edit = function (todo) {
            console.debug("edit: " + JSON.stringify(todo));
            //set form field values.
            parent.viewModel.form.fill(todo);
            //update tenants dropdown for selected listing.
            parent.tenantFilter.getByListing();
            //show popup.
            $("#popup").modal("show");
        };
        self.markComplete = function (todo) {
            parent.markComplete(todo);
        };
        self.remove = function (todo) {
            var result = confirm("Are you sure that you want to delete this To-Do?");
            if (result) {
                //save changes.
                utils.ajax(basePath + "properties/deletetodo",
                    { id: todo.Id },
                    function (success) {
                        //refresh results.
                        self.todos.remove(todo);
                        //show result.
                        utils.showSuccess('The To-Do has been deleted successfully');
                    }
                );
            }
        };
        self.toggleShowCompleted = function () {
            parent.showCompleted(!parent.showCompleted());
            $("#showCompletedTodos").text(parent.showCompleted() ? "Show Active" : "Show Completed");
        };
        self.update = function (todo) {
            var existingToDo = _.find(self.todos(), function (item) { return item.Id() == todo.Id(); });
            existingToDo = todo;
        };

        //load todos from server.
        utils.ajax(basePath + "properties/gettodos",
            { id: $("#originalListingId").val(), showCompleted: parent.showCompleted() },
            function (results) {
                var mappedTodos = $.map(results, function (item) { return new schemas.ToDo(item, parent); });
                self.todos(mappedTodos);
                $("#grid-content").unblock();
            }
        );
    }

    function ToDoFormViewModel() {
        //init.
        var self = this;
        self.todo = ko.observable({});
        utils.setupPopupForm(self.saved);

        //operations.
        self.fill = function (todo, isEdit) {
            //set form field values.
            self.todo = todo;
            if (todo.Id()) {
                //update tenants dropdown for selected listing.
                parent.tenantFilter.getByListing();
            }
            //show popup.
            $("#popup").modal("show");
        };
        self.save = function (todo) {
            self.todo = todo;
            $("#form").submit();
        };
        self.saved = function (result) {
            var todo = new schemas.ToDo(result.todo, parent);
            if (result.isInsert)
                parent.viewModel.list.todos().push(todo);
            else
                parent.viewModel.list.update(todo);
            utils.showSuccess("Your To-Do has been saved successfully.");
        };
        self.markComplete = function (todo) {
            parent.markComplete(todo);
        };
    }

    this.markComplete = function (todo) {
        var result = confirm("Are you sure that you want to mark this To-Do complete?");
        if (result) {
            //save changes.
            utils.ajax(basePath + "properties/marktodocomplete", {
                    id: todo.Id()
                },
                function () {
                    todo.IsComplete(true);
                    //show success.
                    utils.showSuccess('Your To-Do has been marked completed');
                } .bind(this)
            );
        }
    }

    this.init();

}
4

1 回答 1

0

一种可能的解决方案是替换:

data-bind="click: $parent.edit"

data-bind="click:$root.viewModel.list.edit"
于 2013-05-01T22:33:24.927 回答