使用TypeScript和RequireJS进行knockout.mapping

时间:2014-09-25 16:19:57

标签: javascript knockout.js typescript knockout-mapping-plugin

尝试与TypeScript和RequireJS一起理解ko.mapping。据我了解,我可以创建一个视图模型,将其绑定到一个视图,并通过视图模型将一个复杂的对象暴露给我的视图。我没有运气让这个工作。 Web上的大多数示例都希望显示如何获取Web服务响应并直接绑定它。我正在寻找一个比这更基本的例子 - 我只想将未绑定的对象映射到屏幕。我当然可以手动完成,但我认为该工具是为了这个目的而设计的......

我有两个需求:

  1. 在初始显示时显示一个值 - 可能为空白
  2. 使用按钮更改值。
  3. 我一直在玩一些示例代码作为概念证明,作为我能想到的最基本的版本。我们的想法是用按钮呈现视图。按钮的文本应加载" Hello World!",点击后更新为" Goodbye moon ..."。

    我认为我的视图模型需要两个对象......

    1. POJO
    2. 绑定对象,实例化为ko.mapping.fromJS({})
    3. 我的理解(可能是错误的)是映射将采用POJO并在绑定对象中自动创建POJO的可观察版本。视图绑定到绑定对象。在任何时候,例如单击按钮,我可以扩充我的POJO,并重新加载到绑定对象,我的视图将相应更新。

      我的视图模型已连接,因为我可以设置断点并观察它们被击中。页面加载失败,因为绑定对象不可用。如果我从ko.mapping更改为标准的observable,它加载正常。

      考虑ko.mapping时我错过了什么?我的做法是否完全有缺陷?


      基本POJO课程

      class DefaultModel {
          public myField: string;
      }
      export = DefaultModel;
      

      查看

      <!DOCTYPE html>
      <html lang="en">
          <head>
              <meta charset="utf-8" />
              <title>TypeScript HTML App</title>
              <script data-main="Application/require-config" src="Scripts/require.js"></script>
          </head>
          <body>
              <h1>TypeScript HTML App</h1>
              <button id="myMethodTest" data-bind="text: boundModel().myField, click: function () { myButton_Click() }" ></button>
          </body>
      </html>
      

      查看模型

      /// <reference path="../Scripts/typings/knockout/knockout.d.ts" />
      /// <reference path="../Scripts/typings/knockout.mapping/knockout.mapping.d.ts" />
      
      import DefaultModel = require("Models/DefaultModel");
      import ko = require("knockout");
      
      class DefaultViewModel {
          public basicModelInstance: DefaultModel;
          public boundModel: any;
      
          constructor() {
              // INSTANTIATE THE BOUND MODEL TO BE A BLANK KO MAPPED AWARE OBJECT
              this.boundModel = ko.mapping.fromJS({});
      
              // SETUP A BASIC INSTANCE OF A POJO
              this.basicModelInstance = new DefaultModel;
              this.basicModelInstance.myField = "Hello World!";
      
              // LOAD THE POPULATED POJO INTO THE BOUND OBVSERVABLE OBJECT
              this.boundModel = ko.mapping.fromJS(this.basicModelInstance, {}, this.boundModel);
          }
      
          myButton_Click() {
              // UPDATE THE POJO
              this.basicModelInstance.myField = "Goodbye Moon...";
      
              // RELOAD THE POJO INTO THE BOUND OBJECT
              this.boundModel = ko.mapping.fromJS(this.basicModelInstance, {}, this.boundModel);
          }
      }
      export = DefaultViewModel;
      

      RequireJS配置

      /// <reference path="../Scripts/typings/requirejs/require.d.ts" />
      
      require.config({
          baseUrl: "",
          paths: {
              "jQuery": "Scripts/jquery-2.1.1",
              "knockout": "Scripts/knockout-3.2.0.debug",
              "utilities": "Application/utilities",
              "ViewModelMapper": "Application/ViewModelMapper",
              "komapping": "Scripts/knockout.mapping-latest.debug"
      
      
          },
      
          shim: {
              "jQuery": {
                  exports: "$"
              },
              komapping: {
                  deps: ['knockout'],
                  exports: 'komapping'
              }
          },
      });
      
      require(["jQuery"], function ($) {
          $(document).ready(function () {
              // alert('dom ready');
      
              require(["utilities", "knockout", "ViewModelMapper", "komapping"], (utilities, knockout, viewModelMapper, komapping) => {
                  utilities.defineExtensionMethods($);
                  knockout.mapping = komapping;
      
                  var url = window.location;
                  var location = utilities.getLocation(url);
                  var urlPath = location.pathname;
                  var urlPathWithoutExtension = urlPath.replace(".html", "");
      
                  var viewModel = viewModelMapper.getViewModel(urlPathWithoutExtension);
                  knockout.applyBindings(viewModel);
              });
          });
      });
      

2 个答案:

答案 0 :(得分:2)

我将此授予@wired_in以获得所提供的帮助。在这里,我将提供最终解决了我的问题的代码的工作版本。

我的理论 - 如果映射可以采用AJAX调用的结果并自动将其映射到一个可观察的,为什么不能有任何普通的POJO?好吧,它可以!这种基本能力是解放的。现在我可以自由地创建模型,而不会用'可观察'来污染它们。模型可以像任何普通对象一样,没有特殊处理。操纵模型到需要,然后当需要在视图上表示时,通过ko.mapping.fromJS调用绑定它。

