Best practice on passing Mvc Model to KnockoutJS

后端 未结 8 927
独厮守ぢ
独厮守ぢ 2020-12-04 17:12

I googled around on how to pass mvc model to knockoutjs and it seems there are two ways:

  • Using @Html.Raw(Json.Encode(Model))
  • Using $.get or $.ajax
相关标签:
8条回答
  • 2020-12-04 17:25

    @Html.Raw(Json.Encode(Model)) is used when you want the data to be sent as part of the actual page download. This could result in your page taking longer to be rendered to the user but when it does render it should be all ready to go.

    $.get or $.ajax will instead get the data while the page is rendering. This creates a separate call which will result in your page updating after it's rendered.

    As for which to use.. it depends on how your page is laid out, whether or not you have to have all data there to start with and how long it takes to get your data vs. render your page.

    0 讨论(0)
  • 2020-12-04 17:28

    I've successfully used several approaches.

    In a strongly typed Razor view, you can write the JavaScript ViewModel object like you would any other HTML, inserting the Model elements as you go. I find this klunky as Razor and JS don't play well together with Visual Studio and Intellisense, but even with a bunch of red squigglies the resulting code works fine.

    <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>
    

    This code can get ugly in a hurry depending on the complexity of your Model. As you mentioned, you can also serialize the model object into JSON using Html.Raw(). If you go that route, you can use it to build your Knockout ViewModel using the KO Mapping library:

    <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>
    

    This isn't as kludgy as the first option, but I feel like I'm giving up too much control this way. It means my KO ViewModel is pretty tightly coupled to the structure of my MVC ViewModel, which I may or may not want. Not to mention, for this to work my JavaScript needs to be in my cshtml page which I really don't like. Lastly, both of these approaches are purely server side. For more responsive web pages, like SPIs, you'll want to do more on the client side.

    My preference is to use $.getJSON calls client side from the JavaScript itself. At that point, you can process the return data into your ViewModel, either hand rolling or using the mapping library. If you are calling back to actions in your MVC controller, just have the action return a JsonResult type (instead of an ActionResult). (You can also do similar stuff with ContentResult) If you can use the new MVC4 WebAPI, those controllers will return JSON by default.

    0 讨论(0)
  • 2020-12-04 17:28

    Old question, but I think I have a nice tidy solution for passing model data to a KO view model, in a reusable fashion. Note, I'm using require.js as well.

    Add the HtmlHelper extension method, in .NET:

    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));
            }
        }
    }
    

    Add the partial view, KnockoutJsBinderPartial (I've had to use 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>
    

    The model referenced by that view is:

    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;
            }
        }
    }
    

    Now, to wire up your .NET model to your knockout view model, all you need to do is:

    <% 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
    %>
    

    Note, the Javascript file containing your view model SHOULD NOT call ko.applyBindings and should return the Constructor function for the view model. The constructor should take one argument - your JSON data for the model.

    0 讨论(0)
  • 2020-12-04 17:33

    As you say, it's pretty much a per requirement basis.

    If you're doing a "one page application", you're going to do a lot of $.get and $.ajax calls, if you're just rendering a single page, it might be quicker to put the model in the Html, saving an extra request to the server.

    Also depends on how much code you want server-side, if my applications need an API anyway, I tend to reusing the API and do $.get and $.ajax calls, if not, I put the model in the Html.

    0 讨论(0)
  • 2020-12-04 17:34

    What i do is a Html.Raw and then that js object i pass it to my knockout js model. Something like this:

    //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>
    

    That's what i do.

    0 讨论(0)
  • 2020-12-04 17:38

    In my mind it is desirable to keep HTML clean and to avoid mixing in inline JavaScript and Data.

    I prefer to inject data with an AJAX call to an endpoint.

    0 讨论(0)
提交回复
热议问题