您需要 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"> </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,以便您可以使用它。