基本剔除 child 更改保存 parent

Basic knockout Save parent on child changes

我有一个基本的淘汰赛问题: 我有一个 objects 的列表,格式如下:

 [{ Name: "Colors1", Values: ["red", "yellow", "blue"] },
 { Name: "Colors2", Values: ["red", "pink", "blue"] },
 { Name: "Colors3", Values: ["black", "white", "green"] }]);

我想要一个带有颜色名称的下拉菜单。在选择更改时,值中的颜色应显示在文本框中。 当文本框中的颜色发生变化时,整个记录应立即更新。

这是我的资料:

            <div> 
                    <select data-bind="options:myitems, optionsText:'Name', optionsValue:'Name', optionsCaption:'Please select a color', value:selectedColor"></select>

                    <table>
                        <thead>
                            <tr>
                                <th>Color</th>
                            </tr>
                        </thead>
                        <tbody data-bind="foreach:listofColors()?  ">
                            <tr>
                                <td><input data-bind="value:Value, event: { change : $parent.SaveUpdatedColors($root)?}" /></td>
                            </tr>
                        </tbody>
                    </table>
                </div>
        //-----------------------------------   // 
               <script>    
                var  viewModel= function() {
                        var self = this;
                        self.selectedColorChoice = ko.observable();
                        self.listofColors = ko.observableArray();

                        self.myitems = ko.observableArray(
                            [{ Name: "Colors1", Values: ["red", "yellow", "blue"] },
                            { Name: "Colors2", Values: ["red", "pink", "blue"] },
                            { Name: "Colors3", Values: ["black", "white", "green"] }]);
                var getByName = function (items, name) {
                return ko.utils.arrayFirst(items, function (item) {
                    return item.Name === name;
                              });
                           };


   self.selectedColor.subscribe(function(item) //NOT WORKING
    {
        self.listofColors = ko.computed(function () {
            var selColor = getByName(self.myitems(), self.selectedColor);
            return selColor ? ko.utils.arrayMap(selColor.Values, function (item) {
                return {
                    Name: item.Name,
                    Value: item.Value
                };
            }) : [];
        }, this);
    })
                        })

    self.SaveUpdatedColors=function(fullRecord)
    { //logic to update}
                    }
                    ko.applyBindings(new viewModel());
            </script>

我该如何进行?提前致谢。

在我们开始之前:你的代码片段中有很多语法错误,还有很多根本没有用到的东西。我冒昧地移动了一些东西并删除了一些代码.

您想为颜色字符串创建双向数据绑定,但您正在使用字符串和数组渲染普通对象;这些将不支持通过 value 数据绑定进行更新。

正在创建第二层视图模型

做事的"knockout way"就是为每一个要渲染和交互的item创建一个viewmodel。因此,对于您的情况,我创建了一个 ColorItem 视图模型。目前,该项目仅包含一个 name 和一个 values 列表(颜色的字符串名称)。为了可读性,我将字符串包装在一个普通对象中,这样我们就可以通过键入 value: value 进行数据绑定,而不必使用 $data.

存储选择并绑定到它

现在有了 ColorItem 的列表,我们可以在我们的 selectedColor 可观察对象中放置一个 ColorItem 实例;请注意,您不需要选择 属性 来存储。您可以存储整个项目!

<table> 现在使用 with: selectedColor 绑定绑定到 selectedColor。这意味着 table 的上下文是一个具有 namevalues 属性.

的对象

正在订阅更改;存储状态

最后,为了能够对 any 颜色项目的 any 变化做出反应,我们计算了一个 "state":反映所做的所有更改的普通 javascript 对象。 (请注意,如果您打算创建一个非常大的应用程序,这可能太昂贵了)。

对于这个例子,状态只不过是所有可观察属性的展开版本。通过获取 ko.computedko.pureComputed 中的可观察值的值,knockout 会自动为您创建对任何未来更改的订阅。

我对下面示例中的一些代码进行了注释。让我知道您是否理解所有这些内容以及它是否回答了您所有的问题。

注意:部分代码假定您需要添加其他功能,例如更改名称和添加更多颜色。如果不需要,可以用常规值替换某些 observableobservableArray 属性。您也可以删除 "interim state" 并只输出您想要的对象格式。

// Our starting data
var colors = [{
  Name: "Colors1",
  Values: ["red", "yellow", "blue"]
}, {
  Name: "Colors2",
  Values: ["red", "pink", "blue"]
}, {
  Name: "Colors3",
  Values: ["black", "white", "green"]
}];

var ViewModel = function() {
  var self = this;

  // Create ColorItem viewmodels
  self.listOfColors = ko.observableArray(
    colors.map(ColorItem.createFromData));
  self.selectedColor = ko.observable();

  // Computed state, holds name of current selection and list of items
  self.colorState = ko.pureComputed(function() {
    return {
      selectedColorName: self.selectedColor() ? self.selectedColor().name() : null,
      colorStates: self.listOfColors().map(function(colorItem) {
        return colorItem.state();
      })
    };
  });
  
  // By subscribing to the computed state, we can trigger any other methods
  self.colorState.subscribe(function(newState) {
    // If you want to save to server, you'll need to rate limit updates
    // and abort previous posts
    
    // To convert back to your original format:
    console.log(JSON.stringify(
      newState.colorStates.map(function(itemState) {
        return { Name: itemState.name, Values: itemState.values };
      }))
    );
  });
};


var ColorItem = function(name, values) {
  this.name = ko.observable(name);
  this.values = ko.observableArray(values.map(function(str) {
      return {
        // By wrapping the string in an observable, we can bind 
        // the input's value to it
        value: ko.observable(str)
      };
  }));

  // Color items hold their own state: name and a list of strings
  this.state = ko.pureComputed(function() {
    return {
      name: this.name(),
      values: this.values().map(function(valueObj) {
        return valueObj.value();
      })
    };
  }, this);
};

// A helper function to transform a {Name, Values} object in to a viewmodel
ColorItem.createFromData = function(data) {
  return new ColorItem(data.Name, data.Values);
};

ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div>
  <select data-bind="options:listOfColors, 
                     optionsText:'name', 
                     optionsCaption:'Please select a color',    
                     value:selectedColor"></select>

  <table data-bind="with: selectedColor">
    <thead>
      <tr>
        <th>Color</th>
      </tr>
    </thead>
    <tbody data-bind="foreach:values">
      <tr>
        <td>
          <input data-bind="textInput: value" />
        </td>
      </tr>
    </tbody>
  </table>
</div>