构建多视图模型(异步加载)

时间:2014-04-08 21:34:18

标签: knockout.js signalr knockout-mapping-plugin

好的,我已经充分希望在Google和SO上搜索。我已经被困在这3天了,无法弄清楚自己。

我有一个C#Hub,它返回Enumerable<RadicalTask>

我想创建一个内部包含其他模型的视图模型。我有这个:

ViewModel = {
    Tasks: new TaskListModel(null),
    User: new UserModel(null),
};

我做这样的绑定:

$.connection.hub.start().done(function () {

    $.connection.taskHub.server.getTaskList().done(function (res) {
        ViewModel.Tasks = ko.mapping.fromJS(res, TasksMappings);
        $.connection.taskHub.server.getCurrentUser().done(function (ress) {
            ViewModel.User = ko.mapping.fromJS(ress, UserMappings);
            ko.applyBindings(ViewModel);
        }).fail(function (error) {
            console.error(error);
        });
    }).fail(function (error) {
        console.error(error);
    });
});

用户只是一个具有属性的类,如果需要也可以激进的任务我可以给你我的模型。

然后我想在模型上有其他功能,所以我这样做了:

var UserModel = function (data) {
    ko.mapping.fromJS(data, {}, this);

    self = this;
    self.CanEdit = function () {
        if (self.UserPermissions() == null) return false;
        return self.UserPermissions().CanEdit();
    };
    self.CanReply = function () {
        if (self.UserPermissions() == null) return false;
        return self.UserPermissions().CanReply();
    };
    self.CanWork = function () {
        if (self.UserPermissions() == null) return false;
        return self.UserPermissions().CanWork();
    };
    self.CanStopWorking = function (taskID) {
        if (self.User() == null) return false;
        if (self.CurrentTask() == null) return false;
        return self.CurrentTask().ID() == taskID;
    };
    self.CanStartWorking = function () {
        if (self.User() == null) return false;
        if (self.User().CurrentTask() != null) return false;
        return true;
    };
    self.StartTask = function (task) {
        self.hub.server.startTask(task.ID()).done(function (res) {
            if (res) $('.bottom-right').notify({
                message: {
                    text: 'You are working on task with id : ' + task.ID()
                },
                fadeOut: {
                    enabled: true,
                    delay: 2000
                },
                type: 'success'
            }).show();
            else $('.bottom-right').notify({
                message: {
                    text: 'Failed to start working on task with id : ' + task.ID()
                },
                fadeOut: {
                    enabled: true,
                    delay: 2000
                },
                type: 'danger'
            }).show();
        });
    };
    self.StopTask = function (task) {
        self.hub.server.stopTask(task.ID()).done(function (res) {

            if (!res) {
                $('.bottom-right').notify({
                    message: {
                        text: 'Failed to stop work on task with id :' + task.ID()
                    },
                    fadeOut: {
                        enabled: true,
                        delay: 2000
                    },
                    type: 'danger'

                }).show();
            } else {
                $("#quickReply").modal().show();
            }
        });
        $.connection.taskHub.client.UserUpdated = (function (res) {
            ko.mapping.fromJS(res, {}, self);

        });
    };
};


/* Knockout ViewModel */
var TaskListModel = function (data) {
    self = this;
    if (data != null) {
        ko.mapping.fromJS(data, {}, this);
        alert(JSON.stringify(data));
    }
    self.selectedTask = ko.observable(null);
    self.selectedID = ko.observable(-1);
    self.selectTask = function (p) {
        if (self.selectedID() == p.ID()) self.selectedID(-1); //Hide on click
        else self.selectedID(p.ID());
        self.selectedTask(p);
    }; //Create the select method
    self.Visible = function (p) {
        return (p.ID() == self.selectedID());
    };
    self.quickReply = function (formElement) {
        console.warn(formElement.taskId.value);

    };
    self.GetCSSClass = function (st) {
        {
            var statusC = "primary";
            if (st == "Active") {
                statusC = "primary";
            } else if (st == "WaitingApproval") {
                statusC = "success";
            } else if (st == "OnHold") {
                statusC = "danger";
            } else if (st == "WaitingForInfo") {
                statusC = "warning";
            } else if (st == "Complete") {
                statusC = "success";
            }
            return "label-" + statusC;
        }
    },    
    /*SignalR Callbacks*/
    $.connection.taskHub.client.TaskListUpdated = (function (res) {
        ko.mapping.fromJS(res, {}, self);
    });
};

事情就是当我想要使用它时,它不断向我抛出函数未定义等错误。$root,并且$parent未定义。

