Knockout toggle active class on click - javascript

I have a knockout app and within it I have a function which shows/hides elements on the page depending on the selected option. The button that has been selected to activate a particular toggle will have an 'active' class so that it 'stands out' from the others and is clearly visible that that's the selected option. My problem is that I've created a knockout function to toggle the active class but it's triggering the active state on all of the buttons rather than the selected button and I'm not sure why?
var viewModel = function(){
var self = this;
self.isActive = ko.observable(false);
self.toggleActive = function(data, event){
self.isActive(!self.isActive()); //toggle the isActive value between true/false
}
}
<button data-bind="click: toggleActive, css : {'activeStyle' : isActive}">Toggle Active</button>
<button data-bind="click: toggleActive, css : {'activeStyle' : isActive}">Toggle Active</button>
<button data-bind="click: toggleActive, css : {'activeStyle' : isActive}">Toggle Active</button>
Fiddle: http://jsfiddle.net/amMup/5/

You only have one viewmodel for all three buttons. That means you only have a single "isActive" flag that all buttons are bound to.
Instead, use an array of items and a foreach loop to render each one. Here's a tweaked version of your view model:
var viewModel = function(){
var self = this;
self.items = [
{ isActive: ko.observable(false) },
{ isActive: ko.observable(false) },
{ isActive: ko.observable(false) }
];
self.toggleActive = function(data, event){
data.isActive(!data.isActive());//toggle the isActive value between true/false
}
}
var myModel = new viewModel();
ko.applyBindings(myModel);
And the HTML is simplified:
<div data-bind="foreach: items">
<button data-bind="click: $parent.toggleActive, css : {'activeStyle' : isActive}">Toggle Active</button>
</div>
Note the use of $parent to access the parent's binding context. When you're inside a foreach loop, the binding context is the individual item pulled from the foreach loop. By accessing $parent you "reach up" to the object that contains the items property -- which, in your case, is the viewmodel where the toggleActive exists.
Here's an updated fiddle: http://jsfiddle.net/psteele/amMup/6/

This is because you have them all bound to the same observable.
http://jsfiddle.net/Kohan/fdzqJ/
Js
var viewModel = function(){
var self = this;
self.isActive1 = ko.observable(false);
self.isActive2 = ko.observable(false);
self.isActive3 = ko.observable(false);
self.toggleActive = function(data){
data(!data());
}
}
var myModel = new viewModel();
ko.applyBindings(myModel);
HTML
<button data-bind="click: function(){toggleActive(isActive1)}, css : {'activeStyle' : isActive1}">Toggle Active</button>
<button data-bind="click: function(){toggleActive(isActive2)}, css : {'activeStyle' : isActive2}">Toggle Active</button>
<button data-bind="click: function(){toggleActive(isActive3)}, css : {'activeStyle' : isActive3}">Toggle Active</button>

Another way:
<button data-bind="click: function(){setActive('1')}, css: buttonActive() == '1' ? 'active' : '' ">Toggle Active</button>
var viewModel = function(){
var self = this;
self.buttonActive = ko.observable(false);
self.buttonActive = function(index){
self.buttonActive(index);
}
}
var myModel = new viewModel();
ko.applyBindings(myModel);

Related

KnockOutJS Show Hide elements on dropdown selection change

Hi I am trying to get selected value of dropdown in Knockout js so that I can hide/ show other elements based on selection. Below is what I have tried.
What is happening that I am able to get right value on button click but not on dropdown selection change.
Below is my code. The button gives right value, but dropdown selection change event gives previous value & not the selected one.
JS
function ViewModel() {
var self = this;
self.optionValues= ko.observableArray(["Test1", "Test2", "Test3"]);
self.selectedValue = ko.observable();
self.save = function() {
alert(self.selectedValue());
}
}
ko.applyBindings(new ViewModel());
HTML
<select data-bind="event:{ change: save},options: optionValues, value: selectedValue"></select>
<button data-bind="click: save">Save</button>
Instead of the change event binding binding, you should subscribe directly on your selectedValue observable, and call your logic from there:
function ViewModel() {
var self = this;
self.optionValues = ko.observableArray(["Test1", "Test2", "Test3"]);
self.selectedValue = ko.observable();
self.selectedValue.subscribe(function(newValue) {
self.save();
});
self.save = function() {
alert(self.selectedValue());
}
}
ko.applyBindings(new ViewModel());
Html:
<select data-bind="options: optionValues, value: selectedValue"></select>
<button data-bind="click: save">Save</button>
Demo JSFiddle.

data-bind="click" not working with nested knockout view model

