Knockout使用多个绑定更新TextArea

时间:2014-03-17 07:26:49

标签: html sql knockout.js

还在学习......我有一个区域,其参数与其他字段绑定。在这个区域内,我希望用户能够调整任何参数,或自定义编辑SQL查询,甚至粘贴自己的参数,然后单击“获取数据”来执行SQL命令。

理想情况下,此区域看起来是同一个框(使用一个textArea,交换两个textAreas,或执行此text / textArea交换)。我最大的障碍是让更新坚持下去。我怀疑它与dependentObsevable有关,但我无法打破试用/错误周期。现在,我有“获取数据”按钮,连接到下面的框中显示调整后的文本,而不是实际发送命令。

我也相信所选择的值逻辑应该作为一个函数在“selected”可观察中,但只是不确定如何实现。非常感谢您的眼睛和反馈。谢谢!

这是JSFiddle

HTML:

     <b style="width: 800px; height: 163px;" data-bind="visible: !editing(), text: queryBuilder(), click: edit">&nbsp;</b>
     <textarea style="width: 800px; height: 163px;" data-bind="visible: editing, value: queryBuilder(), hasFocus: editing, valueUpdate: 'afterkeydown'"></textarea>
     <button data-bind="click: update">Get Data</button>

查看型号:

  var giveBack = ko.observable("nothing");
  //Query Builder for the text area
  self.queryBuilder = ko.dependentObservable(function () {
      //var giveBack = ko.observable("nothing");
      if (self.selected() == 0) giveBack = self.getTransByHourQuery;
      if (self.selected() == 1) giveBack = self.getTransByHourQuery2;         
      return giveBack;
  }, this);


  self.update = function(){
      return self.query(giveBack);
  };

1 个答案:

答案 0 :(得分:0)

您需要2个单独的可观察对象,一个用于非编辑模式,另一个用于编辑模式。

  self.rawQuery = ko.observable();

  self.queryBuilder = ko.computed(  function() {
      var q1 = self.getTransByHourQuery();
      var q2 = self.getTransByHourQuery2();
      var raw = self.rawQuery();

      var selected = self.selected();

      switch( selected ) {
          case "0":
              // Set current "raw" to this query
              self.rawQuery(q1);
              return q1;
          case "1":
              // Set current "raw" to this query
              self.rawQuery(q2);
              return q2;
          default:
              return raw;
      }
  });

进入编辑模式时清除所选选项

  self.edit = function () {
      self.editing(true)
      self.selected(undefined);
  };

在组合框中添加一个占位符,以便我们可以清除所选的observable并将rawQuery值放入queryBuilder。

<select data-bind="options: querySelector(), optionsText: 'name', optionsValue: 'id', value: selected, optionsCaption: 'select...'"></select>

将click绑定移动到td元素,而不是b元素,这样当rawQuery为空且选中为空时,您可以单击某些内容

因为我使用的是计算机,你需要更改b元素文本绑定以从文本​​绑定值中删除()

<b style="width: 800px; height: 163px;" data-bind="visible: !editing(), text: queryBuilder">&nbsp;</b>

将textarea的值切换为rawQuery并删除valueUpdate绑定。

<textarea style="width: 800px; height: 163px;" data-bind="visible: editing, value: rawQuery, hasFocus: editing"></textarea>

我还在第一行的每个输入数据绑定中添加了启用:选择


现在你有2个observable,你应该能够从rawQuery手动更新回顶部输入框,但这将是棘手的。最好的办法是使用正则表达式来尝试匹配您的参数。

下面的代码有效,但它有点hacky;)

首先附上&#34;匹配&#34;关闭用作参数的每个observable的正则表达式模式。我们稍后会用它。

  var buildParam = function( value, matchRx ) {
      var result = ko.observable(value);
      result.matchRx = matchRx;

      return result;
  };

  self.startDate = buildParam("02/01/2014 12:00 AM", /\d{2}\/\d{2}\/\d{4} \d{2}:\d{2} (?:AM|PM)/);
  self.endDate = buildParam("03/30/2014 12:00 AM", /\d{2}\/\d{2}\/\d{4} \d{2}:\d{2} (?:AM|PM)/);
  self.serviceType = buildParam("401", /\d+/);
  self.channelID = buildParam("101", /\d+/);

