我在谷歌上搜索了如何将 mvc 模型传递给 knockoutjs,似乎有两种方法:
- 使用@Html.Raw(Json.Encode(Model))
- 使用 $.get 或 $.ajax
哪种方式是将 mvc 模型传递给 knockoutjs 的最佳实践?我知道这是每个需求的基础,但似乎使用 $.get 比 @Html.Raw 方法更干净。
我在谷歌上搜索了如何将 mvc 模型传递给 knockoutjs,似乎有两种方法:
哪种方式是将 mvc 模型传递给 knockoutjs 的最佳实践?我知道这是每个需求的基础,但似乎使用 $.get 比 @Html.Raw 方法更干净。
我已经成功地使用了几种方法。
在强类型 Razor 视图中,您可以像编写任何其他 HTML 一样编写 JavaScript ViewModel 对象,随时插入模型元素。我发现这很笨拙,因为 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,您需要在客户端做更多的事情。
我的偏好是使用 $.getJSON 从 JavaScript 本身调用客户端。此时,您可以手动滚动或使用映射库将返回数据处理到您的 ViewModel 中。如果要回调 MVC 控制器中的操作,只需让操作返回 JsonResult 类型(而不是 ActionResult)。(你也可以用 ContentResult 做类似的事情)如果你可以使用新的 MVC4 WebAPI,那些控制器将默认返回 JSON。
@Html.Raw(Json.Encode(Model)) 当您希望将数据作为实际页面下载的一部分发送时使用。这可能会导致您的页面需要更长的时间才能呈现给用户,但是当它确实呈现时,它应该已经准备就绪。
$.get 或 $.ajax 将在页面呈现时获取数据。这将创建一个单独的调用,这将导致您的页面在呈现后更新。
至于使用哪个.. 这取决于您的页面布局方式,是否必须从那里开始所有数据以及获取数据与渲染页面需要多长时间。
我的做法:
一个例子:
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 可以快速提供,因为不涉及真正的数据访问。数据加载可能需要一些时间,具体取决于数据库、数据量等。它还为我的代码提供了非常清晰的关注点分离。
我使用 @Html.Raw 是因为在生成 Knockout UI 之前,用户在页面上没有任何用处,并且由于将 JSON 写入页面的额外初始下载而导致的任何潜在的微不足道的时间延迟都会被用户抵消而不是在构建 UI 之前看到令人不安的延迟。用于构建我的初始视图模型的 JSON 直接在我的页面中,我完全没有问题。
我确实提高效率的地方是通过从 MVC 控制器下载的单独动态脚本中检索任何可重用的参考数据,例如选择选项(产品列表等),然后由浏览器缓存。这样它们就可以跨视图模型重用,并且视图模型只需要存储选定的项目值。
我所做的是一个 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>
我就是做这个的。
正如您所说,这几乎是每个需求的基础。
如果你在做一个“单页应用程序”,你会做很多 $.get 和 $.ajax 调用,如果你只是渲染一个页面,把模型放在Html,保存一个额外的请求到服务器。
还取决于服务器端需要多少代码,如果我的应用程序无论如何都需要 API,我倾向于重用 API 并进行 $.get 和 $.ajax 调用,如果没有,我将模型放在 Html 中。
老问题,但我认为我有一个很好的解决方案,可以以可重用的方式将模型数据传递给 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 并且应返回视图模型的构造函数。构造函数应该接受一个参数——模型的 JSON 数据。
在我看来,最好保持 HTML 干净并避免混合内联 JavaScript 和数据。
我更喜欢通过 AJAX 调用向端点注入数据。