在SPA中使用ko.mapping和Knockout组件

时间:2016-06-08 20:29:09

标签: javascript knockout.js

我正在尝试使用KnockoutJS SPA Yeoman生成器来构建SPA应用程序并使用ko.mapping插件。我调整了require.config.js以包含ko-mapping。

在一个独立的应用程序中,我可以使映射插件工作得很好,但是当我尝试在组件的构造函数中使用它时,模板html中永远不会提供绑定。关于在KO组件中使用映射插件可能做错的任何想法?

var require = {
    baseUrl: ".",
    paths: {
        "bootstrap":            "bower_modules/components-bootstrap/js/bootstrap.min",
        "crossroads":           "bower_modules/crossroads/dist/crossroads.min",
        "hasher":               "bower_modules/hasher/dist/js/hasher.min",
        "jquery":               "bower_modules/jquery/dist/jquery",
        "knockout":             "bower_modules/knockout/dist/knockout",
        "komapping":            "bower_modules/knockout-mapping/knockout.mapping",
        "knockout-projections": "bower_modules/knockout-projections/dist/knockout-projections",
        "signals":              "bower_modules/js-signals/dist/signals.min",
        "text":                 "bower_modules/requirejs-text/text"
    },
    shim: {
        "bootstrap": { deps: ["jquery"] },
        "komapping": { deps: ["knockout"], exports: 'komapping'}
    }
};

define(['jquery', 'knockout', './router', 'bootstrap', 'knockout-projections'], function($, ko, router) {
      // Components can be packaged as AMD modules, such as the following:
      ko.components.register('nav-bar', { require: 'components/nav-bar/nav-bar' });
      ko.components.register('home-page', { require: 'components/home-page/home' });

      // ... or for template-only components, you can just point to a .html file directly:
      ko.components.register('about-page', {
        template: { require: 'text!components/about-page/about.html' }
      });

      ko.components.register('bems', { require: 'components/bems/bems' });
      ko.components.register('user-page', { require: 'components/user-page/user' });
      ko.components.register('team-page', { require: 'components/team-page/team' });
      ko.components.register('exec-page', { require: 'components/exec-page/exec' });

      // [Scaffolded component registrations will be inserted here. To retain this feature, don't remove this comment.]

      // Start the application
      ko.applyBindings({ route: router.currentRoute });
});

define(['knockout', 'text!./bems.html','komapping'], function(ko, templateMarkup, komapping) {
    function Bems(params) {
        ko.mapping = komapping;
        var self = this;
        self.response = ko.observable();

        if (typeof params[0] != 'undefined') {
          // Suppress search while loading
          document.getElementById("bems-input").style.visibility = "hidden";
          self.bemsId = String(params[0]);

          $.ajaxSetup({
            cache: false,
            "error":function() { 
              document.getElementById("bems-input").style.visibility = "visible";
              window.location = ("#/bems/");
            }  
          });

          $.getJSON('/app/skyline/api/bems/' + self.bemsId, function(ajax_response) {
              document.getElementById("bems-input").align = "right";
              document.getElementById("bems-input").style.visibility = "visible";
              document.getElementById("bems-container").style.visibility = "visible";
              ko.mapping.fromJS(ajax_response, {}, self.response);
          });
        }
        // Nothing to do without bemsId parameter
    }

    return { viewModel: Bems, template: templateMarkup };
});

我还测试了以下更改:

self.response = ko.observable();
self.message = ko.observable("It works");

$.getJSON('/app/skyline/api/bems/' + self.bemsId, function(ajax_response) {
    self.response = ko.mapping.fromJS(ajax_response);

在上面,self.message绑定正确,可以在模板中访问。但是self.response中的映射属性不受约束。

我的工作设置和损坏的情况之间的主要区别在于工作示例我将ko.mapping结果设置为整个视图模型变量,其中函数Bems是视图模型,这是vm下的变量。

今天我又看了一遍,接近了问题。看起来主要问题是被触发的AJAX回调和通过Yoeman组件应用程序框架发生的绑定之间的操作顺序。如果我执行以下操作并在主ViewModel中放置2秒延迟,它会设法为视图中的第一个绑定属性加载绑定内容,但是无论如何都会因为TypeError而死,即使数据确实加载了属性。这会暂停脚本,不会加载其他属性。

require.js:900 TypeError: Unable to process binding "text: function (){return response().engagement_list()[0].CASE_TITLE }"(…)

function BemsViewModel(params) {

 self.response = ko.observable();

      $.getJSON('/app/skyline/api/bems/' + self.bemsId(),          function(ajax_response) {
          $("#bems-input").attr("align","right");
          $("#bems-input").attr("style", "visibility: visible")
          $("#bems-container").attr("style", "visibility: visible")
          var foobar = {};
          ko.mapping.fromJS(ajax_response, {}, foobar);
          self.response(foobar);

是否有任何最佳实践/模式确保仅在使用此KO组件SSA模板加载视图模型的所有位之后才进行绑定?任何帮助/想法将不胜感激。我真的不想手动定义整个视图模型......

1 个答案:

答案 0 :(得分:1)

好的,我现在有这个工作。关键是要执行上述操作,将映射的属性正确添加到现有视图模型中,但还必须使用带有属性的data-bind来映射此动态内容。这样ko.binding函数只能挖到顶层,并且不会抱怨它下面缺少属性。由于这些在视图模型已经绑定后加载asyc,因此该数据将在事后填写。

http://knockoutjs.com/documentation/with-binding.html

<div id="bems-dyn" data-bind="with: response">
 <p>BEMS Title: <span data-bind="text: engagement_list()[0].CASE"></span></p>
 <p>Container Type: <span data-bind="text: global_params.container_type"></span></p>
 <p>BEMS Id: <span data-bind="text: global_params.bems_id"></span></p> 
</div>

self.response = ko.observable();

  $.getJSON('/app/test/api/bems/' + self.bemsId(), function(ajax_response) {
      $("#bems-input").attr("align","right");
      $("#bems-input").attr("style", "visibility: visible")
      $("#bems-container").attr("style", "visibility: visible")
      //self.response = ko.mapping.fromJS(ajax_response);
      var foobar = {};
      ko.mapping.fromJS(ajax_response, {}, foobar);
      self.response(foobar);