翻译:Knockout 快速上手 - 3: knockoutJS 快速上手
许多时候,学会一种技术的有效方式就是使用它解决实际中的问题。在这一节,我们将学习使用 Knockout 来创建一个常见的应用,库存管理应用。
应用概览
在创建我们的应用之前,我们需要一个公司,来理解应用解决的问题。我们的应用将能够完成下列任务:
- 浏览公司销售的每种产品,跟踪 SKU 数量和说明。
- 对每种产品的价格,费用和数量进行赋值。
- 当公司决定销售某种新产品的时候,可以创建新的产品。
- 当公司停售某种产品的时候,可以删除这种产品。
第一步 定义命名空间
在我们实际开始开发应用之前,很重要的一个问题就是规划我们如何组织我们的程序,将我们应用的代码与浏览器界面和本地函数进行分离。你可能奇怪对于这么小的应用我们为什么要这么做。对于 JavaScript 应用的最佳实践来说,这么做无论如何都是非常重要的。通过命名空间,即使对于一个很小的应用来说,在以后随着应用的不断扩展,也可以确保容易进行维护,并且与第三方的组件进行分隔。( 例如许多的脚本插件 )
我们将在前面创建的 app.js 中定义我们的命名空间。下面代码就是定义定名空间的代码。
// Define the namespace window.myApp = {}; |
第二步 创建模型
我们创建的第一个模型将用来表示我们的产品对象。我们通过创建一个名为 Product.js 的文件来完成这个任务。文件的内容如下所示。
(function (myApp) { // Product Constructor Function function Product() { var self = this; // "SKU" property self.sku = ko.observable(""); // "Description" property self.description = ko.observable(""); // "Price" property self.price = ko.observable(0.00); // "Cost" property self.cost = ko.observable(0.00); // "Quantity" property self.quantity = ko.observable(0); } // add to our namespace myApp.Product = Product; }(window.myApp)); |
在这段代码中,我们定义了一个函数作为 Product 的构造器。如你所见,我们将这个函数定义在一个称为立即执行的函数表达式中 ( IIFE )。我们为了如下的原因使用这个模式:
- 这使得我们定义了一个 JavaScript 的作用域,防止污染全局命名空间 ( 像 window 和 document 所处的命名空间 )。这使得我们在调试的时候,不会在本地的函数,比如 windows 中看到和使用我们定义的 Product 函数。
- 这使得我们可以创建私有的函数,在其他的代码中禁止访问。如果我们定义了 Product 函数之后,没有将它添加到 myApp 命名空间中,就没有代码可以在 IIFE 之外访问我们的 Product 构造器。这在创建复杂逻辑的时候非常理想,在某种程度上可以防止其它的对象访问和重写我们的逻辑。
在构造器函数内部,每个属性都创建在 self 对象之上。self 对象是一个指向新创建的 Product 对象的引用。在 JavaScript 中,this 是一个关键字,但是程序员经常被它不同的含义所困惑。这使由于它可以表示多种不同的对象 ( 比如调用对象,全局对象等等 )。为了防止这个问题,我们创建一个局部变量 self ,这样,我们就可以确信它总是表示我们当前的对象实例。
最后,每个属性的值被赋予一个 Knockout 的 Observable 实例。Observable 是 Knockout 中创建可以在属性发生变化的时候触发事件的属性的简单方式 ( 这是 Knockout 中的一个核心概念,我们在后继内容中还要深入讨论 )。通过将属性的初始值传递给这个函数,我们得到一个包装了初始值的函数返回值。可以通过调用这个包装函数来为属性赋值和取值。下面的实例演示了如何使用我们的构造器和属性。
// Usage // create an instance of the Product class var productA = new myApp.Product(); // "set" the 'sku' property productA.sku('12345') // "get" the 'sku' property value var skuNumber = productA.sku(); |
第三步 创建模型使用的视图
现在,我们已经定义了我们的模型类。我们需要创建一个视图在屏幕上显示模型,以便用户可以看到我们的产品数据。我们将使用 HTML 来创建这个视图。我们将使用很简单的布局来显示产品的信息。
<div id="productView"> <p> SKU: <span data-bind="text: sku"></span> </p> <p> Description: <span data-bind="text: description"></span> </p> <p> Cost: <span data-bind="text: cost"></span> </p> <p> Price: <span data-bind="text: price"></span> </p> <p> Quantity: <span data-bind="text: quantity"></span> </p> </div> |
这里,我们使用 Knockout 的 text 绑定来显示产品的信息。text 绑定将属性的值转化为 string 之后,设置 HTML 元素的 innerText 属性 ( 通常使用 span 元素 )。
第四步 创建 ViewModel 管理模型
这里,我们将会需要创建业务逻辑,来处理创建产品,删除产品来管理我们的产品列表。我们还需要某种数组来来管理我们的产品列表。因此,我们将建新的类来实现所有的功能、数组、对象以便绑定到用户界面上。我们需要的类就是 ViewModel.
像我们现在创建应用一样,刚开始的 ViewModel 我们仅仅定义一个属性 selectedProduct。这个属性表示我们当前显示在屏幕上进行处理的单个产品,在 js 文件夹中添加一个名为 ProductsViewModel.js 的脚本文件,在其中添加如下代码。
// Products ViewModel (function (myApp) { // constructor function function ProductsViewModel() { var self = this; // the product that we want to view/edit self.selectedProduct = ko.observable(); } // add our ViewModel to the public namespace myApp.ProductsViewModel = ProductsViewModel; }(window.myApp)); |
第五步 使用 Observable 数组
我们公司的业务需要销售多种产品,所以,我们需要保持一个当前产品的列表。在 JavaScript 中,管理和维护一个对象集合的数据结构就是数组。Knockout 更进一步,提供了一个名为 ObservableArray 的对象。后面我会进一步讨论这个对象,这个对象在成员发生变化的时候,会抛出相应的事件通知,这就允许 Knockout 可以在 ObservableArray 发生变化的时候保持用户界面和我们数据结构的同步。
Knockout 的 ObservableArray 与标准的 JavaScript 数组拥有相同的使用方式,包括 ( push, pop, slice, splice ) 等等。所以,如果你使用过 JavaScript 的 Array 话,使用起来非常自然和流畅。
为了创建公司产品的主列表,为们需要为我们的视图模型添加一个新的属性 productCollection 。
// the product that we want to view/edit self.selectedProduct = ko.observable(); // the product collection self.productCollection = ko.observableArray([]); |
第六步 从 ObservableArray 中添加和删除模型
现在,我们已经拥有了一个公司所有产品的列表,下面我们实现向这个列表添加产品和删除产品的逻辑。
添加产品的逻辑仍然比较简单,可以在这个过程中添加一些验证和检查。但是尽可能地简单和清楚。
// creates a new product and sets it up // for editing self.addNewProduct = function () { // create a new instance of a Product var p = new myApp.Product(); // set the selected Product to our new instance self.selectedProduct(p); }; // logic that is called whenever a user is done editing // a product or done adding a product self.doneEditingProduct = function () { // get a reference to our currently selected product var p = self.selectedProduct(); // ignore if it is null if (!p) { return; } // check to see that the product // doesn't already exist in our list if (self.productCollection.indexOf(p) > -1) { self.selectedProduct(null); return; } // add the product to the collection self.productCollection.push(p); // clear out the selected product self.selectedProduct(null); }; |
在这些代码中,我们计划在用户添加新的产品调用addNewProduct 的时候,使用新创建的 Product 对象填充我们当前选中的对象selectedProduct,然后可以开始进行编辑。在用户完成编辑之后,调用doneEditingProduct 的时候,注意需要检查selectedProduct 是否为空,不为空的话,将这个对象添加到产品列表中。
删除产品的逻辑更加简单一些,我们直接检查selectedProduct 是否为空,如果不为空,就直接从列表中删除它。
// logic that removes the selected product // from the collection self.removeProduct = function () { // get a reference to our currently selected product var p = self.selectedProduct(); // ignore if it is null if (!p) { return; } // empty the selectedProduct self.selectedProduct(null); // simply remove the item from the collection return self.productCollection.remove(p); }; |
最后,在用户界面上,我们需要提供一些按钮,用户可以通过它们调用这些业务逻辑。我们添加按钮,绑定按钮的 click 事件到视图模型的相关属性上,如下所示:
<div id="content"> <div id="productView" data-bind="with: selectedProduct"> <p> SKU: <span data-bind="text: sku"></span> </p> <p> Description: <span data-bind="text: description"></span> </p> <p> Cost: <span data-bind="text: cost"></span> </p> <p> Price: <span data-bind="text: price"></span> </p> <p> Quantity: <span data-bind="text: quantity"></span> </p> </div> <div id="buttonContainer"> <button type="button" data-bind="click: addNewProduct">Add</button> <button type="button" data-bind="click: removeProduct">Remove</button> <button type="button" data-bind="click: doneEditingProduct">Done</button> </div> </div> |
第七步 编辑模型的属性
到现在为止,我们仍然没有办法编辑产品列表中每个产品的属性。所以,需要修改我们的视图以便实现双向的绑定。Knockout 的 value 绑定可以帮助我们实现这个目的,但是只能在 input 元素上使用这个绑定。下面我们修改一下我们的视图,如下所示:
<div id="productView"> <form> <fieldset> <legend>Product Details</legend> <label> SKU: <input type="text" data-bind="value: sku" /> </label> <br /> <label> Description: <input type="text" data-bind="value: description" /> </label> <br /> <label> Cost: <input type="text" data-bind="value: cost" /> </label> <br /> <label> Price: <input type="text" data-bind="value: price" /> </label> <br /> <label> Quantity: <input type="text" data-bind="value: quantity" /> </label> </fieldset> </form> </div> |
现在,我们基于表单的视图可以支持编辑产品的属性了。我将会提到这一点,我们需要添加一些输入的验证来保证 Cost 和 Price 中提供了正确的金额,还有Quantity 中是正确的整数。实际上这些问题有些超出了本教程的范围,在互联网上你可以找到很多实现这些功能的脚本库。
第八步 创建主从视图
终于,我们已经创建了管理数据的逻辑,以及通过 HTML 提供了一个非常友好的用户界面,实现了管理公司产品的功能。让我们继续前进,为用户创建一个好用的主从界面视图。
首先,我们需要确认产品视图正确绑定在我们选定的产品上,而且,产品视图只有在选中产品实例之后,才会显示出来。Knockout 提供了一个称为 with 的绑定来实现这些功能。后面我们会详细讨论这些问题。但是 with 绑定不仅提供选中产品的 null 检测,还实现了将绑定的上下文从 ProductViewModel 切换到 selectedProduct ( 这样我们就可以在数据绑定的语法中直接引用这些属性 )。
由于只有在我们选中一个产品的时候,Remove 和 Done 按钮才是可见的,我们将为这两个按钮添加一个 visible 绑定,用来检查 selectedProduct 属性是否已经有值。也可以为 Add 按钮做类似的工作,完成这些功能的代码如下所示。
<div id="buttonContainer"> <button type="button" data-bind="click: addNewProduct, visible: (selectedProduct() ? false : true)">Add</button> <button type="button" data-bind="click: removeProduct, visible: (selectedProduct() ? true : false)">Remove</button> <button type="button" data-bind="click: doneEditingProduct, visible: (selectedProduct() ? true : false)">Done</button> </div> |
最后,我们还需要提供一个显示产品列表的视图来方便用户管理产品。通常是一个表格,列表等等。或者一些控件来实现这些功能。Knockout 足够强大,我们可以直接使用原始的 HTML 来显示产品列表 ProductCollection。
我们使用基本的 select 元素来实现基本的列表。Knockout 提供了一个 options绑定,支持我们将一个 ObservableArray 绑定到 select 元素。我们还将会提供第二个 Observable 绑定来保持视图中选中的产品。为了达到这个目的,我们在 select 元素中使用 value 绑定来绑定到选中的项目,在视图模型中,我们增加一个新的绑定属性listViewSelectedItem,,下面的代码演示了新建的属性。属性后面的 subscription 用来传递这个属性的任何变化到我们的selectedProduct 属性中。
// the product that we want to view/edit self.selectedProduct = ko.observable(); // the product collection self.productCollection = ko.observableArray([]); // product list view selected item self.listViewSelectedItem = ko.observable(null); // push any changes in the list view to our // main selectedProduct self.listViewSelectedItem.subscribe(function (product) { if (product) { self.selectedProduct(product); } }); |
我们的列表视图实现如下所示:
<div id="productListView"> <select id="productList" size="10" style="min-width: 120px;" data-bind="options: productCollection, value: listViewSelectedItem, optionsText: 'sku'"> </select> </div> |
在前面代码中,使用了optionsText 绑定来绑定 ObservableArray 中每个元素的属性,开始的时候,我们设置 Product 的 sku 属性,但是我们如何能够同时看到 sku 属性和 description 属性的值呢?我们可以通过 Computed Observable 来实现,很快我们就会讨论这个特性,现在,我们在 Product 类中添加一个计算出 sku 属性和 description 属性的新属性。
// Computed Observables // simply combines the Sku and Description properties self.skuAndDescription = ko.computed(function () { var sku = self.sku() || ""; var description = self.description() || ""; return sku + ": " + description; }); |
在添加了skuAndDescription 属性之后,应该更新一下产品列表视图,可以将optionsText 属性的值重新设置为skuAndDescription 来代替原来的 sku。
第九步 应用绑定
为了让我们的应用能够实际运行,我们需要启动 Knockout 的绑定处理,我们需要确认在所有的脚本正确加载之后,在 ViewModel 初始化之后,执行绑定处理过程。我建议的方式是在 app.js 中如下处理。
// Define the namespace window.myApp = {}; (function (myApp) { // constructor functio for App function App() { // core logic to run when all // dependencies are loaded this.run = function () { // create an instance of our ViewModel var vm = new myApp.ProductsViewModel(); // tell Knockout to process our bindings ko.applyBindings(vm); } } // make sure its public myApp.App = App; }(window.myApp)); |
在 app.js 中创建了初始化逻辑之后,我们需要创建 app 的实例,然后调用 run 方法,在页面最后的位置添加如下的代码。
<script type="text/javascript"> var app = new myApp.App(); app.run(); </script> |
为了教学的目的,我将这段代码放在页面几乎最后的位置,我们还有其他的方式可以使用,比如通过 jQuery 的 ready 函数来执行。
翻译:Knockout 快速上手 - 3: knockoutJS 快速上手的更多相关文章
- knockoutJS 快速上手
翻译:Knockout 快速上手 - 3: knockoutJS 快速上手 许多时候,学会一种技术的有效方式就是使用它解决实际中的问题.在这一节,我们将学习使用 Knockout 来创建一个常见的应用 ...
- webpack快速入门——webpack3.X 快速上手一个Demo
1.进入根目录,建两个文件夹,分别为src和dist 1).src文件夹:用来存放我们编写的javascript代码,可以简单的理解为用JavaScript编写的模块. 2).dist文件夹:用来存放 ...
- 【翻译】C#和.NET核心快速参考
原文:[翻译]C#和.NET核心快速参考 PS:在网上看到的一篇C#总结,英文的,总结的还可以,都是基础知识,翻译给大家学习.文章结尾有英文原版.发布地址:http://www.cnblogs.com ...
- 乘方快速幂 OR 乘法快速幂
关于快速幂这个算法,已经不想多说,很早也就会了这个算法,但是原来一直靠着模板云里雾里的,最近重新学习,发现忽视了一个重要的问题,就是若取模的数大于int型,即若为__int64的时候应该怎么办,这样就 ...
- 如何优化Mysql千万级快速分页,limit优化快速分页,MySQL处理千万级数据查询的优化方案
如何优化Mysql千万级快速分页,limit优化快速分页,MySQL处理千万级数据查询的优化方案
- 【web自动化测试】Playwright快速入门,5分钟上手
我喜欢Playwright! 这是微软开源的一款非常强大的自动化工具,再过几年,他很有可能取代Selenium在浏览器自动化的通知地位.使用过一段时间,我没有找到很好的中文资料可以参考,导致很多问题无 ...
- Knockoutjs快速入门(经典)
Knockoutjs是一个JavaScript实现的MVVM框架.主要有如下几个功能: 1. Declarative bindings 2. Observables and dependency tr ...
- ASP.NET MVC4.0+ WebAPI+EasyUI+KnockOutJS快速开发框架 通用权限管理系统
1.基于 ASP.NET MVC4.0 + WebAPI + EasyUI + Knockout 的架构设计开发 2.采用MVC的框架模式,具有耦合性低.重用性高.生命周期成本低.可维护性高.有利软件 ...
- [转]Knockoutjs快速入门
本文转自:http://www.cnblogs.com/yinzixin/archive/2012/12/21/2827356.html Knockoutjs是一个JavaScript实现的MVVM框 ...
随机推荐
- 获取在线人数 CNZZ 和 51.la
string Cookies = string.Empty; /// <summary> /// 获取在线人数 (51.la统计器) /// </summary> /// &l ...
- Func系列1:安装配置
简介 Func是由红帽子公司以Fedora平台构建的统一网络控制器,是为解决集群管理.监控问题而设计开发的系统管理框架.它是一个能有效简化多服务多服务器系统管理工作的工具,它易于学习.使用和扩展,功能 ...
- android学习笔记24——事件处理
事件处理 android提供了两种事件处理机制: 1.基于回调的事件处理 2.基于监听器的事件处理(通过绑定特定事件监听器) 注意: android对于基于回调的事件处理而言,主要做法就是重写andr ...
- C语言每日一题之No.5
总在想,但凡编程基础正常点,都不至于惨败到这个地步.也像大多数人毕业出来,新鲜的第一份工作,如果做得好还可以略有成就感,做得一般还有提升的空间,但至少不至于像我这样基本没基础的被鄙视得一塌糊涂,被外界 ...
- 用C#将输入的小写字母转化为大写字母
string A = "adsaf"; string B =""; B=A.ToUper();
- PHP Socket 编程详解
PHP中的实现 服务端 <?php set_time_limit(0); // 设置主机和端口 $host = "127.0.0.1"; $port = 12387; // ...
- 【wp之二 页面布局】
1. panoramic全景视图 新建工程选择:Windows Phone Panorama Application 相当于用放大镜看报纸的感觉.用一张图片作为背景. 2.pivot透视视图
- 5. redis管道, 发布订阅, 模拟队列
一. 发布订阅 #订阅scribe 127.0.0.1:6379> SUBSCRIBE "channel_1" Reading messages... (press Ctrl ...
- python-unicode十进制数字转中文
#coding:utf-8 '''主要是unichr()函数.以下数组中的元素转换后为繁体中文,若不加encode("GB18030")就不能正确显示,而且会报错:('gbk' c ...
- Form_Form Builder Export导出为Excel(案例)
2014-01-09 Created By BaoXinjian