1

我对 Knockout 非常有经验,但这是我第一次使用组件,所以我真的希望我错过了一些明显的东西!我将尝试简化我的用例来解释我的问题。

我有一个名为 Index 的 HTML 和 JS 文件。Index.html 具有组件的数据绑定,而 Index.js 具有ko.components.register调用。

索引.html

<div data-bind="component: { name: CurrentComponent }"></div>

索引.js

var vm = require("SectionViewModel");
var CurrentComponent = ko.observable("section");
ko.components.register("section", {
    viewModel: vm.SectionViewModel,
    template: "<h3>Loading...</h3>"
});
ko.applyBindings();

然后我有另一个 HTML 和 JS 文件 - Section.html 和 SectionViewModel.js。正如您在上面看到的,SectionViewModel 是我指定为组件的视图模型。

Section.html

<div>
    <span data-bind="text: Section().Name"></span>
</div>

SectionViewModel.js

var SectionViewModel = (function() {
    function SectionViewModel() {
        this.Section = ko.observable();
        $.get("http://apiurl").done(function (data) {
            this.Section(new SectionModel(data.Model)); // my data used by the view model
            ko.components.get("dashboard", function() {
                component.template[0] = data.View; // my html from the api
            });
        });
    }
    return SectionViewModel;
});
exports.SectionViewModel = SectionViewModel;

作为 SectionViewModel 中的构造函数的一部分,我调用我的 API 以获取填充我的视图模型所需的所有数据。此 API 调用还返回我需要在模板中使用的 HTML(基本上是从 Section.html 中读取的)。

显然,在调用 applyBindings 之前不会调用此构造函数,因此当我进入 API 调用的成功处理程序时,组件上的模板已设置为我的默认文本。

我需要知道的是,我可以更新这个模板吗?如上所示,我在成功处理程序中尝试了以下内容:

ko.components.get("section", function(component) {
    component.template[0] = dataFromApi.Html;
});

这确实将我的默认文本替换为从我的 API 返回的 html(如调试工具中所示),但此更新不会反映在浏览器中。

所以,基本上毕竟,我真正要问的是,有没有办法在绑定后更新组件模板的内容?

我知道解决上述问题的一个选项可能是需要模板,但我确实简化了上述内容并且在它的完整实现中,我无法做到这一点,因此 API 返回 HTML .

非常感谢任何帮助!我目前确实有一个可行的解决方案,但我真的不喜欢我必须构建 JS 代码以使其工作的方式,因此上述解决方案将是理想的。

谢谢。

4

2 回答 2

1
component.template[0] = $(data)[0]

我知道这是旧的,但我发现它试图做同样的事情,并且在我的情况下,appprocah 帮助我想出了这个,模板似乎是一个元素,而不仅仅是原始 html

于 2017-08-30T14:33:53.617 回答
1

您可以在组件内使用模板绑定。

模板bindign的正常使用是这样的:

<div data-bind="template: { name: tmplName, data: tmplData }"></div>

您可以同时制作tmplDatatmplName可观察的,因此您可以更新绑定的数据,并更改模板。是其tmplName内容将用作模板的元素的 id。如果您使用这种语法,您需要一个带有 required 的元素id,因此,在您的 succes 处理程序中,您可以使用 jQuery 之类的东西来创建一个具有适当 的新元素id,然后更新tmplname, 以便更新模板内容。

*这将不起作用: 另一种选择是以不同的方式使用模板绑定:

<div data-bind="template: { nodes: tmplNodes, data: tmplData }"></div>

在这种情况下,您可以直接将节点提供给模板。即使一个 tmplNodes 可观察,它是用你的<h3>Loading...</h3>元素初始化的。然后将其更改为保存从服务器接收到的节点。

因为 nodes不支持 observables:

节点——直接传递一个 DOM 节点数组以用作模板。这应该是一个不可观察的数组,请注意,如果有元素,则将从其当前父级中删除元素。如果您还为 name 传递了非空值,则忽略此选项。

所以你需要使用第一个选项:创建一个新元素,将它添加到具有已知 id 的文档 DOM 中,并使用该 id 作为模板名称。演示:

// Simulate service that return HTML
var dynTemplNumber = 0;
var getHtml = function() {
    var deferred = $.Deferred();
	var html = 
    '<div class="c"> \
	  <h3>Dynamic template ' + dynTemplNumber++ + '</h3> \
	  Name: <span data-bind="text: name"/> \
    </div>';
    setTimeout(deferred.resolve, 2000, html);
    return deferred.promise();
};

var Vm = function() {
  self = this;
  self.tmplIdx = 0;
  self.tmplName = ko.observable('tmplA');
  self.tmplData = ko.observable({ name: 'Helmut', surname: 'Kaufmann'});
  self.tmplNames = ko.observableArray(['tmplA','tmplB']);
  self.loading = ko.observable(false);
  self.createNewTemplate = function() {
      // simulate AJAX call to service
      self.loading(true);
      getHtml().then(function(html) {
          var tmplName = 'tmpl' + tmplIdx++;
          var $new = $('<div>');
          $new.attr('id',tmplName);
          $new.html(html);
          $('#tmplContainer').append($new);
          self.tmplNames.push(tmplName);
	      self.loading(false);
          self.tmplName(tmplName);
      });
  };
  return self;
};

ko.applyBindings(Vm(), byName);
div.container { border: solid 1px black; margin: 20px 0;}
div {padding: 5px; }
.a { background-color: #FEE;}
.b { background-color: #EFE;}
.c { background-color: #EEF;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="byName" class="container">

  Select template by name: 
  <select data-bind="{options: tmplNames, value: tmplName}"></select>
  <input type="button" value="Add template"
       data-bind="click: createNewTemplate"/>
    <span data-bind="visible: loading">Loading new template...</span>
  <div data-bind="template: {name: tmplName, data: tmplData}"></div>

</div>

<div id="tmplContainer" style="display:none">
  <div id="tmplA">
    <div class="a">
      <h3>Template A</h3>
      <span data-bind="text: name"></span> <span data-bind="text: surname"></span>
    </div>
  </div>
  <div id="tmplB">
    <div class="b">
      <h3>Template B</h3>
      Name: <span data-bind="text: name"/>
    </div>
  </div>
</div>

于 2015-11-18T14:30:11.223 回答