【问题标题】:Basic knockout Save parent on child changes基本淘汰赛 保存父母对孩子的变化
【发布时间】:2017-01-07 13:05:19
【问题描述】:

我有一个基本的淘汰赛问题: 我有一个这种格式的对象列表:

 [{ 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>

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

【问题讨论】:

    标签: c# knockout.js viewmodel


    【解决方案1】:

    在我们开始之前:你的 sn-p 中有相当多的语法错误,还有一些根本没有使用的东西。我冒昧地移动了一些东西并删除了一些代码。

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

    创建第二层视图模型

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

    存储选择并绑定到它

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

    &lt;table&gt; 现在使用with: selectedColor 绑定绑定到selectedColor。这意味着表的上下文是一个具有name 和values 属性的对象。

    订阅更改;存储状态

    最后,为了能够对 any 颜色项中的 any 变化做出反应,我们计算了一个“状态”:一个普通的 JavaScript 对象,它反映了所有的变化制成。 (请注意,如果您打算创建一个非常大的应用程序,这可能太昂贵了)。

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

    我已经注释了下面示例中的一些代码。让我知道您是否了解所有内容并回答您的所有问题。

    注意:部分代码假定您想要添加其他功能,例如更改名称和添加更多颜色。如果不需要,可以用常规值替换一些 observable 和 observableArray 属性。您还可以删除“临时状态”,只输出您想要的对象格式。

    // 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>

    【讨论】:

    • 非常感谢您提供的详细示例。这确实有很大帮助。所以,如果我订阅了一个计算列(例如:colorState 这里) - 如果计算列中的任何值发生变化,我会收到通知吗?我试图在不同的情况下使用此逻辑,并且由于某种原因,当值更改时,计算列上的订阅不会触发。
    • ko.computed 与 observable 一样,是“可订阅的”。 (knockoutjs.com/documentation/images/fn/type-hierarchy.png) 每个用于确定computed 输出的subscribable 值都将在确定computed 何时触发其自己的订阅方面发挥作用。在 sn-p 中,计算状态是所有 ColorItem 状态的组合,这意味着如果 one ColorItem 状态发生变化,则父状态也会发生变化。有点难以解释……
    • 这是有道理的,我能够从子 observableValue 的订阅中成功订阅父的保存。我看到的一件事是:即使我设置 ko.options.deferUpdates=true 或使用 {deferred:true} 扩展计算函数,每次更改都会触发更新。有没有办法模仿一个​​可观察值的订阅,只有当字段失去焦点时才会触发?
    • 如果您使用value 绑定而不是textInput,则在blur 事件上更新可观察对象。您还可以在计算上使用扩展器来延迟通知(例如:knockoutjs.com/documentation/rateLimit-observable.html)
    • 太完美了!非常感谢.. 非常感谢您的帮助。
    猜你喜欢
    • 2012-07-31
    • 2012-03-01
    • 2017-04-12
    • 1970-01-01
    • 2013-08-21
    • 1970-01-01
    • 2012-10-08
    • 2023-03-11
    • 2015-05-06
    相关资源
    最近更新 更多