如何使用 knockout.js 订阅变量状态更改

Posted

技术标签:

【中文标题】如何使用 knockout.js 订阅变量状态更改【英文标题】:How to subscribe to variable state change using knockout.js 【发布时间】:2020-12-06 18:48:42 【问题描述】:

这是我想要实现的一个基本的 knockout.js 小提琴:https://jsfiddle.net/sr3wy17t/

它做我想做的事,但不完全按照我想要的方式。

为了完整起见,我将在此处重复上述小提琴代码的部分内容:

在视图中,我有 for-each 迭代一个 observableArray 的项目:

<div data-bind="foreach: $root.availableItems">
 <div class="switchBox">
  <div class="switchName"><strong data-bind="text: '&nbsp;' + name()"></strong></div>
   <label class="Switch">
    <input type="checkbox" data-bind="checked: state">
   </label>
 </div>

它会遍历我的 availableItems 数组中的元素:

self.availableItems([
new Item(1, "item1", state1, self.onItemStateChange),
new Item(2, "item2", state2, self.onItemStateChange),
new Item(3, "item3", state3, self.onItemStateChange)
]);

如您所见,我还有一个函数,在该函数中我用可观察对象初始化每个项目:

function Item(id, name, state, onChange) 
  var self = this;

  self.id = ko.observable(id);
  self.name = ko.observable(name);
  self.state = ko.observable(state);

  self.state.subscribe(function(newValue) 
    onChange(self, newValue);
  );

数组中的每个项目都有状态变量(state1、state2、state3),它们是布尔值,它们控制检查哪个复选框,哪个不检查。它们(为了这个例子)设置在 ViewModel 的开头:

var state1 = true;
var state2 = false;
var state3 = false;

实际上 state1、state2 和 state3 是从服务器映射的。我想要实现的是,在我用起始状态值初始化我的项目后,我希望它们在 state1、state2 和 state3 的每次更改时都被订阅,以便根据从服务器收到的值选中或不选中复选框.

目前小提琴中的代码通过访问 availableItems 数组来实现状态更改,如下所示:

    setInterval(()=>
    var itemNoThatChanged=Math.floor(Math.random()*3);
    var newState=Math.random()>0.5;
   self.availableItems()[itemNoThatChanged].state(newState)
    ,1000)

这里的问题是,不是 state1 或 state2 或 state3 的变化导致了变化,而是直接访问 availableItems 数组....

如何更改此代码,使 state1、state2 和 state3 的更改导致上述行为像小提琴一样?

我需要尽可能减少对现有代码方法的更改,因为它会影响原始代码中的许多其他内容。

这可能吗,如果可以,有人可以解释一下如何在 knockout.js 中编写代码吗?

【问题讨论】:

【参考方案1】:

因为您希望对现有代码进行最小的更改; 将您的 state1state2state3 变量声明为 observables。

var state1 = ko.observable(true);
var state2 = ko.observable(false);
var state3 = ko.observable(false);

调整您的 Item 以按原样接受和使用这些内容,而不是设置 observable 本身。

function Item(id, name, state, onChange) 
    var self = this;

    self.id = ko.observable(id);
    self.name = ko.observable(name);
    self.state = state;

    self.state.subscribe(function(newValue) 
        onChange(self, newValue);
  );


下面的可运行示例显示,state1(可观察)变量(由计时器回调触发)的值更改也会影响复选框,而没有任何array 访问权限。


function Item(id, name, state, onChange) 
  var self = this;

  self.id = ko.observable(id);
  self.name = ko.observable(name);
  self.state = state;

  self.state.subscribe(function(newValue) 
    onChange(self, newValue);
  );


function ViewModel() 
  var self = this;
  
  var state1 = ko.observable(true);
  var state2 = ko.observable(false);
  var state3 = ko.observable(false);

  self.availableItems = ko.observableArray([]);
  self.activeItemss = ko.computed(function() 
    return self.availableItems().filter(function(item) 
      return item.state();
    );
  );
  
    self.onItemStateChange = function(item, newValue) 
    console.log("State change event: " + item.name() + " (" + newValue + ")");
  ;


  self.init = function() 
    self.availableItems([
      new Item(1, "item1", state1, self.onItemStateChange),
      new Item(2, "item2", state2, self.onItemStateChange),
      new Item(3, "item3", state3, self.onItemStateChange)
    ]);
    setInterval(()=>
      // Simulate a change of state1
        state1(!state1());
      , 1000);
  ;
 


var viewModel = new ViewModel();
ko.applyBindings(viewModel);
viewModel.init();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

<div data-bind="foreach: $root.availableItems">
  <div class="switchBox">
    <div class="switchName"><strong data-bind="text: '&nbsp;' + name()"></strong></div>
      <label class="Switch">
        <input type="checkbox" data-bind="checked: state">
      </label>
  </div>
</div>

【讨论】:

pfx,非常感谢您的回答。这就是我要找的。只有一个问题....如果 state1、state2 和 state3 是使用 ko.mapping.fromJS 映射映射的变量,是否可以使用相同的方法?在这种情况下我还应该更改其他内容吗? @johndoe 我的第一个想法是它应该按原样工作,因为 ko.mapping 实用程序只是设置了 observables(这里:state1、2 和 3)... 但我没有' ko.mapping 的使用不足以提供 100% 的保证。 尝试了上述方法。失败并出现此错误:未捕获的 TypeError: self.state.subscribe is not a function 我想更好的问题是这个问题:如何编写代码以便可以手动控制复选框,并使用从服务器端接收的状态变量......

以上是关于如何使用 knockout.js 订阅变量状态更改的主要内容,如果未能解决你的问题,请参考以下文章

如何订阅 observableArray 项目的更改

Knockout JS 订阅 2 个 observables 的方式是,如果 2 个 observables 中的任何一个发生变化,我只会收到一次通知

使用 Knockout 订阅的循环依赖

如何订阅更改变量?

Knockout JS,如何在更改可观察数组中的值时更改样式属性

使用 Knockout.js 在表单输入上切换按钮状态