如何使用KnockoutJs将VM保存回服务器

时间:2016-02-29 18:56:57

标签: mvvm knockout.js

我在服务器上有一个带有EF 6的ASP.Net MVC5应用程序。该网站有十几个页面,每个页面都有一个网格,显示包含许多列的业务数据。

某些用户对某些页面(视图)上的某些列不感兴趣,所以我需要一种方法让他们指出哪些视图可见。

在数据库上,View有许多列,而User a可以指定要显示的任何视图的列(并存储在UserViewColumn表中):

enter image description here

我使用以下ViewModel将从数据库中选择的数据传回View:

public class UserPrefsViewModel
    {
        public ICollection<VmUser> Users { get; set; }

        public UserPrefsViewModel()
        {
            this.Users = new List<VmUser>();
        }
    }

    public class VmUser
    {
        public int UserId { get; set; }
        public string AdLogonDomain { get; set; }
        public string AdLogonId { get; set; }
        public List<VmView> Views { get; set; }
    }

    public class VmView
    {
        public int ViewId { get; set; }
        public string Name { get; set; }
        public List<VmColumn> AllColumns { get; set; }
        public List<VmColumn> VisibleColumns { get; set; }
    }

    public class VmColumn
    {
        public int ColumnId { get; set; }
        public string Heading { get; set; }
    }

剃刀观点如下:

