在 Knockout 中绑定多个 PartialView

Posted

技术标签:

【中文标题】在 Knockout 中绑定多个 PartialView【英文标题】:Binding with multiple PartialViews in Knockout 【发布时间】:2015-01-17 08:57:23 【问题描述】:

我有一个 jQuery Accordion,每个面板都包含一个表单。所有表单都相同,输入共享相同的 ID、名称和data-bind 属性。

假设每个表单都有不同的绑定上下文(使用ko with:),如果有两个表单,则this is how I would set up the Knockout.js ViewModel。

但是,我事先不知道会有多少表格。我正在为 MVC ViewModel 的表单集合中的每个表单对象呈现一个 PartialView(其中包含表单)。

@model ViewModel

<div class="container-fluid">
    <div id="jQueryAccordion">

        @foreach (var form in Model.AllForms.ToList())
        
           <!-- ko with: items[@form.Key] -->
           html.RenderPartial("_Form", form);
           <!-- /ko --> 
        

        // etc.

如果我不知道会有多少表单,我将如何设置 Knockout.js ViewModel?

【问题讨论】:

【参考方案1】:

我建议您可以通过 ajax 调用动态加载局部视图并相应地绑定数据敲除绑定,如下所示:

//C# XxxController: return partial view:
public ActionResult MyView()

    return PartialView("_MyView");


//Ajax call to load partial view at client side:
$.get('Xxx/MyView', function(view)
    _contentHolder.html(view);
    ko.applyBinding(self, _contentHolder[0]);
)

您可以循环浏览您的模型集合并动态应用剔除绑定。

【讨论】:

可以是div dom:_contentHolder = $('#divContentHolder')【参考方案2】:

正如 Anh Bui 所建议的,我会在浏览器中动态创建它们。将applyBindings 与您在服务器端使用 ASP.net 创建的标记一起使用有点像 hack,这意味着您要对抗 Knockout,而不是使用它。

最好让 Knockout 负责实际创建表单。这意味着

    仅向其提供创建每个 JSON 表单所需的数据 为表单标记创建 Knockout 模板 使用forEach 绑定循环遍历数据

模板:

<script type="text/html" id="form-template">
  <form action="/target-url">
    <label for="user_name">What's your name?</label>
    <input type="text" data-bind="value: user_name" name="user_name" />
    <label for="user_location">Where are you from?</label>
    <input type="text" data-bind="value: user_location" name="user_location" />
  </form>
</script>

接下来,您将相关的表单数据输出为服务器端的 JSON 数组。我没用过ASP.net,所以这里只能给你伪代码:

<script type="application/javascript">
  window.form_json_from_server = "

   @foreach (var form in Model.AllForms.ToList())
   
     // .. ASP.net JSON output magic goes here
   

  ";
</script>

这样你的标记的最终结果看起来像

<script type="application/javascript">
  window.form_json_from_server = "[

    user_name: "Foo1", user_location: "Bar1" ,
    user_name: "Foo2", user_location: "Bar2" ,
    user_name: "Foo3", user_location: "Bar3" 

  ]";
</script>

(请注意,JS 字符串不能包含换行符,我这里用换行符格式化,方便阅读)

现在我们将表单数据格式化为 JSON,保存在 Javascript 字符串中。接下来:您的 Knockout 视图模型:

var ViewModel = function ViewModel() 
  var that = this,
      raw_forms_object;

  // we reconstitute our JSON string into a Javascript object
  raw_forms_object = JSON.parse(window.form_json_from_server);

  // this is where the objects made from our JSON will end up in
  this.forms = ko.observableArray([]);

  ko.utils.arrayForEach(raw_forms_object, function(f) 
    // f contains one of our form objects, such as  user_name: "Foo1", user_location: "Bar1" 

    // instead of adding f directly to the array, we make a new object in which the
    // properties are observables
    var form = 
      user_name: ko.observable(f.user_name),
      user_location: ko.observable(f.user_location),
    ;

    // add our new form object to our observableArray
    // make sure to use 'that', because 'this' is the scope of the arrayForEach callback we're in
    that.forms.push(form);
  );

现在我们的视图模型上有一个名为“forms”的 observableArray,其中包含我们的表单对象。我们使用forEach 绑定来创建与表单对象一样多的表单:

<div data-bind="template:  name: 'form-template', foreach: forms "></div>

剩下的就是将我们的视图模型实例应用到页面:

ko.applyBindings( new ViewModel() );

如果你喜欢,你可以在这个可运行的代码 sn-p 中尝试一下:

var ViewModel = function ViewModel() 
  var that = this,
      raw_forms_object;
    
  // we reconstitute our JSON string into a Javascript object
  raw_forms_object = JSON.parse(window.form_json_from_server);
    
  // this is where the objects made from our JSON will end up in
  this.forms = ko.observableArray([]);
    
  ko.utils.arrayForEach(raw_forms_object, function(f) 
    // f contains one of our form objects, such as
    //  user_name: "Foo1", user_location: "Bar1" 
    
    // instead of adding f directly to the array, we make a new object in which the
    // properties are observables
    var form = 
      user_name: ko.observable(f.user_name),
      user_location: ko.observable(f.user_location),
    ;
    
    // add our new form object to our observableArray
    // make sure to use 'that', because 'this' is the scope 
    // of the arrayForEach callback we're in
    that.forms.push(form);
  );



ko.applyBindings( new ViewModel() );
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<script type="text/html" id="form-template">
  <form action="/target-url">
    <label for="user_name">What's your name?</label>
    <input type="text" data-bind="value: user_name" name="user_name" />
    <label for="user_location">Where are you from?</label>
    <input type="text" data-bind="value: user_location" name="user_location" />
  </form>
</script>

<div data-bind="template:  name: 'form-template', foreach: forms "></div>

<script type="application/javascript">
   window.form_json_from_server = '["user_name": "Foo1","user_location": "Bar1","user_name": "Foo2","user_location": "Bar2","user_name": "Foo3","user_location": "Bar3"]';
</script>

【讨论】:

非常感谢过程的高层解释!我最终使用var jsonData = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model)); 技巧解决了 Knockout Mapping 插件的 ViewModel 问题。我知道我呈现表单的方式存在潜在问题,而这个答案正是我需要让模板变得更平易近人的原因。

以上是关于在 Knockout 中绑定多个 PartialView的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Knockout.js 将多个 View 绑定到单个 ViewModel

多个布尔值上的 Knockout.js“如果绑定”

将多个输入绑定到可观察数组中的同一变量(Knockout.JS)

Knockout.js 与多个 Select2 绑定

Knockout.js:页面或页面的一部分上的多个 ViewModel 绑定

Knockout ValidationMessage 绑定不显示消息