I have a knockout view model that is set up as an observable as my main view model. In the child element, it seems that I can't set up data-bind="click: the same way that I can when I am in the parent element.
My html:
<button id="myButton" type="button" class="btn btn-lg btn-primary" data-bind="click: test">Click Me</button>
In my main view model:
self.childElement = ko.observable(new childElementVm());
and in childElementVm
var childElementVm= function () {
var test = function(){
alert('this is a test');
}
}
what do I need to do differently to use data-bind="click: test" here?
To note, my applyBindings is fine (other knockout observables are functioning correctly) and the button is contained inside of a <div data-bind="with: childElement"
EDIT: here is a fiddle
Your test function is scoped to the childElementVmonly. Change your implementation to this:
var childElementVm= function () {
this.test = function(){
alert('this is a test');
}
}
or this:
var childElementVm= function () {
var self = this;
self.test = function(){
alert('this is a test');
}
}
Here is a working example

How to add class with data-attribute?

I am working on some data buttons but can't figure out how to get the function working. I'm trying to add a class to an element via a data-attribute and a corresponding ID. I then want to stop the event propagation with the button inside the subject.
So far this is what I have:
HTML:
<div class ="subject" id="subject1">
<button class="remove">Remove</button>
The Subject
</div>
<div class ="subject" id="subject2">
<button class="remove">Remove</button>
The Subject 2
</div>
<div class ="subject" id="subject3">
<button class="remove">Remove</button>
The Subject
</div>
<button class="trigger" data-subject="subject1">Trigger</button>
<button class="trigger" data-subject="subject2">Trigger</button>
<button class="trigger" data-subject="subject3">Trigger</button>
CSS:
.active {
color:red
}
JS:
var subject = document.querySelector('.subject');
var trigger = document.querySelector('.trigger');
var remove = subject.querySelector('.close');
var data = trigger.getAttribute("data-subject");
var datajs = (function () {
function init() {
[].forEach.call(trigger),function(btn) {
btn.onclick = function() {this.classList.add("active");}
});
remove.addEventListener('click', function (ev) {
ev.stopPropagation();
removeModalHandler();
});
});
}
init();
})();
and here's a fiddle: http://jsfiddle.net/tNS62/1/
My JS isn't that great as you can probably see.
Try this. First we select all the buttons :
var triggers = document.querySelectorAll('.trigger');
var removes = document.querySelectorAll('.remove');
Then we apply the behavior on each button :
for (var i =0; i < triggers.length; i++) {
var btn = triggers[i];
btn.addEventListener('click', function () {
var id = this.dataset.subject;
document.querySelector('#' + id).classList.toggle('active');
}, false);
}
Use dataset to get the id attached to, then search the corresponding element in order to toogle the active class.
I made the same thing for the remove buttons.

Jquery how to bind element when iterating through .each to class method

I have a simple js class where i am trying to bind 'click' event on an object to the functions. I am iterating through a list of elements:
<ul id="cp">
<li><button id="panel_button_light">Turn Light On</button></li>
<li>
<div class="arrow-up" id="panel_button_temp_up"></div>
<div class="arrow-down" id="panel_button_temp_down"></div>
</li>
</ul>
I am not sure what i need to enter in the code below when iterating through the elements so that I can call the class method when clicking them.
This is the js code that i am using:
<script type="text/javascript">
var CP = function(widget){
this.widget_name = widget;
var self = this;
this.init = function(){
$('#'+this.widget_name).children('li').children('*[id*=panel_]').each(function(self){
//I need function here to bind the click event to the class CP
$("#" + this.id).on('click', $.proxy(this.id, this));
});
}
};
CP.prototype.panel_button_temp_up= function(){
};
CP.prototype.panel_button_temp_down= function(){
};
CP.prototype.panel_button_light = function(){
};
$( document ).ready(function(){
var c = new CP("cp");
c.init();
});
this.id is a string, so $.proxy(this.id, this) doesn't make sense. If you want to access the property with the same name on the instance of cp, you have to do something like instance[this.id].
For example:
var self = this;
this.init = function() {
$('#'+this.widget_name).find('[id^=panel_]').each(function() {
$(this).on('click', self[this.id]);
});
};

Kendo-Knockout: Cannot call a function within template

I want to call the onHover function but with no suceess. The only way I managed to do this is if the function is global, but this is no what I need.
What I am trying to do is onMouseOver of some element of the dropdown to take its value and do something with it in my viewmodel.
HTML:
<div>
<div data-bind="with: myInnerViewModel">
<input type="text" data-bind="kendoDropDownList: {data: myData, value: myValue,template:'<span onMouseOver = \'onHover(this)\' title=\'${data}\'>${data}</span>'}" />
<div>
</div>
JS:
var myViewModel = function () {
this.myInnerViewModel = {
myData : [1, 2 , 3],
myValue : ko.observable(1),
onHover : function(e){
alert(1);
}
};
};
ko.applyBindings(new myViewModel());
fiddler: http://jsfiddle.net/QZWPR/30/
The issue lies with location of your function. If you were to change
onMouseOver = \'onHover(this)\'
to
onMouseOver = \'myViewModel.myInnerViewModel.onHover(this)\'
then i expect you would see your alert.
OR
you need to use the event binding on your span
<span data-bind="event: { mouseover: onHover}">

Categories