基于 observable 变化的切换函数
Toggle function based on change of observable
在 Durandal 中,我正在寻找一种通过分配 viewOptions: ko.observable("small")
来快速初始化活动切换按钮的方法。在 viewOptions
发生变化的同时,我想打开相应 id
的按钮并关闭所有其他按钮。
在我尝试过的几种方法中,none 正是这样做的。以下代码中缺少什么?
- 框架是Durandal,它是Knockout JS的扩展
- 我对观察到的变量的变化没有任何反应
HTML:
<div class="btn-group right" id="view-selector" data-toggle="buttons" data-bind="radio: viewOptions">
<button type="button" id="small" class="btn btn-default" data-bind="css: {active: viewOptions() == 'small'}"><span class="glyphicon glyphicon-stop">Small</button>
<button type="button" id="medium" class="btn btn-default" data-bind="css: {active: viewOptions() == 'medium'}"><span class="glyphicon glyphicon-th-large">Medium</button>
<button type="button" id="large" class="btn btn-default" data-bind="css: {active: viewOptions() == 'large'}"><span class="glyphicon glyphicon-th">Large</button>
</div>
JS:
define(['jquery', 'jquery-ui', 'bootstrap', 'knockout', 'durandal/app', 'plugins/router'], function($, jqueryui, bootstrap, ko, app, router) {
return {
viewOptions: ko.observable("small"),
bindingComplete: function() { // called immediately after databinding occurs.
alert("start"); //called
console.log("validate: this:", this); // Object {__moduleId__: "app/view"}
console.log("validate: viewOptions:",this.viewOptions); // OK, non-empty
this.viewOptions.subscribe(function(newViewOptions){
alert("Hello there!"); //never gets called
alert(newViewOptions); //
});
alert("end"); //called
}
}
});
您使用 the css
binding,告诉它在 viewOptions() == 'small'
(或中等或大)时应用 active
class:
<button ... data-bind="css: {active: viewOptions() == 'small'}">...</button>
示例:
var viewModel = {
viewOptions: ko.observable("small"),
};
ko.applyBindings(viewModel);
loop();
function loop() {
viewModel.viewOptions("small");
setTimeout(function() {
viewModel.viewOptions("medium");
}, 1000);
setTimeout(function() {
viewModel.viewOptions("large");
}, 2000);
setTimeout(loop, 3000);
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
<div class="btn-group right" id="view-selector" data-toggle="buttons" data-bind="radio: viewOptions">
<button type="button" id="small" class="btn btn-default" data-bind="css: {active: viewOptions() == 'small'}"><span class="glyphicon glyphicon-stop">Small</button>
<button type="button" id="medium" class="btn btn-default" data-bind="css: {active: viewOptions() == 'medium'}"><span class="glyphicon glyphicon-th-large">Medium</button>
<button type="button" id="large" class="btn btn-default" data-bind="css: {active: viewOptions() == 'large'}"><span class="glyphicon glyphicon-th">Large</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
我从上面删除了订阅,因为我认为你只是用它来设置 class。如果你想要订阅以及,只需将其添加回来(在浏览器控制台中查看):
var viewModel = {
viewOptions: ko.observable("small"),
attached: function() {
this.viewOptions.subscribe(function(newViewOptions){
console.log(newViewOptions);
}, this); // <== No need for bind, note subscribe' 2nd arg
}
};
ko.applyBindings(viewModel);
viewModel.attached();
var timeout = +new Date() + 10000;
loop();
function loop() {
viewModel.viewOptions("small");
setTimeout(function() {
viewModel.viewOptions("medium");
}, 1000);
setTimeout(function() {
viewModel.viewOptions("large");
}, 2000);
if (+new Date() < timeout) {
setTimeout(loop, 3000);
}
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
<div class="btn-group right" id="view-selector" data-toggle="buttons" data-bind="radio: viewOptions">
<button type="button" id="small" class="btn btn-default" data-bind="css: {active: viewOptions() == 'small'}"><span class="glyphicon glyphicon-stop">Small</button>
<button type="button" id="medium" class="btn btn-default" data-bind="css: {active: viewOptions() == 'medium'}"><span class="glyphicon glyphicon-th-large">Medium</button>
<button type="button" id="large" class="btn btn-default" data-bind="css: {active: viewOptions() == 'large'}"><span class="glyphicon glyphicon-th">Large</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
自定义绑定 "radio" 从数据属性 "data-value" 驱动每个切换按钮的值,因此您必须为每个选项指定该值,例如:
<button type="button" id="small" class="btn btn-default" data-value="small"> ...
并且应该处理这个 fiddle http://jsfiddle.net/ccjnj/171/
中展示的问题
T.J。 Crowder 为您提供了一个很好的答案。我只是认为在您的视图模型中制作按钮项目并使用 foreach
绑定来呈现它们似乎是个好主意。节省重复 HTML.
function buttonData(label, value) {
return {
label: label,
value: value
};
}
var viewModel = {
buttons: [
buttonData('Small', 'small'),
buttonData('Medium', 'medium'),
buttonData('Large', 'large')
],
selectedButton: ko.observable("small"),
setOption: function(data) {
console.debug("Set option", data.value);
viewModel.selectedButton(data.value);
}
};
ko.applyBindings(viewModel);
viewModel.selectedButton.subscribe(function(newViewOptions) {
alert("Hello there!");
alert(newViewOptions);
});
<link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.1.1/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div class="btn-group right" id="view-selector" data-toggle="buttons" data-bind="foreach: buttons">
<button type="button" class="btn btn-default" data-bind="click:$root.setOption, text:label, css: { active: value == $root.selectedButton()}"></button>
</div>
结合一些解决方案,我找到了答案。我意识到 Durandal 中的事件处理模型与 Knockout 略有不同,在我错过 ko.bindingHandlers.radio
和 bindingComplete
:
之前需要以下代码
define(['jquery', 'jquery-ui', 'bootstrap', 'knockout', 'durandal/app', 'plugins/router'], function($, jqueryui, bootstrap, ko, app, router) {
ko.bindingHandlers.radio = {
init: function(element, valueAccessor, allBindings, data, context) {
var $buttons, $element, observable;
observable = valueAccessor();
if (!ko.isWriteableObservable(observable)) {
throw "You must pass an observable or writeable computed";
}
$element = $(element);
if ($element.hasClass("btn")) {
$buttons = $element;
}
else {
$buttons = $(".btn", $element);
}
var elementBindings = allBindings();
$buttons.each(function() {
var $btn, btn, radioValue;
btn = this;
$btn = $(btn);
radioValue = $btn.attr("id");
$btn.on("click", function() {
observable(ko.utils.unwrapObservable(radioValue));
});
return ko.computed({
disposeWhenNodeIsRemoved: btn,
read: function() {
$btn.toggleClass("active", observable() === ko.utils.unwrapObservable(radioValue));
}
});
});
}
};
var vm = {
viewOptions: ko.observable(),
bindingComplete: function() {
vm.viewOptions.subscribe(function(newViewOptions){
alert("Hello there!");
alert(newViewOptions);
});
vm.viewOptions("large");
}
}
return vm;
});
在 Durandal 中,我正在寻找一种通过分配 viewOptions: ko.observable("small")
来快速初始化活动切换按钮的方法。在 viewOptions
发生变化的同时,我想打开相应 id
的按钮并关闭所有其他按钮。
在我尝试过的几种方法中,none 正是这样做的。以下代码中缺少什么?
- 框架是Durandal,它是Knockout JS的扩展
- 我对观察到的变量的变化没有任何反应
HTML:
<div class="btn-group right" id="view-selector" data-toggle="buttons" data-bind="radio: viewOptions">
<button type="button" id="small" class="btn btn-default" data-bind="css: {active: viewOptions() == 'small'}"><span class="glyphicon glyphicon-stop">Small</button>
<button type="button" id="medium" class="btn btn-default" data-bind="css: {active: viewOptions() == 'medium'}"><span class="glyphicon glyphicon-th-large">Medium</button>
<button type="button" id="large" class="btn btn-default" data-bind="css: {active: viewOptions() == 'large'}"><span class="glyphicon glyphicon-th">Large</button>
</div>
JS:
define(['jquery', 'jquery-ui', 'bootstrap', 'knockout', 'durandal/app', 'plugins/router'], function($, jqueryui, bootstrap, ko, app, router) {
return {
viewOptions: ko.observable("small"),
bindingComplete: function() { // called immediately after databinding occurs.
alert("start"); //called
console.log("validate: this:", this); // Object {__moduleId__: "app/view"}
console.log("validate: viewOptions:",this.viewOptions); // OK, non-empty
this.viewOptions.subscribe(function(newViewOptions){
alert("Hello there!"); //never gets called
alert(newViewOptions); //
});
alert("end"); //called
}
}
});
您使用 the css
binding,告诉它在 viewOptions() == 'small'
(或中等或大)时应用 active
class:
<button ... data-bind="css: {active: viewOptions() == 'small'}">...</button>
示例:
var viewModel = {
viewOptions: ko.observable("small"),
};
ko.applyBindings(viewModel);
loop();
function loop() {
viewModel.viewOptions("small");
setTimeout(function() {
viewModel.viewOptions("medium");
}, 1000);
setTimeout(function() {
viewModel.viewOptions("large");
}, 2000);
setTimeout(loop, 3000);
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
<div class="btn-group right" id="view-selector" data-toggle="buttons" data-bind="radio: viewOptions">
<button type="button" id="small" class="btn btn-default" data-bind="css: {active: viewOptions() == 'small'}"><span class="glyphicon glyphicon-stop">Small</button>
<button type="button" id="medium" class="btn btn-default" data-bind="css: {active: viewOptions() == 'medium'}"><span class="glyphicon glyphicon-th-large">Medium</button>
<button type="button" id="large" class="btn btn-default" data-bind="css: {active: viewOptions() == 'large'}"><span class="glyphicon glyphicon-th">Large</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
我从上面删除了订阅,因为我认为你只是用它来设置 class。如果你想要订阅以及,只需将其添加回来(在浏览器控制台中查看):
var viewModel = {
viewOptions: ko.observable("small"),
attached: function() {
this.viewOptions.subscribe(function(newViewOptions){
console.log(newViewOptions);
}, this); // <== No need for bind, note subscribe' 2nd arg
}
};
ko.applyBindings(viewModel);
viewModel.attached();
var timeout = +new Date() + 10000;
loop();
function loop() {
viewModel.viewOptions("small");
setTimeout(function() {
viewModel.viewOptions("medium");
}, 1000);
setTimeout(function() {
viewModel.viewOptions("large");
}, 2000);
if (+new Date() < timeout) {
setTimeout(loop, 3000);
}
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
<div class="btn-group right" id="view-selector" data-toggle="buttons" data-bind="radio: viewOptions">
<button type="button" id="small" class="btn btn-default" data-bind="css: {active: viewOptions() == 'small'}"><span class="glyphicon glyphicon-stop">Small</button>
<button type="button" id="medium" class="btn btn-default" data-bind="css: {active: viewOptions() == 'medium'}"><span class="glyphicon glyphicon-th-large">Medium</button>
<button type="button" id="large" class="btn btn-default" data-bind="css: {active: viewOptions() == 'large'}"><span class="glyphicon glyphicon-th">Large</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
自定义绑定 "radio" 从数据属性 "data-value" 驱动每个切换按钮的值,因此您必须为每个选项指定该值,例如:
<button type="button" id="small" class="btn btn-default" data-value="small"> ...
并且应该处理这个 fiddle http://jsfiddle.net/ccjnj/171/
中展示的问题T.J。 Crowder 为您提供了一个很好的答案。我只是认为在您的视图模型中制作按钮项目并使用 foreach
绑定来呈现它们似乎是个好主意。节省重复 HTML.
function buttonData(label, value) {
return {
label: label,
value: value
};
}
var viewModel = {
buttons: [
buttonData('Small', 'small'),
buttonData('Medium', 'medium'),
buttonData('Large', 'large')
],
selectedButton: ko.observable("small"),
setOption: function(data) {
console.debug("Set option", data.value);
viewModel.selectedButton(data.value);
}
};
ko.applyBindings(viewModel);
viewModel.selectedButton.subscribe(function(newViewOptions) {
alert("Hello there!");
alert(newViewOptions);
});
<link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.1.1/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div class="btn-group right" id="view-selector" data-toggle="buttons" data-bind="foreach: buttons">
<button type="button" class="btn btn-default" data-bind="click:$root.setOption, text:label, css: { active: value == $root.selectedButton()}"></button>
</div>
结合一些解决方案,我找到了答案。我意识到 Durandal 中的事件处理模型与 Knockout 略有不同,在我错过 ko.bindingHandlers.radio
和 bindingComplete
:
define(['jquery', 'jquery-ui', 'bootstrap', 'knockout', 'durandal/app', 'plugins/router'], function($, jqueryui, bootstrap, ko, app, router) {
ko.bindingHandlers.radio = {
init: function(element, valueAccessor, allBindings, data, context) {
var $buttons, $element, observable;
observable = valueAccessor();
if (!ko.isWriteableObservable(observable)) {
throw "You must pass an observable or writeable computed";
}
$element = $(element);
if ($element.hasClass("btn")) {
$buttons = $element;
}
else {
$buttons = $(".btn", $element);
}
var elementBindings = allBindings();
$buttons.each(function() {
var $btn, btn, radioValue;
btn = this;
$btn = $(btn);
radioValue = $btn.attr("id");
$btn.on("click", function() {
observable(ko.utils.unwrapObservable(radioValue));
});
return ko.computed({
disposeWhenNodeIsRemoved: btn,
read: function() {
$btn.toggleClass("active", observable() === ko.utils.unwrapObservable(radioValue));
}
});
});
}
};
var vm = {
viewOptions: ko.observable(),
bindingComplete: function() {
vm.viewOptions.subscribe(function(newViewOptions){
alert("Hello there!");
alert(newViewOptions);
});
vm.viewOptions("large");
}
}
return vm;
});