使用 java 脚本(SIMPLIFIED EX ADDED)在模板中为 ko 绑定输入设置选项选择

set an option selection for a ko bound input in a template USING a java script (SIMPLIFIED EX ADDED)

更新了一个简化的示例,请参阅第二组代码和简化的 FIDDLE

我有一个带有 ko 模板的页面。页面 '$(document).ready(function()' 根据使用页面 url 参数从数据库中提取的数据进行一系列处理。该处理会生成一个 dbcolumn 名称字符串。从本文档中准备好我尝试在 ko 模板中为 ko 绑定输入设置选项 selection。

我在下面的 fiddle 示例中使用输入框模拟了这一点。

当 fiddle 页面运行列表一时加载并自动选择第一个选项,然后根据该 selection 调整列表二选项。这是按设计工作的。但是,当我在输入框中输入“fname”并按回车键时,列表一 selected 选项变为空白(希望它为 select "first name")并且列表二选项列表没有改变.

我确实尝试使用列表一中的 [optionsValue: 'dbcolumn'] 绑定值(见注释掉的 fiddle 代码),但这只会让事情变得更糟。我还尝试将 selection 值设置为对象 '{ "dbcolumn": "fname", "display": "first name", "fieldtype": "Text" }' 来匹配绑定元素,但这也不起作用。

Fiddle 示例:https://jsfiddle.net/bkhummingbird/r0kdL3sb/

我很感激你能给我解决这个问题的任何帮助或指示。 :)

html代码...

<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~MODEL~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<div class="container">
  <div data-bind="with: group">
    <div data-bind="template: templateName"></div>
  </div>
</div>
<div>
  1-db column name to pick from List One:
  <input id='strgToSelect' onchange='updateListOnePick();'/>
  <br/>
  <!-- 2-db column name to pick from List One:
  <input id='strgToSelect' onchange='updateListOnePick2();'/> -->
</div>

<!-- ~~~~~~~~~~~~~~~group-template~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<script type="text/html" id="group-template">
<br>
<u>This part works, you see it happen witht he inital page load</u>
<br/>
The intent of this page is that when <b>List One</b> selection changes <b>List Two</b> options reload with just the options from the get_jsonAttrList() call that have the same "fieldtype" as <b>List One</b>'s selection.
<br><br>
<u>This part FAILS, you see it fail by entering a column name like '<b>fname</b>' in the input box.'</u>
<br/>
The <b>'db column name to pick from List One:'</b> input should set the <b>List One</b> combo box selection on change which should inturn reload the <b>List Two</b> options.  
<br><br><b>**In my actual application the db column name will come from a parsed string from the database.
</b>
<br>
<br>
  <div class="alert alert-warning alert-group" style="border-width: 5px;border-color:blue;">
    List One: 
    <select data-bind="options: attributesAvailable,     
                     optionsText: 'display', 'fieldtype', 
                     value: compareAttrObject
                    " id='attrOne'>
    </select>
    <!-- this attempt made things worse:
                <select data-bind="options: attributesAvailable,     
                    optionsValue: 'dbcolumn', 
                     optionsText: 'display', 'fieldtype', 
                     value: compareAttrObject
                    " id='attrOne'>
    </select> -->
    <br>
    List Two:  
    <select data-bind="options: attributesAvailable2(), 
                     optionsText: 'display', 'fieldtype', 
                     value: compareToAttrObject
                    ">        
    </select>
  </div>
</script>


<script>
  window.addEventListener('load', function() {
    ko.applyBindings(new QueryBuilder.ViewModel());
  }, true);

</script>

js代码...

function updateListOnePick(){
    document.getElementById('attrOne').value = document.getElementById('strgToSelect').value
}
/* function updateListOnePick2(){
  document.getElementById('attrOne').value = '{"dbcolumn": "Apply_d", "display": "Application date", "fieldtype": "Date" }';
} */

function get_jsonAttrList() {
  return [{
      "dbcolumn" : "fid",
      "display" : "id #",
      "fieldtype" : "Number"
    }, {
      "dbcolumn": "fname",
      "display": "first name",
      "fieldtype": "Text"
    }, {
      "dbcolumn": "Apply_d",
      "display": "Application date",
      "fieldtype": "Date"
    }, {
      "dbcolumn": "location",
      "display": "location",
      "fieldtype": "Text"
    }, {
      "dbcolumn": "press_in",
      "display": "press in",
      "fieldtype": "Text"
    }, {
      "dbcolumn": "node1",
      "display": "node count",
      "fieldtype": "Number"
    }, {
      "dbcolumn": "comments",
      "display": "comments",
      "fieldtype": "Text"
    }, {
      "dbcolumn": "reg1_posname",
      "display": "Position Name",
      "fieldtype": "Text"
    }, {
      "dbcolumn": "reg1_inst_",
      "display": "inst count",
      "fieldtype": "Number"
    }, {
      "dbcolumn": "create_d8",
      "display": "create #",
      "fieldtype": "Number"
    }, {
      "dbcolumn": "mapped_d8",
      "display": "mapped #",
      "fieldtype": "Number"
    }, {
      "dbcolumn": "term_d",
      "display": "terminated date",
      "fieldtype": "Date"
    }, {
      "dbcolumn": "start",
      "display": "emp start date",
      "fieldtype": "Date"
    }]
}


