【问题标题】:Radio buttons bound to knockout computed绑定到淘汰赛计算的单选按钮
【发布时间】:2016-02-03 11:52:48
【问题描述】:

我认为,knockoutjs writables 计算 observables 存在一些问题。
我创建了一个fiddle

我需要的其实没那么难:
我有可空的 WeightInGrammsVolumeInMilliliters 值。
这些值应该绑定到两个输入字段(其中只有一个应该是可见的)。
在顶部,用户可以选择他想与单选按钮一起使用的这些值中的哪一个。

在初始化时,当它们都为空时,应选中“g”单选按钮,当WeightInGramms 不为空时也是如此。当VolumeInMilliliters 有一些值时,应该选中“ml”单选按钮。

我为此使用了 knockoutjs 可写计算可观察,如果有更好的方法,请纠正我!

所以,当我更改绑定到WeightInGrammsVolumeInMilliliters 的输入值时,读取功能似乎起作用了。但是当我更改单选按钮时,什么也没有发生......

var ViewModel = function (data) {
    var self = this;
    this.VolumeInMilliliters = ko.observable(data.VolumeInMilliliters);
    this.WeightInGramms = ko.observable(data.WeightInGramms);

    this.GrammIsSelected = ko.computed({
        read: function() {
            return (!self.WeightInGramms() && !self.VolumeInMilliliters()) || !self.VolumeInMilliliters();
    },
        write: function (newValue) {
            console.log(newValue);
            return newValue;
        },
        owner: this
    });

};

当我更改单选按钮时,相应的输入字段应该是可见的:

<div data-bind="visible: GrammIsSelected">g is active</div>
<div data-bind="visible: !GrammIsSelected()">ml is active</div>

编辑:
第一次加载表单时,两个值都将为空 -> 应选中“g”按钮。

observables 可以通过以下方式初始化:

null, null  
33, null 
null, 33

两者都可以为空,但其中只有一个可以有值。 如果用户键入一个值,然后单击另一个单选,则该值可以应用于另一个值。

我希望它更清楚一点

【问题讨论】:

  • 你真的想做什么伙计?你的代码和你的问题真的不清楚......这次避免技术细节,只是简单的英语,你的用户界面应该做什么?
  • 好的@AdrianSalazar,我更新了小提琴和问题。也许它更清楚一点......:-/
  • 好吧...好多了...小菜一碟!
  • 请清楚地描述行为:第一次加载表单时会发生什么? observables 是如何初始化的?如果用户写入一个值然后单击另一个收音机会发生什么?如果用户再次键入另一个收音机并在双方都键入值会发生什么?您需要以一种非常不同的方式来面对这个问题:获取一个单位的值,仅此而已,一切都会更加清晰

标签: javascript knockout.js


【解决方案1】:

一些提示:

  • 使您的 viewModel (JS) 尽可能地类似于视图 (HTML)。此外,这避免了重复过多的标记。在这种情况下,单选按钮始终是列表,因此将选项存储在数组中最为方便。
  • 你应该定义一个selected observable 来保存所选指标,而不是测试GrammIsselected。这样,如果您添加更多选项,代码仍然可以在不重构的情况下工作。
  • 何时使用计算属性?计算属性通过基于多个可观察/变量计算结果来添加 readonly 值。可写计算属性的作用相同,只是您可以写回更改。这使得它对于“全选”样式的复选框(参见 example 2 in the docs)、数据验证和转换特别有用。

您想要实现的绝对最清晰的设置如下:

var ViewModel = function (data) {
    this.metrics = [
        { name: 'g',  value: ko.observable(data.WeightInGramms) },
        { name: 'ml', value: ko.observable(data.VolumeInMilliliters) }
    ];
    this.selectedMetric = ko.observable(this.metrics[0]);
};

通过将对象设置为可观察 (selectedMetric),您可以进一步简化体积/重量输入的标记:

<div class="control-group">
    <label class="control-label">choose</label>
    <div class="controls" data-bind="with: selectedMetric">
        <input type="text" data-bind="value: value">
        <span class="help-inline" data-bind="text: '(' + name + ')'"></span>
    </div>
</div>

获取应用的“最终价值”就像检索 selectedMetric().value() 一样简单。

