Knockout click data binding - javascript

I have an issue in below example
HTML CODE:
<table id="items">
<thead>
<tr>
<td>ToDo List</td>
</tr>
</thead>
<tbody data-bind="foreach: toDoItems">
<tr>
<td><label data-bind="text: toDoItem"></label></td>
<td>Remove</td>
</tr>
</tbody>
</table>
Add item: <input type="text" id="newitem" />
<button data-bind="click: addnewItem">Add</button>
This is my JS code:
$(function () {
var MetalViewModel = function() {
var self = this;
self.toDoItems = ko.observableArray();
self.update = function() {
self.toDoItems.removeAll();
self.toDoItems.push(
new metals({"Task":"This is urgent task."}),
new metals({"Task":"You need to do it also."})
);
}
self.addnewItem = function () {
alert( $("#newitem").val() );
self.toDoItems.push(
new metals({"Task":$("#newitem").val()})
);
};
self.removeToDoItem = function(item) {
self.toDoItems.remove(item);
};
};
var MetalViewModel = new MetalViewModel();
var y = window.setInterval(MetalViewModel.update,1000);
ko.applyBindings(MetalViewModel, document.getElementById("items"));
});
var metals = function (data) {
return {
toDoItem: ko.observable(data.Task)
};
};
Everything is working fine. Listing, removing ....
The only issue I am facing is that when I add new item, function is totally not working... means even if I alert click binding does not reach there.

You bound only the table with your vewmodel (id=items):
ko.applyBindings(MetalViewModel, document.getElementById("items"));
And your button is outside the table so it is not related to your viewmodel.
As a solution, you can bind to the common parent or to the whole page:
ko.applyBindings(MetalViewModel);

Here're two issues:
invalid binding, instead of ko.applyBindings(MetalViewModel, document.getElementById("items")); should be ko.applyBindings(MetalViewModel);
invalid data-bind function name instead of additemToAdd should be addToDoItem
here's working sample

Related

Is it possible to pass a Knockout.JS variable into a onclick function WITHOUT binding?

