【问题标题】:How to access calling child element from parent function in ko.js array?如何从 ko.js 数组中的父函数访问调用子元素?
【发布时间】:2019-05-02 12:43:46
【问题描述】:

我正在使用 knockout.js foreach 循环创建一个复选框列表。我有一个可观察的数组来保存复选框的值,但是当我调用函数来检查它们的值时,this 元素指的是父元素,即 viewModel。如何访问与单击的元素相关的数组元素?

我已尝试从以下代码中删除 $parent 引用,但这会引发此错误:Message: getSampleValue is not defined

这是创建复选框的 foreach 循环:

<!-- ko foreach: sampleList -->
            <div data-bind="css: { hasValue: $parent.getSampleValue() }">
                <span data-bind="click: $parent.sampleClick, text: name"></span>
                <div data-bind="click: $parent.sampleClick" class="spot fa fa-square"></div>
            </div>
<!-- /ko -->

这些是 ko 视图模型中定义的函数:

sampleViewModel = function (data) {
   var self = this;
   self.sampleList = ko.observableArray(data.sampleList);

    self.sampleClick = function () {
       this.isChecked = !this.isChecked;
    }

    self.getSampleValue = function () {
       return this.isChecked;
    }
}

而sampleList的格式是:

{
  {
    "name": "name1",
    "isChecked": false
  },
  {
    "name": "name2",
    "isChecked": true
  }
}

预期结果:当数组中给定项的 isChecked 值发生更改并且应该更新 UI 上的值时,应调用函数 getSampleValue。从该函数返回的 isChecked 值应该是数组中正确的元素,而不是父元素。

实际结果:该函数被调用,但似乎仅在创建复选框时调用,而不是在更新 isChecked 值时调用,并且函数内的 this 是调用者的父元素,因此没有isChecked 值。

非常感谢您提供任何帮助。

编辑: 所以改变这一行:

<div data-bind="css: { hasValue: $parent.getSampleValue() }">

到这里:

<div data-bind="css: { hasValue: isChecked }">

似乎在加载时显示正确的值,但即使可观察数组的值正在更改,它们仍然不会在 UI 上更新。

【问题讨论】:

    标签: knockout.js


    【解决方案1】:

    视图模型中的函数将this 绑定到视图模型本身。您可以使用处理函数的第一个参数访问当前元素:

    sampleViewModel = function (data) {
        var self = this;
        self.sampleList = ko.observableArray(data.sampleList);
    
        self.sampleClick = function (element) {
           element.isChecked = !element.isChecked;
        }
    
        self.getSampleValue = function (element) {
           return element.isChecked;
        }
    }
    

    The "click" binding:

    在调用您的处理程序时,Knockout 将提供当前模型值作为第一个参数。如果您要为集合中的每个项目呈现一些 UI,并且您需要知道单击了哪个项目的 UI,这将特别有用。


    另外,要正确绑定到getSampleValue,您应该删除括号。

    <div data-bind="css: { hasValue: $parent.getSampleValue }">
    

    或者 - 正如你已经发现的 - 因为函数返回值而不修改它,你可以绑定到值本身:

    <div data-bind="css: { hasValue: isChecked }">
    

    【讨论】:

      【解决方案2】:

      您以错误的方式使用 Knockout。不要将属于某项的方法放入该项的父项中。让项目对自己的状态负责。

      考虑下面的例子:

      • SampleItem 类跟踪自己的名称和自己的“已检查”状态。它还提供了一种切换“已检查”状态的方法。
      • SampleApplication 类仅保留项目列表。它将作为页面的$root 视图模型。
      • 视图使用isChecked 决定Font Awesome 的CSS 类,并将点击事件透明地链接到正确的接收器。
      • 整个视图模型图由单个函数调用构建,在此过程中构建嵌套视图模型并将数据映射到可观察对象。
      • 不需要从子级到 $parent 的交叉引用。

      function SampleItem(data) {
        var self = this;
        
        self.name = ko.observable(data.name);
        self.isChecked = ko.observable(data.isChecked);
        
        self.toggleChecked = function () {
          self.isChecked(!self.isChecked());
        };
      }
      
      function SampleApplication(data) {
        var self = this;
        
        self.sampleList = ko.observableArray();
        
        // viewmodel init (i.e. data mapping)
        data.sampleList.forEach(function (itemData) {
          self.sampleList.push( new SampleItem(itemData) );
        });
      }
      
      // -------------------------------------------------------
      var rawModelData = {
        sampleList: [{
          name: "name1",
          isChecked: false
        }, {
          name: "name2",
          isChecked: true
        }]
      };
      
      var vm = new SampleApplication(rawModelData);
      ko.applyBindings(vm);
      <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous">
      <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
      
      <!-- ko foreach: sampleList -->
      <div data-bind="click: toggleChecked">
        <span data-bind="text: name"></span>
        <div class="spot fa-square" data-bind="css: {far: !isChecked(), fas: isChecked()}"></div>
      </div>
      <!-- /ko -->
      
      <hr>
      <pre data-bind="text: ko.toJSON($root, null, 2)">

      使用mapping plugin,创建嵌套视图模型并使它们与服务器数据保持同步的过程变得更加容易,尤其是当有大量数据要映射到可观察对象并且视图模型嵌套结构变得更加复杂时。

      一个简单的电话ko.mapping.fromJS(inputObj, options, target) 处理所有细节。

      function SampleItem(data) {
        var self = this;
      
        self.name = ko.observable("");
        self.isChecked = ko.observable(false);
      
        // viewmodel init (i.e. data mapping)
        ko.mapping.fromJS(data, {}, self);
        
        self.toggleChecked = function () {
          self.isChecked(!self.isChecked());
        };
      }
      
      function SampleApplication(data) {
        var self = this;
        
        self.sampleList = ko.observableArray();
        
        // viewmodel init (i.e. data mapping)
        ko.mapping.fromJS(data, SampleApplication.mapping, self);
      }
      SampleApplication.mapping = {
        sampleList: {
          create: options => new SampleItem(options.data)
        }
      };
      // -------------------------------------------------------
      var rawModelData = {
        sampleList: [{
          name: "name1",
          isChecked: false
        }, {
          name: "name2",
          isChecked: true
        }]
      };
      
      var vm = new SampleApplication(rawModelData);
      ko.applyBindings(vm);
      <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous">
      <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
      
      <!-- ko foreach: sampleList -->
      <div data-bind="click: toggleChecked">
        <span data-bind="text: name"></span>
        <div class="spot fa-square" data-bind="css: {far: !isChecked(), fas: isChecked()}"></div>
      </div>
      <!-- /ko -->
      
      <hr>
      <pre data-bind="text: ko.toJSON(ko.mapping.toJS($root), null, 2)">

      请注意,在此变体中,“取消映射”(通过 ko.mapping.toJS())对于删除映射插件添加以跟踪事物的所有属性是必要的。

      【讨论】:

        猜你喜欢
        • 2015-03-25
        • 1970-01-01
        • 2020-09-25
        • 2012-06-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-13
        • 1970-01-01
        相关资源
        最近更新 更多