I've searched high and low for an answer to this but can't for the life of me figure out what I'm doing differently from the official example, aside from the fact that I think my use case is a bit more plicated:
.html
Basically, I'm trying to create reusable UI elements. The behavior will essentially be passed to them via the "params" object. I want multiple elements to be able to exist on a single page, however, which is where I'm running into difficulty.
I'm using browserify to bundle my code and have the following (some entries truncated for brevity):
index.html
<div data-bind='ponent: { name: "toggle" , params: {
enabledText: "Parental controls are enabled",
disabledText: "Parental controls are disabled"
}}'></div>
<div data-bind='ponent: { name: "toggle" , params: {
enabledText: "Same ponent, different behavior: enabled",
disabledText: "Same ponent, different behavior: disabled"
}}'></div>
main.js
var ko = window.ko = require('knockout'),
toggle = require('./ponents/toggle/toggle');
koponents.register('toggle', toggle);
function Container() {
}
var con = new Container();
ko.applyBindings(con);
ponents/toggle/toggle.js
var ko = require('knockout'),
template = require('./toggle.html');
function vm(params) {
var self = this;
self.enabled = ko.observable(false);
self.label = koputed(function() {
return self.enabled() ? params.enabledText : params.disabledText;
});
}
module.exports = { viewModel: vm, template: template };
And finally, in ponents/toggle/toggle.html:
<input type='checkbox' data-bind='checked: enabled' id='switch-checkbox' class='switch-input' />
<label for='switch-checkbox' class='switch-input-label'>
<span data-bind='text: label'></span>
</label>
The issue I'm having is that the ponents both appear on the page appropriately, but clicking the second one activates the first one (and does nothing for the seconD). I'm new to Knockout and am clearly missing something, but I can't figure out how to fix my issue. Any help would be immensely appreciated!
The strange thing is that the labels are appropriately unique, indicating that the ponents (view models) being instantiated for each HTML entity are, in fact, unique...however, it seems knockout's "checked" binding is only binding to the first.
EDIT: I know it's customary to include an example, so here's one on codepen. I apologize for using browserified code but hopefully it's still readable:
I've searched high and low for an answer to this but can't for the life of me figure out what I'm doing differently from the official example, aside from the fact that I think my use case is a bit more plicated:
http://knockoutjs./documentation/ponent-binding.html
Basically, I'm trying to create reusable UI elements. The behavior will essentially be passed to them via the "params" object. I want multiple elements to be able to exist on a single page, however, which is where I'm running into difficulty.
I'm using browserify to bundle my code and have the following (some entries truncated for brevity):
index.html
<div data-bind='ponent: { name: "toggle" , params: {
enabledText: "Parental controls are enabled",
disabledText: "Parental controls are disabled"
}}'></div>
<div data-bind='ponent: { name: "toggle" , params: {
enabledText: "Same ponent, different behavior: enabled",
disabledText: "Same ponent, different behavior: disabled"
}}'></div>
main.js
var ko = window.ko = require('knockout'),
toggle = require('./ponents/toggle/toggle');
ko.ponents.register('toggle', toggle);
function Container() {
}
var con = new Container();
ko.applyBindings(con);
ponents/toggle/toggle.js
var ko = require('knockout'),
template = require('./toggle.html');
function vm(params) {
var self = this;
self.enabled = ko.observable(false);
self.label = ko.puted(function() {
return self.enabled() ? params.enabledText : params.disabledText;
});
}
module.exports = { viewModel: vm, template: template };
And finally, in ponents/toggle/toggle.html:
<input type='checkbox' data-bind='checked: enabled' id='switch-checkbox' class='switch-input' />
<label for='switch-checkbox' class='switch-input-label'>
<span data-bind='text: label'></span>
</label>
The issue I'm having is that the ponents both appear on the page appropriately, but clicking the second one activates the first one (and does nothing for the seconD). I'm new to Knockout and am clearly missing something, but I can't figure out how to fix my issue. Any help would be immensely appreciated!
The strange thing is that the labels are appropriately unique, indicating that the ponents (view models) being instantiated for each HTML entity are, in fact, unique...however, it seems knockout's "checked" binding is only binding to the first.
EDIT: I know it's customary to include an example, so here's one on codepen. I apologize for using browserified code but hopefully it's still readable:
http://codepen.io/sunny-mittal/pen/OVBNwp
Share Improve this question edited Jul 24, 2015 at 7:10 Moppo 19.3k5 gold badges67 silver badges65 bronze badges asked Jul 24, 2015 at 6:08 sunny-mittalsunny-mittal 5097 silver badges14 bronze badges1 Answer
Reset to default 8When you load multiple instances of this ponent the id
attribute on the input
tag and the for
attribute on the label
tag, that make up your ponent, are no longer going to be unique to the page.
You essentially have two input
tags with the same id
and two label
tags targeting one id
. Your label for="switch-checkbox"
on the second ponent is picking up input id="switch-ponent"
from the first ponent and not the second.
Though knockout ponents are great, unfortunately there is no dom isolation between instances of ponents.
To resolve this issue you need to ensure the values for id
and for
in each instance of your ponent are unique to the entire page.
I have included a snippet below of this working.
var uid = function(){
var seed = 1;
return {
new: function(p){
return p + (seed++);
}
}
}();
var template =
"<input type='checkbox' data-bind='checked: enabled, attr: {id:id}' class='switch-input' />\n<label data-bind='attr: {for: id}' class='switch-input-label'>\n <span data-bind='text: label'></span>\n</label>\n";
var viewModel = function vm(params) {
var self = this;
self.id = uid.new('switch-checkbox-');
self.enabled = ko.observable(false);
self.label = ko.puted(function() {
return self.enabled() ? params.enabledText : params.disabledText;
});
}
var ponent = {
viewModel: viewModel,
template: template
};
ko.ponents.register('toggle', ponent);
var vm = {};
ko.applyBindings(vm);
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
position: relative;
height: 100%;
min-height: 100%;
}
.switch-input {
display: none;
}
.switch-input-label {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
position: relative;
display: inline-block;
cursor: pointer;
font-weight: 500;
text-align: left;
margin: 16px;
padding: 16px 0 16px 44px;
}
.switch-input-label:before,
.switch-input-label:after {
content: '';
position: absolute;
margin: 0;
outline: 0;
top: 50%;
transform: translate(0, -50%);
transition: all 0.3s ease;
}
.switch-input-label:before {
left: 1px;
width: 34px;
height: 14px;
background-color: #9e9e9e;
border-radius: 8px;
}
.switch-input-label:after {
left: 0;
width: 20px;
height: 20px;
background-color: #fafafa;
border-radius: 50%;
box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.14), 0 2px 2px 0 rgba(0, 0, 0, 0.098), 0 1px 5px 0 rgba(0, 0, 0, 0.084);
}
.switch-input:checked + .switch-input-label:before {
background-color: #a5d6a7;
}
.switch-input:checked + .switch-input-label:after {
background-color: #4caf50;
transform: translate(80%, -50%);
}
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind='ponent: { name: "toggle" , params: {
enabledText: "Parental controls are enabled",
disabledText: "Parental controls are disabled"
}}'></div>
<div data-bind='ponent: { name: "toggle" , params: {
enabledText: "Same ponent, different behavior: enabled",
disabledText: "Same ponent, different behavior: disabled"
}}'></div>
<div data-bind='ponent: { name: "toggle" , params: {
enabledText: "Same ponent, another instance: enabled",
disabledText: "Same ponent, another instance: disabled"
}}'></div>