//~~~~~~~~~~~~~~~~~~~~~~group~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
window.QueryBuilder = (function(exports, ko) {
  var Condition = exports.Condition;
  /**
   * creates an instants of the group template.
   */
  function Group() {
    var self = this;
    self.templateName = 'group-template';


    self.compareAttrObject = ko.observable();
    self.attributesAvailable = ko.observableArray(get_jsonAttrList());
    self.compareToAttrObject = ko.observable("");
    self.attributesAvailable2 = ko.observableArray(new Array());

    self.compareAttrObject.subscribe(function() {
      var tempAttrList = get_jsonAttrList();
      if (tempAttrList != null && tempAttrList.length > 0) {
        if (self.compareAttrObject() != undefined && self.compareAttrObject() != null) {
          alert("Length of list two BEFORE list one changed to ["+
                    self.compareAttrObject().fieldtype+
                    "]..." + self.attributesAvailable2().length);

                self.attributesAvailable2.removeAll();

          for (var i = 0; i < tempAttrList.length; i++) {
            if (tempAttrList[i].fieldtype == self.compareAttrObject().fieldtype) {
              self.attributesAvailable2.push(tempAttrList[i]);
            }
          }
          alert("Length of list two AFTER list one changed to ["+
                    self.compareAttrObject().fieldtype+
                    "]..." + self.attributesAvailable2().length);
        }
      }
    });

  }
  exports.Group = Group;
  return exports;
})(window.QueryBuilder || {}, window.ko);

//~~~~~~~~~~view model~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
window.QueryBuilder = (function(exports, ko) {

  var Group = exports.Group;

  function ViewModel() {
    var self = this;
    self.group = ko.observable(new Group());
  }

  exports.ViewModel = ViewModel;
  return exports;

})(window.QueryBuilder || {}, window.ko);

* 更新了简化示例 *


Simplified Fiddle Example of same problem

简化HTML:

<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~MODEL~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<div class="container">
  <div data-bind="with: group">
    <div data-bind="template: templateName"></div>
  </div>
</div>

<!-- ~~~~~~~~~~~~~~~group-template~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<script type="text/html" id="group-template">
 <center> (Simplified Try)</center><br>
  <u>This part FAILS</u>
  <br/> Press the <b>[Try]</b> button, which should change the selection in <b>List One</b> to be the option tied to '"dbcolumn": "comments1"' via the hard coded entry in the updateListOneSelection function. In turn the column name.type text next to the combo box should change to comments1.text.
  <br><br>
  <div>
    <button data-bind='click:updateListOneSelection'>Try</button>
  </div>
  <div class="alert alert-warning alert-group" style="border-width: 5px;border-color:blue;">
    <b>List One: </b>
    <select data-bind="options: attributesAvailable,   
                     optionsText: 'display', 'fieldtype', 
                     value: compareAttrObject
                    " id='attrOne'>
    </select>
    <b>Column Name.type: </b>
    <span data-bind="text:compareAttrObject().dbcolumn + '.' + compareAttrObject().fieldtype"></span>
  </div>
</script>

<script>
  window.addEventListener('load', function() {
    ko.applyBindings(new QueryBuilder.ViewModel());
  }, true);
</script>

简化的 JS:

function get_jsonAttrList() {
  return [{
    "dbcolumn": "fid",
    "display": "id #",
    "fieldtype": "Number"
  }, {
    "dbcolumn": "fname",
    "display": "first name",
    "fieldtype": "Text"
  }, {
    "dbcolumn": "location",
    "display": "location",
    "fieldtype": "Text"
  }, {
    "dbcolumn": "comments1",
    "display": "comments",
    "fieldtype": "Text"
  }, {
    "dbcolumn": "start",
    "display": "emp start date",
    "fieldtype": "Date"
  }]
}