这是最终的解决方案。我将按照我提出原始问题的相同顺序提出......


基本POJO课程:

class DefaultModel {
    public myField: string;
}
export = DefaultModel;

查看:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>TypeScript HTML App</title>
        <script data-main="Application/require-config" src="Scripts/require.js"></script>
    </head>
    <body>
        <h1>TypeScript HTML App</h1>
        <button id="myMethodTest" data-bind="text: boundModel.myField, click: function () { myButton_Click() }" ></button>
    </body>
</html>

查看型号:

/// <reference path="../Scripts/typings/knockout/knockout.d.ts" />
/// <reference path="../Scripts/typings/knockout.mapping/knockout.mapping.d.ts" />

import DefaultModel = require("Models/DefaultModel");
import ko = require("knockout");


class DefaultViewModel {
    public basicModelInstance: DefaultModel;
    public boundModel: KnockoutObservable<DefaultModel>;

    constructor() {
        // SETUP A BASIC INSTANCE OF A POJO
        this.basicModelInstance = new DefaultModel;
        this.basicModelInstance.myField = "Hello World!";

        // LOAD THE POPULATED POJO INTO THE BOUND OBVSERVABLE OBJECT
        this.boundModel = ko.mapping.fromJS(this.basicModelInstance);
    }

    myButton_Click() {
        // UPDATE THE POJO
        this.basicModelInstance.myField = "Goodbye Moon...";

        // RELOAD THE POJO INTO THE BOUND OBJECT
        this.boundModel = ko.mapping.fromJS(this.basicModelInstance, this.boundModel);
    }
}
export = DefaultViewModel;

RequireJS配置:

/// <reference path="../Scripts/typings/requirejs/require.d.ts" />

require.config({
    baseUrl: "",
    paths: {
        "jQuery": "Scripts/jquery-2.1.1",
        "knockout": "Scripts/knockout-3.2.0.debug",
        "utilities": "Application/utilities",
        "ViewModelMapper": "Application/ViewModelMapper",
        "komapping": "Scripts/knockout.mapping-latest.debug"


    },

    shim: {
        "jQuery": {
            exports: "$"
        },
        komapping: {
            deps: ['knockout'],
            exports: 'komapping'
        }
    },
});

require(["jQuery"], function ($) {
    $(document).ready(function () {
        // alert('dom ready');

        require(["utilities", "knockout", "ViewModelMapper", "komapping"], (utilities, knockout, viewModelMapper, komapping) => {
            utilities.defineExtensionMethods($);
            knockout.mapping = komapping;

            var url = window.location;
            var location = utilities.getLocation(url);
            var urlPath = location.pathname;
            var urlPathWithoutExtension = urlPath.replace(".html", "");

            var viewModel = viewModelMapper.getViewModel(urlPathWithoutExtension);
            knockout.applyBindings(viewModel);
        });
    });
});

<强>结论:

最后,我被困在3件事上......

  1. 我的视图错误地引用了视图中的绑定对象 模型。感谢@wired_in在这方面的帮助
  2. 在构造函数中,我传递了太多参数。谢谢 @wired_in指出这一点。的文档 KnockoutJS.mapping在这方面尚不清楚。我觉得使用1 这里可选择3个参数。
  3. 在方法myButton_Click中,我需要传递一个引用 现有的,已绑定的对象(A.K.A.,一个内部的viewmodel 视图模型)。这是允许更新现有的关键 约束模型。
  4. 现在,我可以根据封面下操作的数据控制我的视图何时发生变化。无论数据是来自AJAX调用,还是内部计算操作,来自第三方系统,来自上传的文件 - 无论如何 - 我现在可以在视图中看到数据。很酷。

    最后,问题是“为什么在未绑定的POJO中有数据?为什么不使用绑定对象并将其作为可观察对象操纵?” - 我认为答案是“便携性”。我希望在没有特别考虑的情况下自由地将普通对象传入和传出代码库。将对象标记为可观察的概念是框架强加的约束 - 一种使绑定成为可能的解决方法。要求在任何地方都应用“可观察”属性是不可取的。分离关注宝贝!无论如何,现在离开我的肥皂盒......

    谢谢@wired_in。

答案 1 :(得分:1)

A)在您的视图模型代码中,对ko.mapping.fromJS的调用只需要前两个参数。由于它返回绑定模型,因此您不需要传入绑定模型。它应该是:

this.boundModel = ko.mapping.fromJS(this.basicModelInstance, {});

B) viewModel.boundModel不是函数,它是一个对象。因此,在您的HTML中,您的约束text: boundModel().myField应为text: boundModel.myField

C)您误解了绑定的工作方式。一旦你有你的绑定模型,就没有必要更新&#34; POJO&#34;然后每次视图模型中的某些内容发生更改时重新创建绑定模型。 knockout提供的双向数据绑定将使您的视图模型和您的ui(html)保持同步,因此您只需要使用视图模型。当您需要查看视图模型中的内容并更新您的#PO; POJO&#34;时,只应在需要更新服务器时,您可以使用ko.mapping.toJS函数与ko.mapping.fromJS函数相反。你传递你的绑定模型,它会让你回到香草JS&#34; POJO&#34;对象,删除所有可观察对象。