Knockout.js随手记(2)

本文探讨Knockout.js中计算属性的运用,通过多个实例展示如何利用ko.computed实现数据处理与验证等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

计算属性

   konckout.js的API文档,写的极为详细和生动,透过MVVM的运作原理,开发时只需专注于定义ViewModel逻辑,不需耗费心力处理TextBox、Select的onchange、onclick等互动细节,就能达到UI元素与数据天人合一的境界。这一系列的konckout学习笔记,将逐一探讨knockout.js在常见网页情境上的应用。

  Knockout.js随手记(1)开篇已经示范过最简单的应用,为<input>与<span>加上data-bind宣告,透过ko.applyBindings()绑定到定义好的ViewModel上,当input改变输入内容,会立即反应在span。然而,有些场合数据需经加工才能使用,例如: 指定日期格式,将数字相加... 等等,此时ko.computed()便派上用场。

  使用ko.computed()时,最简单的做法是直接传入function,在其中引用其他的ViewModel属性处理运算后传回结果;因knockout具备强大的依赖关系追踪能力,能记下你引用了哪些属性,一旦被引用的属性来源改变,便会自动调用ko.computed()计算新值。 

备注:页面上不要出现两个applyBindings语句,否则数据无法显示出来

  范例1

  首先我们创建个ViewModel,然后将使用计算属性将结果返回给fullName.

 function AppViewModel() {
            this.firstName = ko.observable('Bob');
            this.lastName = ko.observable('Smith');
            this.fullName = ko.computed(function () { return this.firstName() + " " + this.lastName(); }, this);
        }
        ko.applyBindings(new AppViewModel());

现在我们做的事情就是绑定这些值

     <p>First name: <input data-bind="value: firstName"/></p>
     <p>Last name: <input data-bind="value: lastName"/></p>
     <h2>Hello,   <span data-bind="text: fullName"/>!</h2>

我们来查看下运行效果:

注意:由于this在不同的范围内又不同的含义,往往为了避免混淆,我们采用一项流行的约定,就是把这个this指向另一个习惯性的变量(self),我们的代码可以修改为如下:

复制代码
<body>
     <p>First name: <input data-bind="value: firstName"/></p>
     <p>Last name: <input data-bind="value: lastName"/></p>
     <h2>Hello, <span data-bind="text: fullName"/>!</h2>
    <script  type="text/javascript">
        function AppViewModel() {
            var self = this;
            self.firstName = ko.observable('Bob');
            self.lastName = ko.observable('Smith');
            self.fullName = ko.computed(function () { return self.firstName() + " " + self.lastName(); }, this);
        }
        ko.applyBindings(new AppViewModel());
    </script>
</body>
复制代码

范例2

    可能你和我一样在想,既然knockout支持依赖性追踪特性,那么,我可以通过更改fullName的值去动态修改first Name和 Last Name吗?

那么我们做的工作就是分解FullName的输入值。

复制代码
  <p>First name: <input data-bind="value: firstName"/></p>
     <p>Last name: <input data-bind="value: lastName"/></p>
     <h2>Hello, <input data-bind="value: fullName"/>!</h2>
    <script  type="text/javascript">
        function MyViewModel() {
            var self = this;
            self.firstName = ko.observable('Planet');
            self.lastName = ko.observable('Earth');

            self.fullName = ko.computed({
                read: function () {
                    return self.firstName() + " " + self.lastName();
                },
                write: function (value) {
                    var lastSpacePos = value.lastIndexOf(" ");
                    if (lastSpacePos > 0) { // Ignore values with no space character
                        self.firstName(value.substring(0, lastSpacePos)); // Update "firstName"
                        self.lastName(value.substring(lastSpacePos + 1)); // Update "lastName"
                    }
                },
                owner: self
            });
        }

        ko.applyBindings(new MyViewModel());
    </script>
复制代码

代码很简单,可以说也是最常见的js截取字符串的应用,我们只要注意其中的三个代理功能:read,write,owner就可以,实现的效果如下:

范例3

  这例子其实算是对范例2的一个复习,主要功能是提供金额格式的自动转换(包括精度和格式)已经垃圾字符的过滤

