【问题标题】:Best practice on passing Mvc Model to KnockoutJS将 Mvc 模型传递给 KnockoutJS 的最佳实践
【发布时间】:2012-07-22 15:46:32
【问题描述】:

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

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

将mvc模型传递给knockoutjs的最佳实践有哪些?我知道这是基于每个要求的,但似乎使用 $.get 与 @Html.Raw 方法相比更清洁。

【问题讨论】:

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


    【解决方案1】:

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

    在强类型的 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。

    【讨论】:

    • 我很乐意看到您在上面的答案中使用 $.getJSON 的代码示例。
    【解决方案2】:

    @Html.Raw(Json.Encode(Model)) 当您希望将数据作为实际页面下载的一部分发送时使用。这可能会导致您的页面需要更长的时间才能呈现给用户,但当它呈现时,它应该已经准备就绪。

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

    至于使用哪个.. 这取决于您的页面的布局方式、您是否必须从那里获取所有数据以及获取数据与渲染页面需要多长时间。

    【讨论】:

      【解决方案3】:

      我的做法:

      • 视图模型写在自己的 JS 文件中,没有服务器端代码生成
      • 视图模型通过 $.get 或 $.ajax 加载数据
      • view在创建view model的时候传入一个object,这个object包含了所有服务端生成的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 可以快速提供,因为不涉及真正的数据访问。数据加载可能需要一些时间,具体取决于数据库、数据量等。它还为我的代码提供了非常清晰的关注点分离。

      【讨论】:

      • 我喜欢这个。当我在做“旧式”的事情(回发和有限的 ajax)时,我总是会通过部分视图用初始数据填充页面中的 HTML。但我不再这样做了,因为它没有任何意义。这样做的好处是我不必担心缓存页面会带回旧数据
      【解决方案4】:

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

      我确实提高效率的地方是通过从 MVC 控制器下载的单独动态脚本中检索任何可重用的参考数据,例如选择选项(产品列表等),然后由浏览器缓存。这样它们就可以跨视图模型重用,并且视图模型只需要存储所选项目的值。

      【讨论】:

        【解决方案5】:

        我所做的是一个 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>
        

        我就是这么做的。

        【讨论】:

          【解决方案6】:

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

          如果你在做一个“单页应用程序”,你会做很多 $.get 和 $.ajax 调用,如果你只是渲染一个页面,把Html 中的模型,为服务器保存一个额外的请求。

          还取决于你想要多少代码在服务器端,如果我的应用程序无论如何都需要 API,我倾向于重用 API 并进行 $.get 和 $.ajax 调用,如果没有,我将模型放在 Html .

          【讨论】:

          • 同意,这取决于。另外,如果您不在服务器上使用模型,则无需对模型使用 MVC。相反,只需使用 MVC 3 控制器操作、WCF 服务或带有 API 控制器的新 Web API 来返回它。后者是我的偏好。
          【解决方案7】:

          老问题,但我认为我有一个很好的解决方案,可以以可重用的方式将模型数据传递给 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 数据。

          【讨论】:

            【解决方案8】:

            在我看来,最好保持 HTML 干净并避免混合内联 JavaScript 和数据。

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

            【讨论】:

            • 你好。我通常不会从低质量队列中出来投反对票。但是当我做一些非常糟糕的事情时。在这种情况下,它是 jQuery。
            • 你能看到如何改进这篇文章吗?
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-12-30
            • 2016-02-27
            • 2011-09-08
            • 1970-01-01
            • 1970-01-01
            • 2015-04-01
            相关资源
            最近更新 更多