@model TVS.ESB.BamPortal.Website.Models.UserPrefsViewModel
@using System.Web.Script.Serialization
@{
    ViewBag.Title = "User Preferences";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>@ViewBag.Title</h2>
@{  string data = new JavaScriptSerializer().Serialize(Model); }

<div id="ViewsAndColumns" class="container">
    <div class="row">
        <div id="Views" class="col-md-3 pull-left">
            <span class="label-info">Views</span>
            <br />
            <table>
                <tr>
                    <td>
                        <select style="width:100px" size="5" data-bind="options:views, optionsText: 'Name', value:selectedView"> </select>
                    </td>
                </tr>
            </table>
        </div>
        <div id="Columns" class="col-md-9 pull-right">
            <table>
                <tr>
                    <td>
                        <span class="label-info">Available Columns</span>
                    </td>
                    <td>
                        <span class="label-info">To Display</span>
                    </td>
                </tr>
                <tr>
                    <td>
                        <select size="10" data-bind="options:allColumns, optionsText: 'Heading', value:selectedAvailableColumn"></select>
                    </td>
                    <td>
                        <select size="10" data-bind="options:columnsToAdd, optionsText: 'Heading', value:selectedColumnToRemove"></select>
                    </td>
                </tr>
                <tr>
                    <td>
                        <button class="btn-default" data-bind="click:addColumn">Add</button>
                    </td>
                    <td>
                        <button class="btn-default" data-bind="click:removeColumn">Remove</button>
                    </td>
                </tr>
                <tr>
                    <td>
                        <button class="btn-danger" data-bind="click:saveToDatabase.bind($data, '@ViewBag.Domain', '@ViewBag.LoginId')">Save to Database</button>
                    </td>
                </tr>
            </table>
        </div>
    </div>
</div>



@section Scripts {
    <script src="~/KnockoutVM/UserPrefs.js"></script>

    <script type="text/javascript">
        var vm = new ViewModel(@Html.Raw(data));
        ko.applyBindings(vm, document.getElementById("UserPrefsDiv"));
    </script>
}

这是一个屏幕抓取:

enter image description here

我正在使用knockoutjs来让用户将第一个选择列表中的列添加到第二个,这是我的淘汰文件:

function ViewModel(data) {
    var self = this;
    var viewColumns;
    var visibleViewColumns

    self.selectedAvailableColumn = ko.observable();
    self.selectedView = ko.observable(data.Users[0].Views[0]);
    self.views = ko.observableArray(data.Users[0].Views);
    self.selectedAvailableColumn = ko.observable();
    self.columnsToAdd = ko.observableArray();
    self.selectedColumnToRemove = ko.observable();

    var getById = function (items, id) {
        return ko.utils.arrayFirst(items(), function (item) {
            return item.ViewId == id;
        });
    };

    self.allColumns = ko.computed(function () {
        var view = getById(self.views, self.selectedView().ViewId);
        return view ? ko.utils.arrayMap(view.AllColumns, function (item) {
            return {
                ColumnId: item.Id,
                Heading: item.Heading
            };
        }) : [];
    }, this);

    self.addColumn = function () {
        if (self.columnsToAdd().indexOf(self.selectedAvailableColumn()) < 0) {
            self.columnsToAdd.push(self.selectedAvailableColumn());
        }
    };

    self.removeColumn = function () {
        self.columnsToAdd.remove(self.selectedColumnToRemove());
    };

    self.saveToDatabase = function () {
        var jsSaveModel = ko.toJS(data);

        $.ajax({
            type: "POST",
            url: location.href,
            data: ko.toJSON(jsSaveModel),
            contentType: 'application/json',
            async: true
        });
    };
};

我的问题是,在发布到服务器之前,我不知道如何最好地管理这个淘汰视图模型,以便它能够持久存储到数据库中。如您所见,我目前将数据从服务器VM加载到绑定到视图控件的可观察数组中。在服务器端VM中,它将是User [x] .Views [y] .VisibleColumns集合,我将用它来填充 UserViewColumn 表,标识用户希望显示的列。 / p>

我想一种方法是更改​​Knockout ViewModel接收的 data 参数。在将数据发布到服务器之前,我可以在 self.SaveToDatabase 函数中执行此操作吗?由于我对Knockout或任何MVVM框架的经验很少,所以我对任何关于最佳实践的建议表示感谢。

1 个答案:

答案 0 :(得分:1)

这可能会因为过于宽泛和基于意见而被标记,但我个人使用AJAX。虽然这确实需要第二次回程到服务器,但它会导致最短的显示时间,并且您可以在检索数据本身时显示微调器或其他内容。它也更灵活,因为它允许您更轻松地在一台计算机上托管内容,在另一台计算机上托管数据,随着您的用户群增加而您的托管解决方案需要平衡多台服务器之间的流量,这在赛道上变得非常重要

无论如何,这里有一个关于你的AJAX处理程序看起来像服务器端的想法:

    getModel: function () {
        var self = this;
        $.ajax({
            url: "/GetModel",
            method: "GET",
            dataType: "json",
            success: function (model) {
                // do something with model here and then let's post it back to the server
                self.postModel(model);
            },
            fail: function () {
                alert("Failed to retrieve model");
            },
            cache: false
        });
    },

    postModel: function (model) {
        var self = this;
        $.ajax({
            url: "/PostModel",
            method: "POST",
            data: model,
            dataType: "json",
            success: function (status) {
                alert("Submitted!");
            },
            fail: function () {
                alert("Failed to post model");
            },
            cache: false
        });
    }

客户端:

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
   /* Called when a touch begins */
    if (gameStarted == false) {
        gameStarted = true

        mainSprite.physicsBody?.affectedByGravity = true
        mainSprite.physicsBody?.allowsRotation = true
        let spawn = SKAction.runBlock({
            () in

            self.createWalls()
        })


        let delay = SKAction.waitForDuration(1.5)

        let spawnDelay = SKAction.sequence([spawn, delay])

        let spawnDelayForever = SKAction.repeatActionForever(spawnDelay)

        self.runAction(spawnDelayForever)

        let distance = CGFloat(self.frame.height + wallPair.frame.height)

        let movePipes = SKAction.moveByX(0, y: -distance - 50, duration: NSTimeInterval(0.009 * distance)) // Speed up pipes

        let removePipes = SKAction.removeFromParent()

        moveAndRemove = SKAction.sequence([movePipes, removePipes])

    } else {
        if died == true {

        }
        else {
            mainSprite.physicsBody?.applyImpulse(CGVectorMake(0, 20)) // TRYING TO APPLY AN IMPULSE TO MY SPRITE SO IT CAN JUMP AS IT MOVES
        }
    }



    for touch in touches {
        let location = touch.locationInNode(self)
    }
}

override func update(currentTime: CFTimeInterval) {
    /* Called before each frame is rendered */
    updateSpritePosition()
    mainSprite.physicsBody?.velocity = CGVectorMake(400, 0) // SETS A CONSTANT VELOCITY, HOWEVER I CAN NOT APPLY AN IMPULSE.
}

您通常需要做的是根据此数据创建一个视图模型,the knockout site详细了解如何执行此操作。