【问题标题】:Multiple dependent selects in Knockout JSKnockout JS 中的多个依赖选择
【发布时间】:2017-12-30 22:22:19
【问题描述】:

我正在尝试让一系列依赖选择在 Knockout JS 中工作。我可以在this 线程之后让一个工作,但我正在努力让多个操作。

这个想法是有主题和级别,并允许用户一次选择一个主题和一个(或多个)级别。

例如:

用户在主选择中选择“数学”。然后,从属选择将选项更改为“微积分、代数、几何”。用户选择三个中的两个。

到目前为止已经足够简单了。

然后用户想要选择一对额外的主题和级别。因此,单击添加另一行主要和从属选择元素的按钮。用户选择“英语”,从属选择将选项更改为“语言、文学、历史”,因此他们也可以从中进行选择。

现在有两行选择。一个是数学科目,另一个是英语科目。

我已尝试在 jsfiddle 上进行此操作,但由于某些原因,我不完全确定它甚至不会加载。

self.subjLevList = ko.observableArray([
    {subject: "Math", levels: ["Calculus", "Algebra"]},
    {subject: "English", levels: ["Language", "Literature"]} ]);

我已经盯着这个看了很长时间,我可能错过了一些明显的东西,但如果有人可以帮助我,那将不胜感激:)

【问题讨论】:

    标签: knockout.js


    【解决方案1】:

    修复参考错误

    由于我不完全确定这个东西甚至不会加载的原因

    您的浏览器开发者工具是您的朋友。如果您的视图无法呈现,knockout 通常会在控制台中提供方便的错误消息。在你的情况下,第一个是这样的:

    未捕获的引用错误:无法处理绑定foreach: function (){return subjLevVM.subjLevList } 消息:subjLevVM 未定义

    这意味着您的绑定上下文中有问题:没有名为 subjLevVM 的属性,因此无法访问数组 subjLevList

    这是一个fiddle,已修复所有参考错误。如果您觉得很难找到这些,请查看this answer I wrote earlier

    实现所描述的行为

    您的目标行为并不容易创建。我在你当前的小提琴中开始了一个实现,但不得不改变太多以至于它没有变得更清晰......

    最终,我在RowsLabsoptions 的系列中重新实现了该结构。

    • 只有在仍有Labs 未使用时才能添加行
    • 更改 Row 的实验室时,您只能从未使用的实验室中进行选择

    每一层都使用computed 属性来限制选项。如果代码的任何部分不清楚或难以理解,请告诉我,我很乐意进一步澄清。

    var Lab = function(name, id, options, labsInUse) {
      this.name = name;
      this.id = id;
      
      this.options = options;
      this.selectedOptions = ko.observableArray([]);
      
      this.disabled = ko.pureComputed(function() {
        return labsInUse().some(function(thatId) {
          return thatId === id;
        });
      });
    };
    
    Lab.fromData = function(d, idsInUse) {
      return new Lab(d.lab_name, d.id, d.lab_options.split(","), idsInUse);
    }
    
    var Row = function(labs) {
      this.labs = labs;
      
      this.selectedLab = ko.observable(labs.find(function(l) {
          return !l.disabled();
        })
      );
      
      this.setOptionDisable = function(option, item) {
        ko.applyBindingsToNode(option, { disable: item.disabled }, item);
      }
    };
    
    var App = function(courseData, initialSelection) {
    
      this.rows = ko.observableArray([]);
    
      // Every lab can only be added once
      var labIdsInUse = ko.pureComputed(function() {
        return this.rows().map(function(r) {
          return r.selectedLab() && r.selectedLab().id;
        });
      }, this);
      
      var labs = courseData.map(function(d) {
        return Lab.fromData(d, labIdsInUse)
      });
      
      // Display a report of selections
      this.selection = ko.pureComputed(function() {
        return this.rows()
          .map(function(r) {
            var lab = r.selectedLab();
            return lab.name + ", with subjects: " + lab.selectedOptions().join(", ");
          });
      }, this);
      
      this.canAdd = ko.pureComputed(function() {
        return labs.length > labIdsInUse().length;
      }, this);
      
      this.addRow = function() {
        this.rows.push(new Row(labs));
      }.bind(this);
      
      this.removeRow = function(row) {
        this.rows.remove(row);
      }.bind(this);
      
      // Set initial selection
      Object.keys(initialSelection)
        .map(function(labName) {
          var row = new Row(labs);
          var labToSelect = labs.find(function(lab) {
            return lab.name === labName;
          });
          
          // Set options
          labToSelect.selectedOptions(
            initialSelection[labName]
          );
          
          // Add lab to row
          row.selectedLab(labToSelect);
          
          // Add row to app
          this.rows.push(row);
          
        }.bind(this));
    };
    
    ko.applyBindings(
      new App(
        getCourseData(),
        {"Math": ["Calculus", "Algebra"], "English": ["Language"]}  
      )
    );
    
    
    function getCourseData(){return[{id:"1003",lab_name:"Math",lab_index:"1",lab_options:"Calculus,Algebra,Geometry"},{id:"1004",lab_name:"English",lab_index:"2",lab_options:"Language,Literature,History"},{id:"1005",lab_name:"History",lab_index:"3",lab_options:"Ancient,Modern"}]}
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
    <div data-bind="foreach: rows">
      <div>
        <select data-bind="options: labs, optionsText: 'name', value: selectedLab, optionsAfterRender: setOptionDisable"></select>
        <!-- ko with: selectedLab -->
        <select data-bind="options: options, selectedOptions: selectedOptions" multiple></select> 
        <!-- /ko -->
        <button data-bind="click: $parent.removeRow">delete</button>
      </div>
    </div>
    
    <button data-bind="click: addRow, enable: canAdd">add lab</button>
    
    <h3>Selection:</h3>
    <ul data-bind="foreach: selection">
      <li data-bind="text: $data"></li>
    </ul>

    【讨论】:

    • 哇,非常好的解决方案 - 非常感谢!也感谢您修复参考错误的小提琴。一旦我看到你的修复,一切都变得清晰了。我陷入了困境,无法直视。
    • 我正在努力弄清楚如何设置选择的初始状态。假设我们从 {"Math": ["Calculus", "Algebra"], "English": ["Language"]} 这样的东西开始,你将如何加载行 observableArray?
    • 我添加了将初始选择传递给示例的选项。我们首先根据课程数据创建所有个实验室。然后,我们使用find 比较名称找到与选择相对应的实验室。一旦我们有了要查找的lab,我们添加选项并注入一行。
    • 做到了!再次感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 2011-01-24
    • 1970-01-01
    • 2018-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-31
    相关资源
    最近更新 更多