Is it possible to pass a Knockout.JS variable into an onclick function WITHOUT binding the function to the view model?
Below, i'm trying to pass the username parameter for the particular row below:
<tbody data-bind="foreach: viewModel">
<tr>
<td data-bind="text: UserId"></td>
<td><button onclick="alertRowName($element.UserName)"></button></td>
</tr>
</tbody>
<script>
function alertRowName(string){
alert(string);
}
//in my example the model is from a c# viewmodel..
viewModel = ko.mapping.fromJS(model);
var jsModel = ko.toJS(viewModel);
ko.applyBindings(viewModel);
</script>
You can do it something like this.
EDIT
hmmmm.. just noticed the Without binding. Can you clarify why without binding?
var app = Window.app || {};
app.model = [{
UserId: 1,
UserName: "User Name Here",
}];
app.ViewModel = function ViewModel(model){
var self = this;
self.data = ko.mapping.fromJS(model);
};
app.ViewModel.prototype.alertRowName = function alertRowName(user) {
alert(user.UserName());
};
ko.applyBindings(new app.ViewModel(app.model));
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
<table>
<tbody data-bind="foreach: data">
<tr>
<td data-bind="text: UserId"></td>
<td><button data-bind="click: $root.alertRowName, text: UserName"></button> </td>
</tr>
</tbody>
</table>
BEWARE...HERE BE DRAGONS
You can do the following, but I would strongly recommend not doing this.
This is attaching a function to the Window object making it a Global function. each event handler generally has an event object available that you can use.
var app = Window.app || {};
app.model = [{
UserId: 1,
UserName: "User Name Here",
}];
app.ViewModel = function ViewModel(model){
var self = this;
self.data = ko.mapping.fromJS(model);
};
app.ViewModel.prototype.alertRowName = function alertRowName(user) {
alert(user.UserName());
};
ko.applyBindings(new app.ViewModel(app.model));
Window.myButtonClick = function(){
var item = ko.dataFor(event.currentTarget);
alert(item.UserName());
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
<table>
<tbody data-bind="foreach: data">
<tr>
<td data-bind="text: UserId"></td>
<td><button onclick="Window.myButtonClick()" data-bind="text: UserName"></button> </td>
</tr>
</tbody>
</table>

Knockout.js two-way binding for checkbox not working

I'm new to knockout.js and I have some trouble with two-way binding of a checkbox.
I have a table that's bound to a list of "companies".
<table>
<thead>
<tr>
<th>...</th>
<th>...</th>
<th>...</th>
</tr>
</thead>
<tbody data-bind="foreach: companies">
<tr>
<td>...</td>
<td><input type="checkbox" data-bind="checked:isCompanyForCommunication, click:$root.checkedNewCompanyForCommunication" /></td>
<td>...</td>
</tr>
</tbody>
</table>
But there should only be 1 company with isCompanyForCommunication = true in the table. So when another checkbox is checked, I have to set all other companies to isCompanyForCommunication = false. Therefore I created a method in the viewModel to uncheck all other companies.
// Definition of Company
var Company = function (crmAccountObject, contactId, companyForCommunicationId) {
this.isCompanyForCommunication = ko.observable(false);
}
// Definition of the ViewModel
var ViewModel = function () {
var self = this;
// ...
// Lists
this.companies = null; // This observableArray is set somewhere else
// Events
this.checkedNewCompanyForCommunication = function (company, event) {
// Set all companies to False
for (var i = 0; i < self.companies().length; i++) {
self.companies()[i].isCompanyForCommunication = ko.observable(false);
}
// Set the "checked" company to TRUE
company.isCompanyForCommunication = ko.observable(true);
return true;
}
}
However, the changes are not reflected in the HTML page. All other checkboxes remain checked.
I created a simplyfied example in jsfiddle to show what exactly I want to achieve with the checkbox https://jsfiddle.net/htZfX/59/
Someone has any idea what I'm doing wrong? Thanks in advance!
You are not setting the value of the isCompanyForCommunication but overriding it with a new observable.
Observables are functions and to update them you need to call them with the new value as the argument:
this.checkedNewCompanyForCommunication = function (company, event) {
// Set all companies to False
for (var i = 0; i < self.companies().length; i++) {
self.companies()[i].isCompanyForCommunication(false);
}
// Set the "checked" company to TRUE
company.isCompanyForCommunication(true);
return true;
}
I've also updated your sample code: JSFiddle.

How can I toggle the display of a textarea via a button using knockout with the foreach binding?

I am new to knockout. For my problem, I am trying to make it so that for each project, there is a button and textarea. The textarea will be hidden upon page load. If I click the button, it will show the textarea (toggle). Currently, if I click the button, ALL textareas on the page will show, rather than just the corresponding textarea.
I'm hoping the fix for this isn't too dramatic and involving a complete reworking of my code as by some magic, every other functionality has been working thus far. I added the {attr id: guid} (guid is a unique identifier of a project retrieved from the database) statement in an attempt to establish a unique ID so that the right controls were triggered...although that did not work.
Sorry I do not have a working jfiddle to show the issue... I tried to create one but it does not demonstrate the issue.
JS:
//if a cookie exists, extract the data and bind the page with cookie data
if (getCookie('filterCookie')) {
filterCookie = getCookie('filterCookie');
var cookieArray = filterCookie.split(",");
console.log(cookieArray);
$(function () {
var checkboxes = new Array();
for (var i = 0; i < cookieArray.length; i++) {
console.log(i + cookieArray[i]);
checkboxes.push(getCheckboxByValue(cookieArray[i]));
//checkboxes.push(document.querySelectorAll('input[value="' + cookieArray[i] + '"]'));
console.log(checkboxes);
checkboxes[i].checked = true;
}
})
filterCookie = getCookie('filterResultsCookie');
cookieArray = filterCookie.split(",");
filterCookieObj = {};
filterCookieObj.action = "updateProjects";
filterCookieObj.list = cookieArray;
$.ajax("/api/project/", {
type: "POST",
data: JSON.stringify(filterCookieObj)
}).done(function (response) {
proj = response;
ko.cleanNode(c2[0]);
c2.html(original);
ko.applyBindings(new ProjectViewModel(proj), c2[0]);
});
}
//if the cookie doesn't exist, just bind the page
else {
$.ajax("/api/project/", {
type: "POST",
data: JSON.stringify({
action: "getProjects"
})
}).done(function (response) {
proj = response;
ko.cleanNode(c2[0]);
c2.html(original);
ko.applyBindings(new ProjectViewModel(proj), c2[0]);
});
}
View Model:
function ProjectViewModel(proj) {
//console.log(proj);
var self = this;
self.projects = ko.observableArray(proj);
self.show = ko.observable(false);
self.toggleTextArea = function () {
self.show(!self.show());
};
};
HTML:
<!-- ko foreach: projects -->
<div id="eachOppyProject" style="border-bottom: 1px solid #eee;">
<table>
<tbody>
<tr>
<td><a data-bind="attr: { href: '/tools/oppy/' + guid }" style="font-size: 25px;"><span class="link" data-bind=" value: guid, text: name"></span></a></td>
</tr>
<tr data-bind="text: projectDescription"></tr>
<%-- <tr data-bind="text: guid"></tr>--%>
</tbody>
</table>
<span class="forminputtitle">Have you done project this before?</span> <input type="button" value="Yes" data-bind="click: $parent.toggleTextArea" class="btnOppy"/>
<textarea placeholder="Tell us a little of what you've done." data-bind="visible: $parent.show, attr: {'id': guid }" class="form-control newSessionAnalyst" style="height:75px; " /><br />
<span> <input type="checkbox" name="oppyDoProjectAgain" style="padding-top:10px; padding-right:20px;">I'm thinking about doing this again. </span>
<br />
</div><br />
<!-- /ko -->
Spencer:
function ProjectViewModel(proj) {
//console.log(proj);
var self = this;
self.projects = ko.observableArray(proj);
self.projects().forEach(function() { //also tried proj.forEach(function())
self.projects().showComments = ko.observable(false);
self.projects().toggleComments = function () {
self.showComments(!self.showComments());
};
})
};
It's weird that
data-bind="visible: show"
doesn't provide any binding error because context of binding inside ko foreach: project is project not the ProjectViewModel.
Anyway, this solution should solve your problem:
function ViewModel() {
var self = this;
var wrappedProjects = proj.map(function(p) {
return new Project(p);
});
self.projects = ko.observableArray(wrappedProjects);
}
function Project(proj) {
var self = proj;
self.show = ko.observable(false);
self.toggleTextArea = function () {
self.show(!self.show());
}
return self;
}
The problem is that the show observable needs to be defined in the projects array. Currently all the textareas are looking at the same observable. This means you'll have to move the function showTextArea into the projects array as well.
Also you may want to consider renaming your function or getting rid of it entirely. Function names which imply they drive a change directly to the view fly in the face of the MVVM pattern. I'd recommend a name like "toggleComments" as it doesn't reference a view control.
EDIT:
As an example:
function ProjectViewModel(proj) {
//console.log(proj);
var self = this;
self.projects = ko.observableArray(proj);
foreach(var project in self.projects()) {
project.showComments = ko.observable(false);
project.toggleComments = function () {
self.showComments(!self.showComments());
};
}
};
There is probably a much cleaner way to implement this in your project I just wanted to demonstrate my meaning without making a ton of changes to the code you provided.

Knockoutjs does not update View

I use knockoutjs to update my Html view when an Javascript (Signalr) function is fired. But the view does not update when producthub.client.showOnlineUser is called. When i apply the binding everytime the table has same content multiple times. How can i just update the view in knockoutjs?
Html:
<table id="usersTable">
<thead>
<tr>
<th>Name</th>
<th>Mail</th>
</tr>
</thead>
<tbody data-bind="foreach: seats">
<tr>
<td data-bind="text: NameFirst"></td>
<td data-bind="text: Mail"></td>
</tr>
</tbody>
</table>
Javascript:
$(document).ready(function () {
var applied = false;
var model;
producthub.client.showOnlineUser = function (userOnlineOnUrl, msg1) {
function ReservationsViewModel() {
var self = this;
self.seats = ko.observableArray(userOnlineOnUrl);
}
model = new ReservationsViewModel();
if (!applied) {
ko.applyBindings(model);
applied = true;
}
};
});
userOnlineOnUrl is an JSON-Array which changes it's data on each function call. The view (table) is not updated with it's data.
try not to call model every time just update it with function
$(document).ready(function () {
var applied = false;
var model;
function ReservationsViewModel() {
var self = this;
self.seats = ko.observableArray();
}
model = new ReservationsViewModel();
ko.applyBindings(model);
applied = true;
producthub.client.showOnlineUser = function (userOnlineOnUrl, msg1) {
model = new ReservationsViewModel();
//remove the previous data in array
model.seats.removeAll();
//Add new data to array
model.seats(userOnlineOnUrl);
$('#onlineUsers').append(msg1);
};
});

Knockout.js ObservableArray not binding

I have this code as global for the whole page:
<script type="text/javascript">
var data = [];
var VM_FiltroSeguros =
{
seguros: ko.observableArray(data)
};
ko.applyBindings(VM_FiltroSeguros.seguros);
</script>
Then when a succesfull ajax call is made, executed this:
function okFiltrarSeguros(data)
{
var parsedData = parse(data);
if (parsedData.Ok)
{
toastr.success('Se encontraron ' + parsedData.Value.length.toString() + ' Seguros.');
$('#liResultsFiltroSeguro').show();
VM_FiltroSeguros.seguros = parsedData.Value;
};
The Html is these:
<table class="table table-hover">
<thead>
<tr>
<th>Ramo</th>
<th>Poliza</th>
</tr>
</thead>
<tbody data-bind="foreach: seguros">
<tr>
<td><span data-bind="text: NroRamo"></span></td>
<td><span data-bind="text: NroSeguro"></span></td>
</tr>
</tbody>
</table>
After VM_FiltroSeguros.seguros = parsedData.Value; is executed I can see in the debugger that the viewModel is filled whith objects, but the is never updated.
What could be wrong? Thanks!!!!
There's a couple of things you're doing wrong here. First, you need to bind the entire ViewModel:
var data = [];
var VM_FiltroSeguros =
{
seguros: ko.observableArray(data)
};
ko.applyBindings(VM_FiltroSeguros);
Then you need to add data to the 'seguros' property with a function call like this:
VM_FiltroSeguros.seguros(parsedData.Value);

Categories

Resources