复制代码
<p>Enter bid price: <input data-bind="value: formattedPrice"/></p><br/>
    <script  type="text/javascript">
        function MyViewModel() {
            this.price = ko.observable(25.99);

            this.formattedPrice = ko.computed({
                read: function () {
                    return '¥' + this.price().toFixed(2);
                },
                write: function (value) {
                    // Strip out unwanted characters, parse as float, then write the raw data back to the underlying "price" observable
                    value = parseFloat(value.replace(/[^\.\d]/g, ""));
                    this.price(isNaN(value) ? 0 : value); // Write to underlying storage
                },
                owner: this
            });
        }

        ko.applyBindings(new MyViewModel());
    </script>
复制代码

    不管用户什么时候输入新价格,输入什么格式,text box里会自动更新为带有2位小数点和货币符号的数值。这样用户可以看到你的程序有多聪明,来告诉用户只能输入2位小数,否则的话自动删除多余的位数,当然也不能输入负数,因为write的callback函数会自动删除负号。

我们来查看下运行效果:  

范例4 过滤并验证用户输入

复制代码
 <p>Enter a numeric value: <input data-bind="value: attemptedValue"/>
      <span data-bind="visible:lastInputWasValid()">验证通过!</span>
  </p>
  <div data-bind="visible: !lastInputWasValid()">这不是一个合法的数字!</div>

    <script  type="text/javascript">
        function MyViewModel() {
            this.acceptedNumericValue = ko.observable(123);
            this.lastInputWasValid = ko.observable(true);

            this.attemptedValue = ko.computed({
                read: this.acceptedNumericValue,
                write: function (value) {
                    if (isNaN(value))
                        this.lastInputWasValid(false);
                    else {
                        this.lastInputWasValid(true);
                        this.acceptedNumericValue(value); // Write to underlying storage
                    }
                },
                owner: this
            });
        }

        ko.applyBindings(new MyViewModel());
    </script>
复制代码

运行效果:

依赖跟踪是如何工作的

新手没必要知道太清楚,但是高级开发人员可以需要知道为什么依赖监控属性能够自动跟踪并且自动更新UI…

事实上,非常简单,甚至说可爱。跟踪的逻辑是这样的:

 1. 当你声明一个依赖监控属性的时候,KO会立即调用执行函数并且获取初始化值。

 2. 当你的执行函数运行的时候,KO会把所有需要依赖的依赖属性(或者监控依赖属性)都记录到一个Log列表里。

执行函数结束以后,KO会向所有Log里需要依赖到的对象进行订阅。订阅的callback函数重新运行你的执行函数。然后回头重新执行上面的第一步操作(并且注销不再使用的订阅)。

3.最后KO会通知上游所有订阅它的订阅者,告诉它们我已经设置了新值。

4.所有说,KO不仅仅是在第一次执行函数执行时候探测你的依赖项,每次它都会探测。举例来说,你的依赖属性可以是动态的:依赖属性A代表你是否依赖于依赖属性B或者C,这时候只有当A或者你当前的选择B或者C改变的时候执行函数才重新执行。你不需要再声明其它的依赖:运行时会自动探测到的。

另外一个技巧是:一个模板输出的绑定是依赖监控属性的简单实现,如果模板读取一个监控属性的值,那模板绑定就会自动变成依赖监控属性依赖于那个监控属性,监控属性一旦改变,模板绑定的依赖监控属性就会自动执行。嵌套的模板也是自动的:如果模板X render模板 Y,并且Y需要显示监控属性Z的值,当Z改变的时候,由于只有Y依赖它,所以只有Y这部分进行了重新绘制(render)。

范例5

  knocut自动依赖性跟踪通常不正是您想要。但你有时可能需要控制观测值将更新计算观察到的,尤其是如果计算可观察执行某种操作,如一个Ajax请求。peek函数,可以让你访问到监控属性或计算属性,而无需创建一个依赖。

复制代码
ko.computed(function() {
    var params = {
        page: this.pageIndex(),
        selected: this.selectedItem.peek()
    };
    $.getJSON('/Some/Json/Service', params, this.currentPageData);
}, this);
复制代码

其中selectItem属性使用了Peek函数,所以计算属性会随时监控和更新PageIndex的值,但它忽略更改selectItem。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值