//~~~~~~~~~~~~~~~~~~group~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~creates an instants of the group template.
window.QueryBuilder = (function(exports, ko) {
  var Condition = exports.Condition;

  function Group() {
    var self = this;
    self.templateName = 'group-template';
    self.compareAttrObject = ko.observable();
    self.attributesAvailable = ko.observableArray(get_jsonAttrList());
    self.updateListOneSelection = function() {
      alert("Will now try to set List One to the Comments1 column selection.");
      self.compareAttrObject({
        "dbcolumn": "comments1",
        "display": "comments",
        "fieldtype": "Text"
      });
    };
  }
  exports.Group = Group;
  return exports;
})(window.QueryBuilder || {}, window.ko);

//~~~~~~~~~~view model~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
window.QueryBuilder = (function(exports, ko) {
  var Group = exports.Group;

  function ViewModel() {
    var self = this;
    self.group = ko.observable(new Group());
  }
  exports.ViewModel = ViewModel;
  return exports;
})(window.QueryBuilder || {}, window.ko);

这个问题的解决方案可以在我的 fiddle 中看到:fiddle of the solution

我必须在模板中做以下工作:

1. 设置 optionAfterRender 函数以将 data-dbcolumn 添加到选项的 dom。

2. 然后按钮按下操作 (updateListOneSelection) 能够查看每个选项中的 data-dbcolumn 值以找到 'comments1' 列选项。

3. 使用它找到的索引,然后我可以设置 selectedIndex 值。

4. 这使选择发生了变化,但未能触发对组合框后面的 "Column Name.type" 值文本的数据绑定更新。为了让数据绑定到更新,我必须添加 $('#attrOne').trigger('change');行。

html...

<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~MODEL~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<div class="container">
  <div data-bind="with: group">
    <div data-bind="template: templateName"></div>
  </div>
</div>

<!-- ~~~~~~~~~~~~~~~group-template~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<script type="text/html" id="group-template">
  <center> (--Fixed--)<br>
  pressing the [Try] button will reset the Option-list selection to "comments".  The Column Name.type value will always update to the data for the selected value in the Option-list wether that option changed via selecting it or pressing the button.
  </center>
  <br><br>
  <div>
    <button data-bind='click:updateListOneSelection'>Try</button> sets selection to 'comments' using the dbcolumn value.
  </div>
  <div style="border-style: solid;border-width: 2px;border-color:blue;">
    <b>Option-list: </b>
    <select data-bind="options: attributesAvailable,   
                     optionsText: 'display', 'fieldtype', 'dbcolumn',
                     optionsAfterRender: setOptionDataAttrs,
                     value: compareAttrObject
                    " id='attrOne'>
    </select>
    <br><b>Column Name.type: </b>
    <span data-bind="text:compareAttrObject().dbcolumn + '.' + compareAttrObject().fieldtype"></span>
  </div>
</script>

<script>
  window.addEventListener('load', function() {
    ko.applyBindings(new QueryBuilder.ViewModel());
  }, true);

</script>

JS...

function get_jsonAttrList() {
  return [{
    "dbcolumn": "fid",
    "display": "id #",
    "fieldtype": "Number"
  }, {
    "dbcolumn": "fname",
    "display": "first name",
    "fieldtype": "Text"
  }, {
    "dbcolumn": "location",
    "display": "location",
    "fieldtype": "Text"
  }, {
    "dbcolumn": "comments1",
    "display": "comments",
    "fieldtype": "Text"
  }, {
    "dbcolumn": "start",
    "display": "emp start date",
    "fieldtype": "Date"
  }]
}

//~~~~~~~~~~~~~~~~~~group~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~creates an instants of the group template.
window.QueryBuilder = (function(exports, ko) {

  function Group() {
    var self = this;
    self.templateName = 'group-template';
    self.compareAttrObject = ko.observable();
    self.attributesAvailable = ko.observableArray(get_jsonAttrList());
    self.updateListOneSelection = function() {
      var cnt = -1;
      $("#attrOne > option").each(function() {
        cnt++;
        //also works//var tmp = this.getAttribute('data-dbcolumn'); 
        var tmp = $(this).data('dbcolumn');
        console.log(this.text + '/' + this.value + ' - ' + tmp);

        if (tmp == 'comments1') {
          document.getElementById('attrOne').selectedIndex = cnt;
          $('#attrOne').trigger('change');
        }
      });
    };
    self.setOptionDataAttrs = function(option, item) {
      $(option).attr('data-dbcolumn', item.dbcolumn); // this  show up on DOM.
      $(option).attr('data-fieldtype', item.fieldtype); // this  show up on DOM.
    };
  }
  exports.Group = Group;
  return exports;
})(window.QueryBuilder || {}, window.ko);

//~~~~~~~~~~view model~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
window.QueryBuilder = (function(exports, ko) {
  var Group = exports.Group;

  function ViewModel() {
    var self = this;
    self.group = ko.observable(new Group());
  }
  exports.ViewModel = ViewModel;
  return exports;
})(window.QueryBuilder || {}, window.ko);