<tbody data-bind="foreach: Items">
  <tr data-bind="click: $parents[0].selectTask">
    <td data-bind="text: $data.StatusText"></td>
    <td data-bind="text: $data.ID"></td>
    <td data-bind="text: $data.Title"></td>
    <td data-bind="text: $data.Priority"></td>
    <td data-bind="text: $data.Date"></td>
    <td data-bind="text: $data.Creator.Name"></td>
    <td data-bind="text: $data.UserAssigned.Name"></td>
  </tr>
  <tr data-bind="visible:true">
    <td colspan="8">
      <div>
        <div class="well">
          <table class="table" cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">
            <tr>
              <td>Title:</td>
              <td data-bind="text:$data.Title"></td>
            </tr>@*@if (Model.Task.ParentTask != null) {
            <tr>
              <td>Parent:</td>
              <td><a href="www.google.com/" data-bind="text:$data.Title"></a> 
              </td>
            </tr>}*@
            <tr>
              <td>Description:</td>
              <td data-binding="text:$data.Description"></td>
            </tr>
            <tr>
              <td>Creator:</td>
              <td data-bind="text:$data.Creator.Name"></td>
            </tr>
            <tr>
              <td>Status:</td>
              <td data-bind="text:$data.StatusText"></td>
            </tr>@*
            <tr>
              <td>Last Update:</td>
              <td data-bind="text:$data.LastUpdate"></td>
            </tr>
            <tr>
              <td>Total Time:</td>
              <td data-bind="text:$data.TotalTime"></td>
            </tr>*@
            <tr>
              <td>Test Name :</td>
              <td data-bind="text: $data.UnitTestName"></td>
            </tr>
            <tr>
              <td>Actions :</td>
              <td>@*
                <div data-bind="text: alert(JSON.stringify($root))"> <a class="btn btn-info" data-bind="attr : { href : '/Home/TaskDetails/' + $data.ID()}"> Go To Task</a>

                  <button class="btn btn-danger" data-bind="visible : $parent.U.CanStopWorking($data.ID()), click:$root.StopTask">Stop Task</button>
                  <button class="btn btn-success" data-bind="visible : $root.CanStartWorking, click:$root.StartTask">Start Task</button>
                  <button class="btn btn-primary " data-bind="visible: $root.User.CanReply" data-toggle="modal" data-target="#quickReply">QuickReply</button>
                </div>*@</td>
            </tr>
          </table>
          <div class="panel panel-primary">
            <div class="panel-heading">Last Status Comments:</div>
            <div class="panel-body" data-bind="foreach: $data.Comments">
              <p><span class="label" data-bind="text:$data.StatusString, css : GetCSSClass($data.StatusString())"></span>  <b data-bind="text: $data.CommentStamp"> </b>  <span data-bind="text:$data.Text"></span>
              </p>
            </div>
          </div>
        </div>
      </div>
    </td>
  </tr>
</tbody>

所以我很确定这里存在结构问题,但我不确定如何或从哪里开始。

工作流程如下:使用SignalR获取数据,使用knockout更新UI。

但我希望能够使用多个视图模型,而不必使用Bind(elementID) 所以我希望它封装在主视图模型中。

有人能说出错的地方吗?更好的想法,或只是告诉如何解决这个问题?


修改

好的我已经尝试改变我的模型的方式:

var ViewModel = {
    Tasks: new TaskListModel(),
    User: new UserModel()
};

$(function () {

    //Load Data from server
    $.connection.hub.start().done(function () {

        $.connection.taskHub.server.getTaskList().done(function (res) {
            ViewModel.Tasks.SetData(res);

            $.connection.taskHub.server.getCurrentUser().done(function (ress) {
                ViewModel.User.SetData(ress);
                ko.applyBindings(ViewModel);

            }).fail(function (error) {
                console.error(error);
            });
        }).fail(function (error) {
            console.error(error);
        });
    });
});

我的SetData功能如下:

// in User model:
self.User = ko.mapping.fromJS(null);
self.SetData = function (data) {
    ko.mapping.fromJS(data,{},self.User);
};

// in Task model:
self.Items = ko.mapping.fromJS(null);
self.SetData = function (data) {
    ko.mapping.fromJS(data, {}, self.Items);
};

任务模型项被分配用户不是。

我不知道为什么会这样,如果我这样做

var v = ko.mapping(data);

我的v确实已分配给用户。

真诚地,我完全没有想法。我错过了什么,不应该这么困难,不是吗?

0 个答案:

没有答案