Javascript 使用knockoutjs将button类与选项控件同步

Javascript 使用knockoutjs将button类与选项控件同步,javascript,html,twitter-bootstrap,user-interface,knockout.js,Javascript,Html,Twitter Bootstrap,User Interface,Knockout.js,通常我可以想出一种方法让淘汰js做我想做的事。然而,在这种情况下,我有点挣扎,所以我向这里的社区开放这个问题 介绍 我正在使用typescript、bootstrap、knockoutjs和nodejs后端编写一个html5web应用程序 在我的UI中,所有这些都是通过knockoutJS控制的,我有一组按钮,形成一个由可选择选项组成的bootstrap 3按钮组 这个对齐的组,水平方向上给我4个按钮,但允许按钮选择的行为与一组选项按钮保持一致 一致性很重要,因为一次只能选择一个按钮,所以当单击

通常我可以想出一种方法让淘汰js做我想做的事。然而,在这种情况下,我有点挣扎,所以我向这里的社区开放这个问题

介绍 我正在使用typescript、bootstrap、knockoutjs和nodejs后端编写一个html5web应用程序

在我的UI中,所有这些都是通过knockoutJS控制的,我有一组按钮,形成一个由可选择选项组成的bootstrap 3按钮组

这个对齐的组,水平方向上给我4个按钮,但允许按钮选择的行为与一组选项按钮保持一致

一致性很重要,因为一次只能选择一个按钮,所以当单击一个按钮时,其余按钮将取消选择

这是BS3中的默认组件,如下图所示:

如图所示,选择了“丢弃”按钮,要实现此目的,必须将“活动”类添加到构成控件的内部单选元素周围的标签元素的现有类列表中。以下HTML用于创建此图像:

<div class="btn-group btn-group-justified" data-toggle="buttons">
  <label class="btn btn-primary">
    <input type="radio" name="options" id="option1" checked >Rejected
  </label>
  <label class="btn btn-primary active">
    <input type="radio" name="options" id="option2">Discarded
  </label>
  <label class="btn btn-primary">
    <input type="radio" name="options" id="option3">Held
  </label>
  <label class="btn btn-primary">
    <input type="radio" name="options" id="option3">Header Added
  </label>
</div>
这些支持字段中的每一个都是一个标准的布尔值,其中包含true/false,我不知道如何在封闭标签上添加/删除“active”类,以反映选择了哪些状态

更确切地说,我想不出最好的方法来优雅地做这件事

我试过的方法 我所知道的有效方法是向返回的每个标签添加一个简单的计算可观测值

"btn btn-primary active"
当该选项设置为true时,以及

"btn btn-primary"
当它不是

我知道这一点,因为在我的视图模型中,我有一个简单的函数:

SenderDialogViewModel.prototype.isRejectSelected = function () {
        if (this.reject == true) {
            return "btn btn-primary active";
        }
        return "btn btn-primary";
    };
this.selectedOptionString = ko.computed({
    read: function () {
        if (this.reject())
            return "reject";
        if (this.discard())
            return "discard";
        if (this.hold())
            return "hold";
        if (this.addheader())
            return "addheader";
    },
    write: function (value) {
        console.log("rejectstr:");
        console.log(value);
        if (value == "reject") {
            this.reject(true);
            this.discard(false);
            this.hold(false);
            this.addheader(false);
        }
        if (value == "discard") {
            this.reject(false);
            this.discard(true);
            this.hold(false);
            this.addheader(false);
        }
        if (value == "hold") {
            this.reject(false);
            this.discard(false);
            this.hold(true);
            this.addheader(false);
        }
        if (value == "addheader") {
            this.reject(false);
            this.discard(false);
            this.hold(false);
            this.addheader(true);
        }
    }
}, this);
然而,这种方法意味着4个函数,每个要测试的标志一个,这使得以后很难添加新标志

我希望能够做到以下几点:

<label class="btn btn-primary" data-bind="class: isSelected(reject)">
其中,选择变量可以是视图模型中传递的任何可用标志

这里的问题是,这仅在第一次绘制UI时更新,随后对标志的更改无法更新UI以反映给定的状态

为了尝试解决这个问题,我将函数更改为computed observable,但在绘制UI时收到一个JS错误,指出computed observable必须添加一个“write”处理程序,因为我正在传入一个参数

如果我需要添加一个写处理程序,那么这很好,但我不想

总结
总之,有很多方法可以与其他选项同步更改类列表,但大多数都很混乱,我试图创建一种在添加新按钮时可以轻松扩展的方法(这一点很重要,因为一些按钮集是动态生成的),与其添加处理程序来单独检查和报告每个变量的状态,不如在一个函数调用中添加处理程序,该函数调用可以简单地添加到视图模型中并反复使用。

确定。。。就像经常发生的那样,我一发布这篇文章,我就想知道如何让它工作

这个解决方案一直盯着我看,以击倒js css绑定的形式

