我谷歌搜索了一下如何将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对象,插入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。
我的做法:
一个例子:
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();
});
我所做的是使用Html.Raw,然后将该js对象传递给我的knockout 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>
这就是我所做的。
我使用 @Html.Raw 是因为在生成 Knockout UI 之前,用户无法在页面上执行任何有用的操作。此外,将 JSON 写入页面中会导致潜在的微小延迟,但由于用户在构建 UI 之前看不到令人不安的延迟,因此可以抵消这些延迟。对于用于构建初始视图模型的 JSON 直接放在页面中,我完全没有问题。
我在效率方面取得了提高,是通过从 MVC 控制器中检索用于选择选项(产品列表等)的可重复使用的参考数据,并在单独的动态脚本下载中进行缓存。这样,它们可以在视图模型之间被重复使用,而视图模型只需要存储所选项目的值。
虽然这是一个老问题,但我认为我有一个漂亮而整洁的解决方案,可以以可重用的方式将模型数据传递给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;
}
}
}
<% 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
%>