【问题标题】:Knockout Update TextArea with Multiple Binding具有多个绑定的淘汰赛更新 TextArea
【发布时间】:2014-04-22 07:58:22
【问题描述】:

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

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

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

这里是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);
  };

【问题讨论】:

  • 我最大的障碍是坚持更新”是什么意思?如果您的意思是在发布后保留用户选择,那么您需要编写代码以在页面重新加载后将它们传回。
  • 不...我希望用户能够从下拉列表中选择一个选项,并在文本区域中有一个预填充的值。然后,如果用户决定他们想要调整“给定”参数,他们可以,并且文本区域的内容将相应地更新。最后,如果用户决定他们想要更改文本或非常诚实地替换文本区域的内容,他们可以。总而言之,文本区域中的最终值是我要捕获和发送的值。希望这可以澄清

标签: html sql knockout.js


【解决方案1】:

您需要 2 个单独的 observable,一个用于非编辑模式,另一个用于编辑模式。

  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>

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

因为我使用的是计算的,所以你需要更改 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>

我还在第一行的每个输入的数据绑定中添加了 enable: selected


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

下面的代码可以工作,但是有点hacky ;)

首先在每个用作参数的可观察对象上附加一个“匹配”正则表达式模式。我们稍后会用到它。

  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 构建每个查询定义。我已更新您的原始 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 )

  };

注意参数索引和传入函数的可观察对象的顺序之间的相关性,每个条目的“键”对应于选择框后备存储的“id”。

计算的 queryBuilder 现在可以使用 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 标志用于检测用户何时单击“编辑”但未进行更改。在这种情况下,rawQuery 订阅不会触发,选择框也不会重新选择已定义的查询,因此我们需要在需要编辑返回 false 时手动强制通知。

已使用这些更新创建一个新的jsFiddle,以便您可以使用它。

【讨论】:

  • 这看起来很棒。我很抱歉;我已经离开几天了。首先,非常感谢您抽出时间如此仔细地研究这个问题。在接下来的几天里,我会更仔细地检查它,并让你知道它是如何适应的。再次感谢你。一般来说,看到这一点有助于提供更多关于淘汰赛工作的视角。
猜你喜欢
  • 2017-05-11
  • 2014-10-30
  • 1970-01-01
  • 1970-01-01
  • 2014-03-07
  • 2014-01-08
  • 2016-06-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多