将Mvc Model传递给KnockoutJS的最佳实践

时间:2012-07-24 10:30:20

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

我搜索了如何将mvc模型传递给knockoutjs,似乎有两种方法:

  • 使用@ Html.Raw(Json.Encode(Model))
  • 使用$ .get或$ .ajax

将mvc模型传递给knockoutjs的最佳做法是哪种?我知道这是一个基于要求的基础,但似乎使用$ .get比@ Html.Raw方法更清晰。

8 个答案:

答案 0 :(得分:23)

我成功地使用了几种方法。

在强类型Razor视图中,您可以像编写任何其他HTML一样编写JavaScript ViewModel对象,随时插入Model元素。我觉得这个笨拙,因为Razor和JS与Visual Studio和Intellisense不能很好地配合,但即使有一堆红色波浪形,结果代码也能正常工作。

<script type="text/javascript">

var data = [
    @for (int i=0; i < Model.Packages.Count; i++)
    {
        var package = Model.Packages[i];
        <text>{Id: ko.observable(package.Id),
               Name: ko.observable(package.Name)
              }</text>
    }

    var viewModel = {
        var packages = ko.observableArray(data);
        // more code
    }

    ko.applyBindings(viewModel);
</script>

根据模型的复杂程度,此代码可能会匆忙变得难看。如前所述,您还可以使用Html.Raw()将模型对象序列化为JSON。如果你走那条路,你可以使用它来使用KO Mapping库构建你的Knockout ViewModel:

<script type="text/javascript">
    var data = @Html.Raw(new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(Model));
    var viewModel = ko.mapping.fromJS(data);
    ko.applyBindings(viewModel);
</script>

这不像第一种选择那样愚蠢,但我觉得我放弃了太多的控制。这意味着我的KO ViewModel非常紧密地耦合到我的MVC ViewModel的结构,我可能想要也可能不想要。更不用说,为了实现这一点,我的JavaScript需要在我真正不喜欢的cshtml页面中。最后,这两种方法都是纯粹的服务器端。对于响应速度更快的网页,如SPI,您需要在客户端进行更多操作。

我的偏好是使用JavaScript本身的$ .getJSON调用客户端。此时,您可以手动滚动或使用映射库将返回数据处理到ViewModel中。如果要回调MVC控制器中的操作,只需让操作返回JsonResult类型(而不是ActionResult)。 (您也可以使用ContentResult执行类似操作)如果您可以使用新的MVC4 WebAPI,那些控制器将默认返回JSON。

答案 1 :(得分:8)

当您希望将数据作为实际页面下载的一部分发送时,使用

@ Html.Raw(Json.Encode(Model))。这可能会导致您的页面需要更长时间才能呈现给用户,但是当它呈现时,它应该已经准备好了。

$。get或$ .ajax将在页面呈现时获取数据。这会创建一个单独的调用,这将导致页面在呈现后更新。

至于使用哪个..这取决于您的网页布局方式,是否必须包含所有数据,以及获取数据与呈现网页所需的时间。

答案 2 :(得分:7)

我的方法:

  • 视图模型是用自己的JS文件编写的,没有服务器端代码生成
  • 视图模型通过$ .get或$ .ajax
  • 加载数据
  • 视图在创建视图模型时传入对象,并且此对象包含所有服务器端生成的URL

一个例子:

function MyViewModel(urls) {
    var self = this;
    self.isLoading = ko.observable(true);
    self.items = ko.observableArray();
    self.loadData = function() {
        $.get(urls.load, function(data) {
                // Process data and push into our items array
                self.isLoading(false);
            });
    }
}

var vm = new MyViewModel({
        load: '@Url.Action("GetData", "MyItemController")'
    });

$(function() {
    ko.applyBindings(vm);
    viewModel.loadData();
});

这意味着我对数据进行了额外的AJAX调用,但IMO用户开始意识到数据!= UI。好处是我的UI可以快速提供,因为不涉及真正的数据访问。数据加载可能需要一些时间,具体取决于数据库,数据量等等。它还为我的代码提供了非常清晰的关注点分离。

答案 3 :(得分:3)

我使用@ Html.Raw,因为无论如何生成Knockout UI,用户无法在页面上做任何有用的事情,并且由于将JSON写入页面的额外初始下载而导致的任何潜在的微不足道的时间延迟都会被用户在构建UI之前没有看到令人不安的延迟。我完全没有问题,用于构建我的初始视图模型的JSON直接在我的页面中。

我在哪里提高效率是通过从MVC控制器单独的动态脚本下载中检索任何可重复使用的参考数据,例如选择选项(产品列表等),然后由浏览器进行缓存。这样,它们可以在视图模型中重复使用,视图模型只需要存储选定的项值。

答案 4 :(得分:3)

我做的是一个Html.Raw然后那个js对象我将它传递给我的淘汰赛js模型。像这样:

//I'm using Newtonsoft library to serialize the objects
@{
    var jsModel = Newtonsoft.Json.JsonConvert.SerializeObject(Model);
}

<script type="text/javascript">
var model = @Html.Raw(jsModel);

var vm = new MyKnockoutModel(model);
ko.applyBindings(vm);

var MyKnockoutModel = function(model) {
    var self = this;
    this.Id = ko.observable(model.Id);
    this.Name = ko.observable(model.Name);
    this.Array1 = ko.observableArray(model.Array1);
}

</script>

这就是我的所作所为。

答案 5 :(得分:2)

正如你所说,这几乎是每个要求的基础。

如果您正在进行“单页应用程序”,那么您将要执行大量$ .get和$ .ajax调用,如果您只是渲染单个页面,则可能会更快地执行Html中的模型,为服务器保存了额外的请求。

还取决于您希望服务器端有多少代码,如果我的应用程序需要API,我倾向于重用API并执行$ .get和$ .ajax调用,如果没有,我将模型放在Html中

答案 6 :(得分:0)

老问题,但我认为我有一个很好的整洁解决方案,可以以可重复使用的方式将模型数据传递给KO视图模型。请注意,我也使用require.js。

在.NET中添加HtmlHelper扩展方法:

using System.Web;
using System.Web.Mvc;
using System.Web.Script.Serialization;

namespace PROJECT.YOUR.EXTENSIONS.NAMESPACE {

    public static class HtmlHelperJavascriptExtensions {

        public static IHtmlString Serialize(this HtmlHelper helper, object obj) {
            return helper.Raw(new JavaScriptSerializer().Serialize(obj));
        }
    }
}

添加局部视图,KnockoutJsBinderPartial(我必须使用aspx):

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<PROJECT.Views.Shared.KnockoutJsBinderViewData>" %>
<%@ Import Namespace="PROJECT.YOUR.EXTENSIONS.NAMESPACE" %>

<script type="text/javascript">
    require(["Path/To/KnockoutJs", "<%= Model.ViewModelPath %>"], function (ko, ViewModel) {
        ko.applyBindings(new ViewModel(<%= Html.Serialize(Model.InnerModel) %>));
    });
</script>

该视图引用的模型是:

namespace PROJECT.Views.Shared {

    public class KnockoutJsBinderViewData {

        public object InnerModel { get; private set; }

        public string ViewModelPath { get; private set; }

        public KnockoutJsBinderViewData(object innerModel, string viewModelPath) {
            InnerModel = innerModel;
            ViewModelPath = viewModelPath;
        }
    }
}

现在,要将.NET模型连接到淘汰视图模型,您需要做的就是:

<% Html.RenderPartial(
   "~/Views/Shared/KnockoutJsBinderPartial.ascx",
   new PROJECT.Views.Shared.KnockoutJsBinderViewData(
        Model, // The model from your strongly typed MVC view
        "Path/To/Knockout/ViewModel")); // The path to the Javascript file containing your view model
%>

注意,包含视图模型的Javascript文件不应该调用ko.applyBindings,并且应该返回视图模型的Constructor函数。构造函数应该使用一个参数 - 模型的JSON数据。

答案 7 :(得分:-3)

在我看来,最好保持HTML清洁,避免混入内联JavaScript和数据。

我更喜欢使用AJAX调用向端点注入数据。