要引用淘汰版js文档:

css绑定向关联的DOM元素添加或删除一个或多个命名的css类。例如,如果某个值变为负值,则用红色突出显示该值非常有用

这是什么意思,如“我可以根据视图模型中变量的值,对已经存在的类集合应用或删除单个类”

因此,我的问题的答案很简单,就是:

<div class="btn-group btn-group-justified" data-toggle="buttons">
  <label class="btn btn-primary" data-bind="css: { active: reject }">
    <input type="radio" name="options" id="option1" checked >Rejected
  </label>
  <label class="btn btn-primary" data-bind="css: { active: discard }">
    <input type="radio" name="options" id="option2">Discarded
  </label>
  <label class="btn btn-primary" data-bind="css: { active: hold }">
    <input type="radio" name="options" id="option3">Held
  </label>
  <label class="btn btn-primary" data-bind="css: { active: addheader }">
    <input type="radio" name="options" id="option3">Header Added
  </label>
</div>
(使用self、this、me…或任何你用来定义淘汰模型的东西)重要的是它们是简单的可观察布尔型

您还需要一个同时具有写入和读取功能的计算可观察对象:

SenderDialogViewModel.prototype.isRejectSelected = function () {
        if (this.reject == true) {
            return "btn btn-primary active";
        }
        return "btn btn-primary";
    };
this.selectedOptionString = ko.computed({
    read: function () {
        if (this.reject())
            return "reject";
        if (this.discard())
            return "discard";
        if (this.hold())
            return "hold";
        if (this.addheader())
            return "addheader";
    },
    write: function (value) {
        console.log("rejectstr:");
        console.log(value);
        if (value == "reject") {
            this.reject(true);
            this.discard(false);
            this.hold(false);
            this.addheader(false);
        }
        if (value == "discard") {
            this.reject(false);
            this.discard(true);
            this.hold(false);
            this.addheader(false);
        }
        if (value == "hold") {
            this.reject(false);
            this.discard(false);
            this.hold(true);
            this.addheader(false);
        }
        if (value == "addheader") {
            this.reject(false);
            this.discard(false);
            this.hold(false);
            this.addheader(true);
        }
    }
}, this);
这可能会做得更优雅,但本质上,当组中的一个选项在knockout下激活时,knockout会获取“Value”属性中的任何内容,并将其作为字符串发送到视图模型中

如果将其绑定到一个简单的可观察对象,那么该可观察对象将被设置为该属性的字符串值。然而,对我来说,因为我有一系列4个标志来设置控制UI上的各种状态,所以一个链式if是合适的(一个开关或者一个查找数组或哈希表也可以)

可观察的最终结果是一次只能设置一个布尔值和一个,因为按钮中的CSS与此标志绑定,那么活动类将应用于给定的按钮,该按钮的ever设置为true

对于读取,您需要将您的标志状态转换回一个字符串,以便将其与它知道的值进行比较,因此读取的操作是相反的

对于按钮点击处理程序,您必须按照标记中所示进行内联操作,这是因为knockout保留了一些参数用于自动操作,如元素名称、事件信息和其他,我们这里不需要这些参数,所以最简单的方法是内联

然而,在lining中,它意味着您没有绑定到属性,因此您不能将它直接绑定到选项控件使用的计算可观察对象

相反,您需要做的是向视图模型添加一个小存根函数,如下所示:

<input type="radio" name="options" id="option1" checked data-bind="checked: reject">Rejected
SenderDialogViewModel.prototype.changeType = function (newType) {
    this.selectedOptionString(newType);
};
这不需要以任何方式进行观察,因为单击按钮时只会以一种方式调用它

如果你想看到完整的解决方案,我将在我的git hub页面上免费提供这是其中一部分的应用程序供人们使用,但它还没有完成

希望以上的一切对一些人来说都是有用的,我不得不承认,这比我想象的更具挑战性


Shawty

我会创建一个自定义绑定,因为这将使ViewModel与Domya分离,我上次在
this.selectedOptionString = ko.computed({
    read: function () {
        if (this.reject())
            return "reject";
        if (this.discard())
            return "discard";
        if (this.hold())
            return "hold";
        if (this.addheader())
            return "addheader";
    },
    write: function (value) {
        console.log("rejectstr:");
        console.log(value);
        if (value == "reject") {
            this.reject(true);
            this.discard(false);
            this.hold(false);
            this.addheader(false);
        }
        if (value == "discard") {
            this.reject(false);
            this.discard(true);
            this.hold(false);
            this.addheader(false);
        }
        if (value == "hold") {
            this.reject(false);
            this.discard(false);
            this.hold(true);
            this.addheader(false);
        }
        if (value == "addheader") {
            this.reject(false);
            this.discard(false);
            this.hold(false);
            this.addheader(true);
        }
    }
}, this);
SenderDialogViewModel.prototype.changeType = function (newType) {
    this.selectedOptionString(newType);
};