接下来创建一个构建查询定义的函数

  var buildQueryDef = function() {
      // Get the arguments, first is the query, all the remaining are observables
      var theArgs = [].splice.call(arguments, 0);

      // Start building the object.  shift the first arg off directly off as the query
      var def = {
          query: theArgs.shift()
      };

      // The remaining elements in the array now all observables.

      // Create a computed to convert the parameters placeholders to real values.
      def.transformed = ko.computed( function() {

          var result = this.query;
          var index = 0;
          theArgs.forEach( function(item) {
              // replace {@p1} with first observable value, and so on
              result = result.replace( new RegExp( "\{\@p" + (index++) + "\}", "g" ), item());
          } );

          return result;
      }, def);

      // Create a function that matches text back to the original query.
      def.doMatch = function( text ) {

          // Convert any special regex control characters to literals and convert spaces to '\s+'
          var src = this.query.replace( /[\(\)\[\]]/g, "\\$&").replace( /[ ]+/g, "\\s+");
          var index = 0;

          // Replace the parameter placeholder with the regex from the parameter observable
          theArgs.forEach( function(item) {
              src = src.replace( new RegExp( "\{\@p" + (index++) + "\}", "g" ), "(" + item.matchRx.source + ")");
          } );

          // Match whole string, allow trailing spaces
          src = "^\s*" + src + "\s*$";

          return { src: src, matches: text.match( new RegExp(src) ) };
      }.bind(def);

      // Update the observables from the match set.
      def.setObservables = function(matches) {
          // Ignore first match as it's the entire string
          for(var index = 1; index < matches.length; index++) {
              theArgs[index-1](matches[index]);
          }

      }.bind(def);

      return def;
  };

从查询SQL构建每个查询def。我已经更新了您的原始SQL以包含您串联字符串的参数占位符(希望您不要介意!)

  self.queryDefs = {
      '0': buildQueryDef("SELECT " +
          "TABLE_ID as TABLE, " +
          "DATEPART(HH,CREATE_DATE) AS HOUR, " +
          "CAST(CREATE_DATE AS DATE) AS DATE, " +
          "COUNT(TRANSACTION_ID) as TRANSACTION_COUNT " +
          "FROM " +
          "[TRANSACTION_SUMMARY] " +
          "WHERE " +
          "CREATE_DATE BETWEEN '{@p0}' AND '{@p1}' " +
          "AND SERVICE_TYPE = {@p2} " +
          "AND TABLE_ID= {@p3}", this.startDate, this.endDate, this.serviceType, this.channelID ),
      '1': buildQueryDef("SELECT " +
          "TABLE_ID as TABLE2, " +
          "DATEPART(HH,CREATE_DATE) AS HOUR2, " +
          "CAST(CREATE_DATE AS DATE) AS DATE2, " +
          "COUNT(TRANSACTION_ID) as TRANSACTION_COUNT2 " +
          "FROM " +
          "[TRANSACTION_SUMMARY] " +
          "WHERE " +
          "CREATE_DATE BETWEEN '{@p0}' AND '{@p1}' " +
          "AND SERVICE_TYPE = {@p2} " +
          "AND TABLE_ID= {@p3}", this.startDate, this.endDate, this.serviceType, this.channelID )

  };

注意参数索引与传递给函数的observable顺序之间的相关性,也就是&#34; key&#34;每个条目对应于&#34; id&#34;支持商店的选择框。

queryBuilder computed现在可以使用queryDefs对象,使用selected()可观察值作为键。

  self.queryBuilder = ko.computed(  function() {

      var selected = self.selected();
      if ( self.queryDefs[selected] ) {
          var result = self.queryDefs[selected].transformed();
          self.rawQuery(result);
          return result;
      } else
          return self.rawQuery();
  });

添加几个订阅编辑和rawQuery以运行上面的匹配系统

  self.editing.subscribe( function(isEdit) {
      if ( !isEdit && ( self.rawQuery.isDirty != true )) {
          // force a refresh if the observable wasn't changed
          // otherwise the queryDefs won't match back up
          self.rawQuery.notifySubscribers(self.rawQuery());
          self.rawQuery.isDirty = false;
      }
  });
  self.rawQuery.subscribe( function(text) {
      if ( self.selected() ){
          return;
      }

      self.rawQuery.isDirty = true;

      for(var query in self.queryDefs) {
          var def = self.queryDefs[query];
          var result = def.doMatch(text);

          if ( !result.matches )
              continue;

          self.selected(query);
          def.setObservables( result.matches );

          return;
      }
  });

isDirty标志用于检测用户何时点击&#34;编辑&#34;但是没有做出改变。在那种情况下,rawQuery订阅不会触发,选择框也不会重新选择已定义的查询,因此我们需要在编辑恢复为false时手动强制通知。

在创建一个包含这些更新的新jsFiddle时,您可以使用它。