计算属性在这里并不是很有用,但是例如,如果您想为用户提供一种方法来设置 g/ml 与单选按钮和文本,您可以将以下方法添加到您的 viewModel:

this.selectedMetricByText = ko.computed({
    read: function() {
        return this.selectedMetric().name;
    },
    write: function(value) {
        var newMetric = ko.utils.arrayFirst(this.metrics, function(metric) {
            return metric.name === value;
        }) || false;
        this.selectedMetric(newMetric || this.metrics[0]);
    }
}, this);

Fiddle

【讨论】:

  • 这是一个很好的建议。它比我所做的更偏离问题中的原始设计,但为了更好的 IMO。 OP,如果您能够重新设计事物,请选择这个答案而不是我之前的建议。
【解决方案2】:

您的 write 函数似乎没有写入任何内容?

this other answer相反,根据我的经验,我会给你建议不要避免可写的计算:明智地使用它们会非常有效!

注意:在我的回答中,我尝试与问题的原始设计保持接近,但如果您能够(有可用资源),我建议您根据the answer by @Tyblitz 重新设计更多内容。

这是您可以使用计算的方法来解决此问题的方法:

var ViewModel = function (data) {
  var self = this;
  
  self.VolumeInMilliliters = ko.observable(data.VolumeInMilliliters);
  self.WeightInGramms = ko.observable(data.WeightInGramms);

  var _measurementType = ko.observable("volume");

  self.MeasurementType = ko.computed({
    read: function() {
      return _measurementType();
    },
    write: function (newValue) {
      _measurementType(newValue);
      self.VolumeInMilliliters(newValue === "volume" ? 0 : null);
      self.WeightInGramms(newValue === "mass" ? 0 : null);
    }
  });
};

ko.applyBindings(new ViewModel({ VolumeInMilliliters: 12 }));
label { cursor: pointer; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<label>
  <input type="radio" name="measurementType" value="volume" data-bind="checked: MeasurementType">
  Volume
</label>
<input data-bind="value: VolumeInMilliliters, enable: MeasurementType() === 'volume'">

<label>
  <input type="radio" name="measurementType" value="mass" data-bind="checked: MeasurementType">
  Weight in gramms
</label>
<input data-bind="value: WeightInGramms, enable: MeasurementType() === 'mass'">

【讨论】:

  • 即使@Tyblitz 的答案可能是这里最好的答案,这个答案也解决了我的问题
【解决方案3】:

对于单选按钮,您需要使用“选中”绑定。

http://knockoutjs.com/documentation/checked-binding.html

根据我个人的经验(作为一个 KO nija),我必须给你一个建议:避免可写的 ko 计算。

<input type="radio" name="unitSelector" value="g" data-bind="checked: unit" /> Grams</br>
<input type="radio" name="unitSelector" value="ml" data-bind="checked: unit" /> Millis</br>

现在是视图模型

var ViewModel = function (data) {
    var self = this;
    self.unit = ko.observable('g');
    self.userValue = ko.observable(data.WeightInGramms);
};

现在绑定应该只关心用户输入的值,这里不需要计算,也不需要两个字段...

<input type="text" data-bind="textInput: userValue ">
<span data-bind="text: unit">  </span>

它看起来真的太简单了,但这正是你所需要的,正如@Jotabe 所提到的,你应该把测量和单位作为两个独立的东西......你以后用这个东西做什么,可以用计算的 observables 来完成。

如果这件事不能解决你的问题,那么你应该说出你真正想要的是什么......

【讨论】:

  • 好的,现在非常感谢!问题是,后端响应给了我数据。所以我认为我们必须改变这一点......
  • “我必须给你建议:避免可写的ko计算。”基于什么经验/错误?我从来没有遇到过他们的麻烦..
  • @Tybitz 认真的吗?你想从我这里得到什么...不能/不会基于高度主观的问题进行烦人的讨论...
  • 不要把它当成个人,@Tyblitz 只是质疑 post 的一部分。通过在答案中添加 opinionated 部分,您已经打开了这扇门。这很好,但其他人要求您通过示例支持这样的主张并不少见。如果您添加了经验/错误/示例来表明可写计算会导致问题,那么它将改进您的帖子,从而使所有相关人员受益。
猜你喜欢
  • 2016-05-10
  • 2016-12-01
  • 2013-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-30
  • 1970-01-01
相关资源
最近更新 更多